From fbd893915b6aedcf3b1cff7a02b8437a0ba98f3e Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Thu, 28 Jul 2022 01:00:14 +0200 Subject: [PATCH] Added smoother client control! --- .../java/nl/andrewl/aos2_client/Client.java | 114 ++++++++------ .../aos2_client/control/InputHandler.java | 139 +++++++++++++++--- .../control/PlayerInputKeyCallback.java | 34 ++++- .../PlayerInputMouseClickCallback.java | 14 +- .../aos2_client/render/GameRenderer.java | 13 +- .../aos2_client/render/gui/GuiRenderer.java | 39 ++++- .../aos2_client/sound/SoundManager.java | 9 +- 7 files changed, 284 insertions(+), 78 deletions(-) diff --git a/client/src/main/java/nl/andrewl/aos2_client/Client.java b/client/src/main/java/nl/andrewl/aos2_client/Client.java index 7e5eeeb..7b85a67 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/Client.java +++ b/client/src/main/java/nl/andrewl/aos2_client/Client.java @@ -20,15 +20,17 @@ import org.joml.Vector3f; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.awt.image.RenderedImage; import java.io.IOException; import java.nio.file.Path; import java.util.List; import java.util.Map; +import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; public class Client implements Runnable { private static final Logger log = LoggerFactory.getLogger(Client.class); - public static final double FPS = 60; private final ClientConfig config; private final CommunicationHandler communicationHandler; @@ -43,6 +45,7 @@ public class Client implements Runnable { private final Map projectiles; private final Map teams; private final Chat chat; + private final Queue mainThreadActions; public Client(ClientConfig config) { this.config = config; @@ -52,6 +55,7 @@ public class Client implements Runnable { this.communicationHandler = new CommunicationHandler(this); this.inputHandler = new InputHandler(this, communicationHandler); this.chat = new Chat(); + this.mainThreadActions = new ConcurrentLinkedQueue<>(); } public ClientConfig getConfig() { @@ -80,8 +84,9 @@ public class Client implements Runnable { return; } - gameRenderer = new GameRenderer(config.display, this); + gameRenderer = new GameRenderer(this); gameRenderer.setupWindow( + inputHandler, new PlayerViewCursorCallback(config.input, this, gameRenderer.getCamera(), communicationHandler), new PlayerInputKeyCallback(inputHandler), new PlayerInputMouseClickCallback(inputHandler), @@ -94,12 +99,17 @@ public class Client implements Runnable { while (!gameRenderer.windowShouldClose() && !communicationHandler.isDone()) { long now = System.currentTimeMillis(); float dt = (now - lastFrameAt) / 1000f; + world.processQueuedChunkUpdates(); + while (!mainThreadActions.isEmpty()) { + mainThreadActions.remove().run(); + } soundManager.updateListener(myPlayer.getPosition(), myPlayer.getVelocity()); gameRenderer.getCamera().interpolatePosition(dt); interpolatePlayers(now, dt); interpolateProjectiles(dt); soundManager.playWalkingSounds(myPlayer, now); + gameRenderer.draw(); lastFrameAt = now; } @@ -118,25 +128,27 @@ public class Client implements Runnable { communicationHandler.sendMessage(new ChunkHashMessage(u.cx(), u.cy(), u.cz(), -1)); } } else if (msg instanceof PlayerUpdateMessage playerUpdate) { - if (playerUpdate.clientId() == myPlayer.getId() && playerUpdate.timestamp() > lastPlayerUpdate) { - myPlayer.getPosition().set(playerUpdate.px(), playerUpdate.py(), playerUpdate.pz()); - myPlayer.getVelocity().set(playerUpdate.vx(), playerUpdate.vy(), playerUpdate.vz()); - myPlayer.setCrouching(playerUpdate.crouching()); - if (gameRenderer != null) { - gameRenderer.getCamera().setToPlayer(myPlayer); + runLater(() -> { + if (playerUpdate.clientId() == myPlayer.getId() && playerUpdate.timestamp() > lastPlayerUpdate) { + myPlayer.getPosition().set(playerUpdate.px(), playerUpdate.py(), playerUpdate.pz()); + myPlayer.getVelocity().set(playerUpdate.vx(), playerUpdate.vy(), playerUpdate.vz()); + myPlayer.setCrouching(playerUpdate.crouching()); + if (gameRenderer != null) { + gameRenderer.getCamera().setToPlayer(myPlayer); + } + if (soundManager != null) { + soundManager.updateListener(myPlayer.getPosition(), myPlayer.getVelocity()); + } + lastPlayerUpdate = playerUpdate.timestamp(); + } else { + OtherPlayer p = players.get(playerUpdate.clientId()); + if (p != null) { + playerUpdate.apply(p); + p.setHeldItemId(playerUpdate.selectedItemId()); + p.updateModelTransform(); + } } - if (soundManager != null) { - soundManager.updateListener(myPlayer.getPosition(), myPlayer.getVelocity()); - } - lastPlayerUpdate = playerUpdate.timestamp(); - } else { - OtherPlayer p = players.get(playerUpdate.clientId()); - if (p != null) { - playerUpdate.apply(p); - p.setHeldItemId(playerUpdate.selectedItemId()); - p.updateModelTransform(); - } - } + }); } else if (msg instanceof ClientInventoryMessage inventoryMessage) { myPlayer.setInventory(inventoryMessage.inv()); } else if (msg instanceof InventorySelectedStackMessage selectedStackMessage) { @@ -149,19 +161,23 @@ public class Client implements Runnable { player.setSelectedBlockValue(blockColorMessage.block()); } } else if (msg instanceof PlayerJoinMessage joinMessage) { - Player p = joinMessage.toPlayer(); - OtherPlayer op = new OtherPlayer(p.getId(), p.getUsername()); - if (joinMessage.teamId() != -1) { - op.setTeam(teams.get(joinMessage.teamId())); - } - op.getPosition().set(p.getPosition()); - op.getVelocity().set(p.getVelocity()); - op.getOrientation().set(p.getOrientation()); - op.setHeldItemId(joinMessage.selectedItemId()); - op.setSelectedBlockValue(joinMessage.selectedBlockValue()); - players.put(op.getId(), op); + runLater(() -> { + Player p = joinMessage.toPlayer(); + OtherPlayer op = new OtherPlayer(p.getId(), p.getUsername()); + if (joinMessage.teamId() != -1) { + op.setTeam(teams.get(joinMessage.teamId())); + } + op.getPosition().set(p.getPosition()); + op.getVelocity().set(p.getVelocity()); + op.getOrientation().set(p.getOrientation()); + op.setHeldItemId(joinMessage.selectedItemId()); + op.setSelectedBlockValue(joinMessage.selectedBlockValue()); + players.put(op.getId(), op); + }); } else if (msg instanceof PlayerLeaveMessage leaveMessage) { - players.remove(leaveMessage.id()); + runLater(() -> { + players.remove(leaveMessage.id()); + }); } else if (msg instanceof SoundMessage soundMessage) { if (soundManager != null) { soundManager.play( @@ -172,17 +188,19 @@ public class Client implements Runnable { ); } } else if (msg instanceof ProjectileMessage pm) { - Projectile p = projectiles.get(pm.id()); - if (p == null && !pm.destroyed()) { - p = new Projectile(pm.id(), new Vector3f(pm.px(), pm.py(), pm.pz()), new Vector3f(pm.vx(), pm.vy(), pm.vz()), pm.type()); - projectiles.put(p.getId(), p); - } else if (p != null) { - p.getPosition().set(pm.px(), pm.py(), pm.pz()); // Don't update position, it's too short of a timeframe to matter. - p.getVelocity().set(pm.vx(), pm.vy(), pm.vz()); - if (pm.destroyed()) { - projectiles.remove(p.getId()); + runLater(() -> { + Projectile p = projectiles.get(pm.id()); + if (p == null && !pm.destroyed()) { + p = new Projectile(pm.id(), new Vector3f(pm.px(), pm.py(), pm.pz()), new Vector3f(pm.vx(), pm.vy(), pm.vz()), pm.type()); + projectiles.put(p.getId(), p); + } else if (p != null) { + p.getPosition().set(pm.px(), pm.py(), pm.pz()); // Don't update position, it's too short of a timeframe to matter. + p.getVelocity().set(pm.vx(), pm.vy(), pm.vz()); + if (pm.destroyed()) { + projectiles.remove(p.getId()); + } } - } + }); } else if (msg instanceof ClientHealthMessage healthMessage) { myPlayer.setHealth(healthMessage.health()); } else if (msg instanceof ChatMessage chatMessage) { @@ -201,6 +219,10 @@ public class Client implements Runnable { return world; } + public InputHandler getInputHandler() { + return inputHandler; + } + public Map getTeams() { return teams; } @@ -217,6 +239,10 @@ public class Client implements Runnable { return chat; } + public SoundManager getSoundManager() { + return soundManager; + } + public void interpolatePlayers(long now, float dt) { Vector3f movement = new Vector3f(); for (var player : players.values()) { @@ -236,6 +262,10 @@ public class Client implements Runnable { } } + public void runLater(Runnable runnable) { + mainThreadActions.add(runnable); + } + public static void main(String[] args) throws IOException { List configPaths = Config.getCommonConfigPaths(); if (args.length > 0) { diff --git a/client/src/main/java/nl/andrewl/aos2_client/control/InputHandler.java b/client/src/main/java/nl/andrewl/aos2_client/control/InputHandler.java index ec230c3..db7d566 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/control/InputHandler.java +++ b/client/src/main/java/nl/andrewl/aos2_client/control/InputHandler.java @@ -17,6 +17,8 @@ public class InputHandler { private final Client client; private final CommunicationHandler comm; + private long windowId; + private ClientInputState lastInputState = null; private boolean forward; @@ -29,6 +31,9 @@ public class InputHandler { private boolean hitting; private boolean interacting; private boolean reloading; + private int selectedInventoryIndex; + + private boolean debugEnabled; public InputHandler(Client client, CommunicationHandler comm) { @@ -36,27 +41,16 @@ public class InputHandler { this.comm = comm; } - public void updateInputState(long window) { - // TODO: Allow customized keybindings. - int selectedInventoryIndex; - selectedInventoryIndex = client.getMyPlayer().getInventory().getSelectedIndex(); - if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS) selectedInventoryIndex = 0; - if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS) selectedInventoryIndex = 1; - if (glfwGetKey(window, GLFW_KEY_3) == GLFW_PRESS) selectedInventoryIndex = 2; - if (glfwGetKey(window, GLFW_KEY_4) == GLFW_PRESS) selectedInventoryIndex = 3; + public void setWindowId(long windowId) { + this.windowId = windowId; + } + public void updateInputState() { ClientInputState currentInputState = new ClientInputState( comm.getClientId(), - glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS, - glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS, - glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS, - glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS, - glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS, - glfwGetKey(window, GLFW_KEY_LEFT_CONTROL) == GLFW_PRESS, - glfwGetKey(window, GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS, - glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_1) == GLFW_PRESS, - glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS, - glfwGetKey(window, GLFW_KEY_R) == GLFW_PRESS, + forward, backward, left, right, + jumping, crouching, sprinting, + hitting, interacting, reloading, selectedInventoryIndex ); if (!currentInputState.equals(lastInputState)) { @@ -67,7 +61,7 @@ public class InputHandler { ClientPlayer player = client.getMyPlayer(); // Check for "pick block" functionality. - if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS && player.getInventory().getSelectedItemStack() instanceof BlockItemStack stack) { + if (glfwGetMouseButton(windowId, GLFW_MOUSE_BUTTON_3) == GLFW_PRESS && player.getInventory().getSelectedItemStack() instanceof BlockItemStack stack) { Hit hit = client.getWorld().getLookingAtPos(player.getEyePosition(), player.getViewVector(), 50); if (hit != null) { byte selectedBlock = client.getWorld().getBlockAt(hit.pos().x, hit.pos().y, hit.pos().z); @@ -78,4 +72,111 @@ public class InputHandler { } } } + + public boolean isForward() { + return forward; + } + + public void setForward(boolean forward) { + this.forward = forward; + updateInputState(); + } + + public boolean isBackward() { + return backward; + } + + public void setBackward(boolean backward) { + this.backward = backward; + updateInputState(); + } + + public boolean isLeft() { + return left; + } + + public void setLeft(boolean left) { + this.left = left; + updateInputState(); + } + + public boolean isRight() { + return right; + } + + public void setRight(boolean right) { + this.right = right; + updateInputState(); + } + + public boolean isJumping() { + return jumping; + } + + public void setJumping(boolean jumping) { + this.jumping = jumping; + updateInputState(); + } + + public boolean isCrouching() { + return crouching; + } + + public void setCrouching(boolean crouching) { + this.crouching = crouching; + updateInputState(); + } + + public boolean isSprinting() { + return sprinting; + } + + public void setSprinting(boolean sprinting) { + this.sprinting = sprinting; + updateInputState(); + } + + public boolean isHitting() { + return hitting; + } + + public void setHitting(boolean hitting) { + this.hitting = hitting; + updateInputState(); + } + + public boolean isInteracting() { + return interacting; + } + + public void setInteracting(boolean interacting) { + this.interacting = interacting; + updateInputState(); + } + + public boolean isReloading() { + return reloading; + } + + public void setReloading(boolean reloading) { + this.reloading = reloading; + updateInputState(); + } + + public int getSelectedInventoryIndex() { + return selectedInventoryIndex; + } + + public void setSelectedInventoryIndex(int selectedInventoryIndex) { + this.selectedInventoryIndex = selectedInventoryIndex; + updateInputState(); + } + + public boolean isDebugEnabled() { + return debugEnabled; + } + + public void toggleDebugEnabled() { + this.debugEnabled = !debugEnabled; + } } diff --git a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputKeyCallback.java b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputKeyCallback.java index c088a67..ed4ddda 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputKeyCallback.java +++ b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputKeyCallback.java @@ -13,9 +13,37 @@ public class PlayerInputKeyCallback implements GLFWKeyCallbackI { @Override public void invoke(long window, int key, int scancode, int action, int mods) { - if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) { - glfwSetWindowShouldClose(window, true); + if (action == GLFW_PRESS) { + switch (key) { + case GLFW_KEY_W -> inputHandler.setForward(true); + case GLFW_KEY_A -> inputHandler.setLeft(true); + case GLFW_KEY_S -> inputHandler.setBackward(true); + case GLFW_KEY_D -> inputHandler.setRight(true); + case GLFW_KEY_SPACE -> inputHandler.setJumping(true); + case GLFW_KEY_LEFT_CONTROL -> inputHandler.setCrouching(true); + case GLFW_KEY_LEFT_SHIFT -> inputHandler.setSprinting(true); + case GLFW_KEY_R -> inputHandler.setReloading(true); + + case GLFW_KEY_1 -> inputHandler.setSelectedInventoryIndex(0); + case GLFW_KEY_2 -> inputHandler.setSelectedInventoryIndex(1); + case GLFW_KEY_3 -> inputHandler.setSelectedInventoryIndex(2); + case GLFW_KEY_4 -> inputHandler.setSelectedInventoryIndex(3); + + case GLFW_KEY_F3 -> inputHandler.toggleDebugEnabled(); + } + } else if (action == GLFW_RELEASE) { + switch (key) { + case GLFW_KEY_W -> inputHandler.setForward(false); + case GLFW_KEY_A -> inputHandler.setLeft(false); + case GLFW_KEY_S -> inputHandler.setBackward(false); + case GLFW_KEY_D -> inputHandler.setRight(false); + case GLFW_KEY_SPACE -> inputHandler.setJumping(false); + case GLFW_KEY_LEFT_CONTROL -> inputHandler.setCrouching(false); + case GLFW_KEY_LEFT_SHIFT -> inputHandler.setSprinting(false); + case GLFW_KEY_R -> inputHandler.setReloading(false); + + case GLFW_KEY_ESCAPE -> glfwSetWindowShouldClose(window, true); + } } - inputHandler.updateInputState(window); } } diff --git a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseClickCallback.java b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseClickCallback.java index 7f8a4f6..9c4f872 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseClickCallback.java +++ b/client/src/main/java/nl/andrewl/aos2_client/control/PlayerInputMouseClickCallback.java @@ -2,6 +2,8 @@ package nl.andrewl.aos2_client.control; import org.lwjgl.glfw.GLFWMouseButtonCallbackI; +import static org.lwjgl.glfw.GLFW.*; + /** * Callback that's called when the player clicks with their mouse. */ @@ -14,6 +16,16 @@ public class PlayerInputMouseClickCallback implements GLFWMouseButtonCallbackI { @Override public void invoke(long window, int button, int action, int mods) { - inputHandler.updateInputState(window); + if (action == GLFW_PRESS) { + switch (button) { + case GLFW_MOUSE_BUTTON_1 -> inputHandler.setHitting(true); + case GLFW_MOUSE_BUTTON_2 -> inputHandler.setInteracting(true); + } + } else if (action == GLFW_RELEASE) { + switch (button) { + case GLFW_MOUSE_BUTTON_1 -> inputHandler.setHitting(false); + case GLFW_MOUSE_BUTTON_2 -> inputHandler.setInteracting(false); + } + } } } 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 40e26ab..6fe81c3 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 @@ -3,6 +3,7 @@ package nl.andrewl.aos2_client.render; import nl.andrewl.aos2_client.Camera; import nl.andrewl.aos2_client.Client; import nl.andrewl.aos2_client.config.ClientConfig; +import nl.andrewl.aos2_client.control.InputHandler; import nl.andrewl.aos2_client.model.ClientPlayer; import nl.andrewl.aos2_client.render.chunk.ChunkRenderer; import nl.andrewl.aos2_client.render.gui.GuiRenderer; @@ -56,8 +57,8 @@ public class GameRenderer { private final Matrix4f perspectiveTransform; - public GameRenderer(ClientConfig.DisplayConfig config, Client client) { - this.config = config; + public GameRenderer(Client client) { + this.config = client.getConfig().display; this.client = client; this.camera = new Camera(); camera.setToPlayer(client.getMyPlayer()); @@ -65,6 +66,7 @@ public class GameRenderer { } public void setupWindow( + InputHandler inputHandler, GLFWCursorPosCallbackI viewCursorCallback, GLFWKeyCallbackI inputKeyCallback, GLFWMouseButtonCallbackI mouseButtonCallback, @@ -90,6 +92,7 @@ public class GameRenderer { windowHandle = glfwCreateWindow(screenWidth, screenHeight, "Ace of Shades 2", 0, 0); } if (windowHandle == 0) throw new RuntimeException("Failed to create GLFW window."); + inputHandler.setWindowId(windowHandle); log.debug("Initialized GLFW window."); // Setup callbacks. @@ -110,7 +113,7 @@ public class GameRenderer { GL.createCapabilities(); // GLUtil.setupDebugMessageCallback(System.out); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearColor(0.1f, 0.1f, 0.1f, 0.0f); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glCullFace(GL_BACK); @@ -259,7 +262,7 @@ public class GameRenderer { flagModel.bind(); for (Team team : client.getTeams().values()) { modelTransform.identity() - .translate(team.getSpawnPoint()); + .translate(team.getSpawnPoint().x() - 0.25f, team.getSpawnPoint().y(), team.getSpawnPoint().z() - 0.25f); modelTransform.normal(normalTransform); modelRenderer.setAspectColor(team.getColor()); modelRenderer.render(flagModel, modelTransform, normalTransform); @@ -271,7 +274,7 @@ public class GameRenderer { // GUI rendering guiRenderer.start(); guiRenderer.drawNameplates(myPlayer, camera.getViewTransformData(), perspectiveTransform.get(new float[16])); - guiRenderer.drawNvg(screenWidth, screenHeight, myPlayer, client.getChat()); + guiRenderer.drawNvg(screenWidth, screenHeight, client); 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 index 63ca69e..c3bc9a0 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,10 +1,12 @@ package nl.andrewl.aos2_client.render.gui; import nl.andrewl.aos2_client.Camera; +import nl.andrewl.aos2_client.Client; import nl.andrewl.aos2_client.model.Chat; import nl.andrewl.aos2_client.model.ClientPlayer; import nl.andrewl.aos2_client.model.OtherPlayer; import nl.andrewl.aos2_client.render.ShaderProgram; +import nl.andrewl.aos2_client.sound.SoundSource; import nl.andrewl.aos_core.FileUtils; import nl.andrewl.aos_core.model.Player; import nl.andrewl.aos_core.model.item.BlockItem; @@ -216,14 +218,17 @@ public class GuiRenderer { shaderProgram.use(); } - public void drawNvg(float width, float height, ClientPlayer player, Chat chat) { + public void drawNvg(float width, float height, Client client) { nvgBeginFrame(vgId, width, height, width / height); nvgSave(vgId); drawCrosshair(width, height); - drawChat(width, height, chat); - drawHealthBar(width, height, player); - drawHeldItemStackInfo(width, height, player); + drawChat(width, height, client.getChat()); + drawHealthBar(width, height, client.getMyPlayer()); + drawHeldItemStackInfo(width, height, client.getMyPlayer()); + if (client.getInputHandler().isDebugEnabled()) { + drawDebugInfo(width, height, client); + } nvgRestore(vgId); nvgEndFrame(vgId); @@ -346,4 +351,30 @@ public class GuiRenderer { y -= 16; } } + + private void drawDebugInfo(float w, float h, Client client) { + float y = h / 4 + 10; + nvgFontSize(vgId, 12f); + nvgFontFaceId(vgId, jetbrainsMonoFont); + nvgTextAlign(vgId, NVG_ALIGN_LEFT | NVG_ALIGN_TOP); + nvgFillColor(vgId, GuiUtils.rgba(1, 1, 1, 1, colorA)); + var pos = client.getMyPlayer().getPosition(); + nvgText(vgId, 5, y, String.format("Pos: x=%.3f, y=%.3f, z=%.3f", pos.x, pos.y, pos.z)); + y += 12; + var vel = client.getMyPlayer().getVelocity(); + nvgText(vgId, 5, y, String.format("Vel: x=%.3f, y=%.3f, z=%.3f, speed=%.3f", vel.x, vel.y, vel.z, vel.length())); + y += 12; + var view = client.getMyPlayer().getOrientation(); + nvgText(vgId, 5, y, String.format("View: horizontal=%.3f, vertical=%.3f", Math.toDegrees(view.x), Math.toDegrees(view.y))); + y += 12; + var soundSources = client.getSoundManager().getSources(); + int activeCount = (int) soundSources.stream().filter(SoundSource::isPlaying).count(); + nvgText(vgId, 5, y, String.format("Sounds: %d / %d playing", activeCount, soundSources.size())); + y += 12; + nvgText(vgId, 5, y, String.format("Projectiles: %d", client.getProjectiles().size())); + y += 12; + nvgText(vgId, 5, y, String.format("Players: %d", client.getPlayers().size())); + y += 12; + nvgText(vgId, 5, y, String.format("Chunks: %d", client.getWorld().getChunkMap().size())); + } } diff --git a/client/src/main/java/nl/andrewl/aos2_client/sound/SoundManager.java b/client/src/main/java/nl/andrewl/aos2_client/sound/SoundManager.java index 60a19e3..e6599b5 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/sound/SoundManager.java +++ b/client/src/main/java/nl/andrewl/aos2_client/sound/SoundManager.java @@ -10,10 +10,7 @@ import org.slf4j.LoggerFactory; import java.nio.ByteBuffer; import java.nio.IntBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadLocalRandom; @@ -133,6 +130,10 @@ public class SoundManager { } } + public Collection getSources() { + return Collections.unmodifiableCollection(availableSources); + } + private SoundSource getNextAvailableSoundSource() { for (var source : availableSources) { if (!source.isPlaying()) return source;