diff --git a/client/pom.xml b/client/pom.xml
index b0c3bb7..e146b39 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -164,6 +164,10 @@
org.lwjgl
lwjgl-glfw
+
+ org.lwjgl
+ lwjgl-nanovg
+
org.lwjgl
lwjgl-meshoptimizer
@@ -195,6 +199,11 @@
lwjgl-glfw
${lwjgl.natives}
+
+ org.lwjgl
+ lwjgl-nanovg
+ ${lwjgl.natives}
+
org.lwjgl
lwjgl-meshoptimizer
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java
index 2535468..987113b 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/GameRenderer.java
@@ -5,7 +5,7 @@ import nl.andrewl.aos2_client.Client;
import nl.andrewl.aos2_client.config.ClientConfig;
import nl.andrewl.aos2_client.model.ClientPlayer;
import nl.andrewl.aos2_client.render.chunk.ChunkRenderer;
-import nl.andrewl.aos2_client.render.gui.GUIRenderer;
+import nl.andrewl.aos2_client.render.gui.GuiRenderer;
import nl.andrewl.aos2_client.render.gui.GUITexture;
import nl.andrewl.aos2_client.render.model.Model;
import nl.andrewl.aos_core.model.Team;
@@ -39,7 +39,7 @@ public class GameRenderer {
private final ClientConfig.DisplayConfig config;
private ChunkRenderer chunkRenderer;
- private GUIRenderer guiRenderer;
+ private GuiRenderer guiRenderer;
private ModelRenderer modelRenderer;
private final Camera camera;
private final Client client;
@@ -129,7 +129,11 @@ public class GameRenderer {
this.chunkRenderer = new ChunkRenderer();
log.debug("Initialized chunk renderer.");
- this.guiRenderer = new GUIRenderer();
+ try {
+ this.guiRenderer = new GuiRenderer();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
crosshairTexture = new GUITexture("gui/crosshair.png");
clipTexture = new GUITexture("gui/clip.png");
bulletTexture = new GUITexture("gui/bullet.png");
@@ -192,7 +196,7 @@ public class GameRenderer {
}
public void draw() {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
chunkRenderer.draw(camera, client.getWorld().getChunkMeshesToDraw());
ClientPlayer myPlayer = client.getMyPlayer();
@@ -324,6 +328,7 @@ public class GameRenderer {
-0.90f,
-0.90f
);
+ guiRenderer.drawNvg(screenWidth, screenHeight);
guiRenderer.end();
glfwSwapBuffers(windowHandle);
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUIRenderer.java b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java
similarity index 66%
rename from client/src/main/java/nl/andrewl/aos2_client/render/gui/GUIRenderer.java
rename to client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java
index 6dba8df..4c2cd13 100644
--- a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GUIRenderer.java
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiRenderer.java
@@ -1,19 +1,40 @@
package nl.andrewl.aos2_client.render.gui;
import nl.andrewl.aos2_client.render.ShaderProgram;
+import nl.andrewl.aos2_client.util.ResourceUtils;
+import nl.andrewl.aos_core.FileUtils;
import org.joml.Matrix4f;
import org.lwjgl.BufferUtils;
+import org.lwjgl.nanovg.NVGColor;
+import org.lwjgl.nanovg.NVGPaint;
+import java.io.IOException;
+import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Map;
+import static org.lwjgl.nanovg.NanoVG.*;
+import static org.lwjgl.nanovg.NanoVGGL3.*;
import static org.lwjgl.opengl.GL46.*;
+import static org.lwjgl.system.MemoryUtil.memFree;
/**
* Manages rendering of 2D GUI components like cross-hairs, inventory stuff, etc.
*/
-public class GUIRenderer {
+public class GuiRenderer {
+ private final long vgId;
+ private final int jetbrainsMonoFont;
+ private final ByteBuffer jetbrainsMonoFontData;
+
+ private static final NVGColor colorA = NVGColor.create();
+ private static final NVGColor colorB = NVGColor.create();
+ private static final NVGColor colorC = NVGColor.create();
+
+ private static final NVGPaint paintA = NVGPaint.create();
+ private static final NVGPaint paintB = NVGPaint.create();
+ private static final NVGPaint paintC = NVGPaint.create();
+
private final int vaoId;
private final int vboId;
private final int vertexCount;
@@ -25,7 +46,16 @@ public class GUIRenderer {
private final Map textures = new HashMap<>();
- public GUIRenderer() {
+ public GuiRenderer() throws IOException {
+ vgId = nvgCreate(NVG_ANTIALIAS);
+ jetbrainsMonoFontData = FileUtils.readClasspathResourceAsDirectByteBuffer("text/JetBrainsMono-Regular.ttf");
+ jetbrainsMonoFont = nvgCreateFontMem(
+ vgId,
+ "jetbrains-mono",
+ jetbrainsMonoFontData,
+ 0
+ );
+
vaoId = glGenVertexArrays();
vboId = glGenBuffers();
FloatBuffer buffer = BufferUtils.createFloatBuffer(8);
@@ -87,6 +117,17 @@ public class GUIRenderer {
glDrawArrays(GL_TRIANGLE_STRIP, 0, vertexCount);
}
+ public void drawNvg(float width, float height) {
+ nvgBeginFrame(vgId, width, height, width / height);
+ nvgSave(vgId);
+ nvgFontSize(vgId, 60f);
+ nvgFontFaceId(vgId, jetbrainsMonoFont);
+ nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP);
+ nvgText(vgId, 50, 50, "Hello world!");
+ nvgRestore(vgId);
+ nvgEndFrame(vgId);
+ }
+
public void end() {
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
@@ -96,9 +137,13 @@ public class GUIRenderer {
}
public void free() {
+ memFree(jetbrainsMonoFontData);
+ nvgDelete(vgId);
for (var tex : textures.values()) tex.free();
glDeleteBuffers(vboId);
glDeleteVertexArrays(vaoId);
shaderProgram.free();
}
+
+
}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiUtils.java b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiUtils.java
new file mode 100644
index 0000000..c22b480
--- /dev/null
+++ b/client/src/main/java/nl/andrewl/aos2_client/render/gui/GuiUtils.java
@@ -0,0 +1,17 @@
+package nl.andrewl.aos2_client.render.gui;
+
+import org.lwjgl.nanovg.NVGColor;
+
+public class GuiUtils {
+ public static NVGColor rgba(float r, float g, float b, float a, NVGColor color) {
+ color.r(r);
+ color.g(g);
+ color.b(b);
+ color.a(a);
+ return color;
+ }
+
+ public static boolean isBlack(NVGColor color) {
+ return color.r() == 0 && color.g() == 0 && color.b() == 0 && color.a() == 0;
+ }
+}
diff --git a/client/src/main/java/nl/andrewl/aos2_client/util/ResourceUtils.java b/client/src/main/java/nl/andrewl/aos2_client/util/ResourceUtils.java
new file mode 100644
index 0000000..01b90e1
--- /dev/null
+++ b/client/src/main/java/nl/andrewl/aos2_client/util/ResourceUtils.java
@@ -0,0 +1,59 @@
+package nl.andrewl.aos2_client.util;
+
+import org.lwjgl.BufferUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.lwjgl.BufferUtils.createByteBuffer;
+import static org.lwjgl.system.MemoryUtil.memSlice;
+
+public class ResourceUtils {
+ public static ByteBuffer ioResourceToByteBuffer(String resource, int bufferSize) throws IOException {
+ ByteBuffer buffer;
+
+ Path path = Paths.get(resource);
+ if (Files.isReadable(path)) {
+ try (SeekableByteChannel fc = Files.newByteChannel(path)) {
+ buffer = createByteBuffer((int)fc.size() + 1);
+ while (fc.read(buffer) != -1) {
+ ;
+ }
+ }
+ } else {
+ try (
+ InputStream source = ResourceUtils.class.getClassLoader().getResourceAsStream(resource);
+ ReadableByteChannel rbc = Channels.newChannel(source)
+ ) {
+ buffer = createByteBuffer(bufferSize);
+
+ while (true) {
+ int bytes = rbc.read(buffer);
+ if (bytes == -1) {
+ break;
+ }
+ if (buffer.remaining() == 0) {
+ buffer = resizeBuffer(buffer, buffer.capacity() * 3 / 2); // 50%
+ }
+ }
+ }
+ }
+
+ buffer.flip();
+ return memSlice(buffer);
+ }
+
+ private static ByteBuffer resizeBuffer(ByteBuffer buffer, int newCapacity) {
+ ByteBuffer newBuffer = BufferUtils.createByteBuffer(newCapacity);
+ buffer.flip();
+ newBuffer.put(buffer);
+ return newBuffer;
+ }
+}
diff --git a/client/src/main/resources/text/JetBrainsMono-Regular.ttf b/client/src/main/resources/text/JetBrainsMono-Regular.ttf
new file mode 100644
index 0000000..8da8aa4
Binary files /dev/null and b/client/src/main/resources/text/JetBrainsMono-Regular.ttf differ
diff --git a/core/src/main/java/nl/andrewl/aos_core/FileUtils.java b/core/src/main/java/nl/andrewl/aos_core/FileUtils.java
index 3dac464..0530b34 100644
--- a/core/src/main/java/nl/andrewl/aos_core/FileUtils.java
+++ b/core/src/main/java/nl/andrewl/aos_core/FileUtils.java
@@ -2,14 +2,40 @@ package nl.andrewl.aos_core;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
public final class FileUtils {
private FileUtils() {}
+ /**
+ * Reads a classpath resource as a string.
+ * @param resource The resource to read.
+ * @return The string contents of the resource.
+ * @throws IOException If the resource can't be found or read.
+ */
public static String readClasspathFile(String resource) throws IOException {
try (InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(resource)) {
if (in == null) throw new IOException("Could not load classpath resource: " + resource);
return new String(in.readAllBytes());
}
}
+
+ /**
+ * Reads a classpath resource into a directly-allocated byte buffer that
+ * must be deallocated manually.
+ * @param resource The resource to read.
+ * @return The byte buffer containing the resource.
+ * @throws IOException If the resource can't be found or read.
+ */
+ public static ByteBuffer readClasspathResourceAsDirectByteBuffer(String resource) throws IOException {
+ try (InputStream in = FileUtils.class.getClassLoader().getResourceAsStream(resource)) {
+ if (in == null) throw new IOException("Could not load classpath resource: " + resource);
+ byte[] bytes = in.readAllBytes();
+ ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length).order(ByteOrder.nativeOrder());
+ buffer.put(bytes);
+ buffer.flip();
+ return buffer;
+ }
+ }
}
diff --git a/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java b/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java
index 393b4e3..656e4b3 100644
--- a/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java
+++ b/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java
@@ -20,10 +20,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.net.Socket;
+import java.net.*;
import java.util.LinkedList;
/**
@@ -118,6 +115,8 @@ public class ClientCommunicationHandler {
.withShutdownHook(() -> server.getPlayerManager().deregister(this.player));
new Thread(tcpReceiver).start();
}
+ } catch (SocketTimeoutException e) {
+ // Ignore this one, since this will happen if the client doesn't send data properly.
} catch (IOException e) {
log.warn("An IOException occurred while attempting to establish a connection to a client.", e);
}