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 9aac7f5..b242a4d 100644 --- a/client/src/main/java/nl/andrewl/aos2_client/Client.java +++ b/client/src/main/java/nl/andrewl/aos2_client/Client.java @@ -96,7 +96,7 @@ public class Client implements Runnable { gameRenderer.getCamera().interpolatePosition(dt); interpolatePlayers(now, dt); interpolateProjectiles(dt); - soundManager.playWalkingSounds(myPlayer, now); + soundManager.playWalkingSounds(myPlayer, world, now); gameRenderer.draw(); lastFrameAt = now; @@ -121,6 +121,7 @@ public class Client implements Runnable { myPlayer.getPosition().set(playerUpdate.px(), playerUpdate.py(), playerUpdate.pz()); myPlayer.getVelocity().set(playerUpdate.vx(), playerUpdate.vy(), playerUpdate.vz()); myPlayer.setCrouching(playerUpdate.crouching()); + myPlayer.setMode(playerUpdate.mode()); if (gameRenderer != null) { gameRenderer.getCamera().setToPlayer(myPlayer); } @@ -138,7 +139,7 @@ public class Client implements Runnable { } }); } else if (msg instanceof ClientInventoryMessage inventoryMessage) { - myPlayer.setInventory(inventoryMessage.inv()); + runLater(() -> myPlayer.setInventory(inventoryMessage.inv())); } else if (msg instanceof InventorySelectedStackMessage selectedStackMessage) { myPlayer.getInventory().setSelectedIndex(selectedStackMessage.index()); } else if (msg instanceof ItemStackMessage itemStackMessage) { @@ -238,7 +239,7 @@ public class Client implements Runnable { movement.set(player.getVelocity()).mul(dt); player.getPosition().add(movement); player.updateModelTransform(); - soundManager.playWalkingSounds(player, now); + soundManager.playWalkingSounds(player, world, now); } gameRenderer.getGuiRenderer().updateNamePlates(players.values()); } 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 b7afd74..0330608 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 @@ -11,11 +11,14 @@ import nl.andrewl.aos2_client.render.model.Model; import nl.andrewl.aos_core.model.PlayerMode; import nl.andrewl.aos_core.model.Team; import nl.andrewl.aos_core.model.item.BlockItemStack; +import nl.andrewl.aos_core.model.item.Inventory; import nl.andrewl.aos_core.model.item.ItemTypes; import org.joml.Matrix3f; import org.joml.Matrix4f; import org.joml.Vector3f; -import org.lwjgl.glfw.*; +import org.lwjgl.glfw.Callbacks; +import org.lwjgl.glfw.GLFWErrorCallback; +import org.lwjgl.glfw.GLFWVidMode; import org.lwjgl.opengl.GL; import java.io.IOException; @@ -173,6 +176,7 @@ public class GameRenderer { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); ClientPlayer myPlayer = client.getMyPlayer(); + Inventory inv = myPlayer.getInventory(); if (inputHandler.isNormalContextActive() && inputHandler.getNormalContext().isScopeEnabled()) { updatePerspective(15); } else { @@ -199,7 +203,7 @@ public class GameRenderer { // Render guns! rifleModel.bind(); - if (myPlayer.getInventory().getSelectedItemStack().getType().getId() == ItemTypes.RIFLE.getId()) { + if (inv.getSelectedItemStack() != null && inv.getSelectedItemStack().getType().getId() == ItemTypes.RIFLE.getId()) { modelRenderer.render(rifleModel, myPlayer.getHeldItemTransformData(), myPlayer.getHeldItemNormalTransformData()); } for (var player : client.getPlayers().values()) { @@ -210,7 +214,7 @@ public class GameRenderer { } rifleModel.unbind(); smgModel.bind(); - if (myPlayer.getInventory().getSelectedItemStack().getType().getId() == ItemTypes.AK_47.getId()) { + if (inv.getSelectedItemStack() != null && inv.getSelectedItemStack().getType().getId() == ItemTypes.AK_47.getId()) { modelRenderer.render(smgModel, myPlayer.getHeldItemTransformData(), myPlayer.getHeldItemNormalTransformData()); } for (var player : client.getPlayers().values()) { @@ -221,7 +225,7 @@ public class GameRenderer { } smgModel.unbind(); shotgunModel.bind(); - if (myPlayer.getInventory().getSelectedItemStack().getType().getId() == ItemTypes.WINCHESTER.getId()) { + if (inv.getSelectedItemStack() != null && inv.getSelectedItemStack().getType().getId() == ItemTypes.WINCHESTER.getId()) { modelRenderer.render(shotgunModel, myPlayer.getHeldItemTransformData(), myPlayer.getHeldItemNormalTransformData()); } for (var player : client.getPlayers().values()) { @@ -233,7 +237,7 @@ public class GameRenderer { shotgunModel.unbind(); blockModel.bind(); - if (myPlayer.getInventory().getSelectedItemStack().getType().getId() == ItemTypes.BLOCK.getId()) { + if (inv.getSelectedItemStack() != null && inv.getSelectedItemStack().getType().getId() == ItemTypes.BLOCK.getId()) { BlockItemStack stack = (BlockItemStack) myPlayer.getInventory().getSelectedItemStack(); modelRenderer.setAspectColor(client.getWorld().getPalette().getColor(stack.getSelectedValue())); modelRenderer.render(blockModel, myPlayer.getHeldItemTransformData(), myPlayer.getHeldItemNormalTransformData()); 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 00bf9b0..811615c 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 @@ -8,6 +8,7 @@ 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.PlayerMode; import nl.andrewl.aos_core.model.item.BlockItem; import nl.andrewl.aos_core.model.item.BlockItemStack; import nl.andrewl.aos_core.model.item.Gun; @@ -196,10 +197,12 @@ public class GuiRenderer { glUniform1i(namePlateTextureSamplerUniform, 0); glUniformMatrix4fv(namePlateViewTransformUniform, false, viewTransformData); glUniformMatrix4fv(namePlatePerspectiveTransformUniform, false, perspectiveTransformData); + // Show nameplates from farther away if we're in creative/spectator. + float nameplateRadius = myPlayer.getMode() == PlayerMode.NORMAL ? 50 : 200; for (var entry : playerNamePlates.entrySet()) { OtherPlayer player = entry.getKey(); - // Skip rendering far-away nameplates. - if (player.getPosition().distance(myPlayer.getPosition()) > 50) continue; + // There are some scenarios where we skip rendering the name. + if (player.getPosition().distance(myPlayer.getPosition()) > nameplateRadius || player.getMode() == PlayerMode.SPECTATOR) continue; GuiTexture texture = entry.getValue(); float aspectRatio = (float) texture.getHeight() / (float) texture.getWidth(); transformMatrix.identity() @@ -223,11 +226,12 @@ public class GuiRenderer { nvgSave(vgId); boolean scopeEnabled = client.getInputHandler().getNormalContext().isScopeEnabled(); + PlayerMode mode = client.getMyPlayer().getMode(); drawCrosshair(width, height, scopeEnabled); drawHeldItemStackInfo(width, height, client.getMyPlayer()); if (!scopeEnabled) { drawChat(width, height, client); - drawHealthBar(width, height, client.getMyPlayer()); + if (mode == PlayerMode.NORMAL) drawHealthBar(width, height, client.getMyPlayer()); } if (client.getInputHandler().getNormalContext().isDebugEnabled()) { drawDebugInfo(width, height, client); 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 bcac781..94601cd 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 @@ -1,6 +1,8 @@ package nl.andrewl.aos2_client.sound; import nl.andrewl.aos_core.model.Player; +import nl.andrewl.aos_core.model.PlayerMode; +import nl.andrewl.aos_core.model.world.World; import org.joml.Vector3f; import org.lwjgl.openal.AL; import org.lwjgl.openal.ALC; @@ -115,8 +117,9 @@ public class SoundManager { play(soundName, gain, position, new Vector3f(0, 0, 0)); } - public void playWalkingSounds(Player player, long now) { - if (player.getVelocity().length() <= 0) return; + public void playWalkingSounds(Player player, World world, long now) { + // Don't play sounds for players who are still, non-normal mode, or not on the ground. + if (player.getVelocity().length() <= 0 || player.getMode() != PlayerMode.NORMAL || !player.isGrounded(world)) return; long lastSoundAt = lastPlayerWalkingSounds.computeIfAbsent(player, p -> 0L); long delay = 500; // Delay in ms between footfalls. if (player.getVelocity().length() > 5) delay -= 150; diff --git a/core/src/main/java/nl/andrewl/aos_core/model/item/Inventory.java b/core/src/main/java/nl/andrewl/aos_core/model/item/Inventory.java index e5533f4..4b82c6e 100644 --- a/core/src/main/java/nl/andrewl/aos_core/model/item/Inventory.java +++ b/core/src/main/java/nl/andrewl/aos_core/model/item/Inventory.java @@ -31,6 +31,7 @@ public class Inventory { } public ItemStack getSelectedItemStack() { + if (itemStacks.isEmpty()) return null; return itemStacks.get(selectedIndex); } @@ -62,4 +63,9 @@ public class Inventory { } return 1; } + + public void clear() { + itemStacks.clear(); + selectedIndex = -1; + } } diff --git a/server/src/main/java/nl/andrewl/aos2_server/PlayerManager.java b/server/src/main/java/nl/andrewl/aos2_server/PlayerManager.java index 662df22..f00b78d 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/PlayerManager.java +++ b/server/src/main/java/nl/andrewl/aos2_server/PlayerManager.java @@ -4,10 +4,7 @@ import nl.andrewl.aos2_server.model.ServerPlayer; import nl.andrewl.aos_core.Net; import nl.andrewl.aos_core.model.PlayerMode; import nl.andrewl.aos_core.model.Team; -import nl.andrewl.aos_core.model.item.BlockItemStack; -import nl.andrewl.aos_core.model.item.Gun; -import nl.andrewl.aos_core.model.item.GunItemStack; -import nl.andrewl.aos_core.model.item.ItemStack; +import nl.andrewl.aos_core.model.item.*; import nl.andrewl.aos_core.net.client.*; import nl.andrewl.aos_core.net.connect.DatagramInit; import nl.andrewl.record_net.Message; @@ -47,7 +44,7 @@ public class PlayerManager { joinMessage = username + " joined the game."; } player.setPosition(getBestSpawnPoint(player)); - player.setMode(PlayerMode.NORMAL); + setMode(player, PlayerMode.NORMAL); // Tell all other players that this one has joined. broadcastTcpMessageToAllBut(new PlayerJoinMessage( player.getId(), player.getUsername(), player.getTeam() == null ? -1 : player.getTeam().getId(), @@ -201,6 +198,19 @@ public class PlayerManager { handler.sendTcpMessage(ChatMessage.privateMessage("You've been resupplied at your team base.")); } + public void setMode(ServerPlayer player, PlayerMode mode) { + player.setMode(mode); + var inv = player.getInventory(); + inv.clear(); + if (mode == PlayerMode.NORMAL || mode == PlayerMode.CREATIVE) { + inv.getItemStacks().add(new GunItemStack(ItemTypes.RIFLE)); + inv.getItemStacks().add(new GunItemStack(ItemTypes.AK_47)); + inv.getItemStacks().add(new GunItemStack(ItemTypes.WINCHESTER)); + inv.getItemStacks().add(new BlockItemStack(ItemTypes.BLOCK, 50, (byte) 1)); + inv.setSelectedIndex(0); + } + } + public void handleUdpInit(DatagramInit init, DatagramPacket packet) { var handler = getHandler(init.clientId()); if (handler != null) { diff --git a/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/PlayerModeCommand.java b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/PlayerModeCommand.java index 62c2939..141bc2f 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/PlayerModeCommand.java +++ b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/PlayerModeCommand.java @@ -6,6 +6,7 @@ import nl.andrewl.aos2_server.cli.ingame.PlayerCommand; import nl.andrewl.aos2_server.model.ServerPlayer; import nl.andrewl.aos_core.model.PlayerMode; import nl.andrewl.aos_core.net.client.ChatMessage; +import nl.andrewl.aos_core.net.client.ClientInventoryMessage; public class PlayerModeCommand implements PlayerCommand { @Override @@ -17,7 +18,8 @@ public class PlayerModeCommand implements PlayerCommand { String modeText = args[0].trim().toUpperCase(); try { PlayerMode mode = PlayerMode.valueOf(modeText); - player.setMode(mode); + server.getPlayerManager().setMode(player, mode); + handler.sendTcpMessage(new ClientInventoryMessage(player.getInventory())); server.getPlayerManager().broadcastUdpMessage(player.getUpdateMessage(System.currentTimeMillis())); } catch (IllegalArgumentException e) { handler.sendTcpMessage(ChatMessage.privateMessage("Invalid mode. Should be NORMAL, CREATIVE, or SPECTATOR.")); diff --git a/server/src/main/java/nl/andrewl/aos2_server/logic/PlayerActionManager.java b/server/src/main/java/nl/andrewl/aos2_server/logic/PlayerActionManager.java index dfaccd9..0483b24 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/logic/PlayerActionManager.java +++ b/server/src/main/java/nl/andrewl/aos2_server/logic/PlayerActionManager.java @@ -65,27 +65,30 @@ public class PlayerActionManager { public void tick(long now, float dt, World world, Server server) { updated = false; // Reset the updated flag. This will be set to true if the player was updated in this tick. - if (player.getInventory().getSelectedIndex() != input.selectedInventoryIndex()) { - player.getInventory().setSelectedIndex(input.selectedInventoryIndex()); - // Tell the client that their inventory slot has been updated properly. - server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new InventorySelectedStackMessage(player.getInventory().getSelectedIndex())); - updated = true; // Tell everyone else that this player's selected item has changed. - } - ItemStack selectedStack = player.getInventory().getSelectedItemStack(); - if (selectedStack instanceof BlockItemStack b) { - tickBlockAction(now, server, world, b); - } else if (selectedStack instanceof GunItemStack g) { - tickGunAction(now, server, g); - } + if (player.getMode() != PlayerMode.SPECTATOR) { + if (player.getInventory().getSelectedIndex() != input.selectedInventoryIndex()) { + player.getInventory().setSelectedIndex(input.selectedInventoryIndex()); + // Tell the client that their inventory slot has been updated properly. + server.getPlayerManager().getHandler(player.getId()).sendDatagramPacket(new InventorySelectedStackMessage(player.getInventory().getSelectedIndex())); + updated = true; // Tell everyone else that this player's selected item has changed. + } - if ( - now - lastResupplyAt > server.getConfig().actions.resupplyCooldown * 1000 && - player.getTeam() != null && - player.getPosition().distance(player.getTeam().getSpawnPoint()) < server.getConfig().actions.resupplyRadius - ) { - server.getPlayerManager().resupply(player); - lastResupplyAt = now; + ItemStack selectedStack = player.getInventory().getSelectedItemStack(); + if (selectedStack instanceof BlockItemStack b) { + tickBlockAction(now, server, world, b); + } else if (selectedStack instanceof GunItemStack g) { + tickGunAction(now, server, g); + } + + if ( + now - lastResupplyAt > server.getConfig().actions.resupplyCooldown * 1000 && + player.getTeam() != null && + player.getPosition().distance(player.getTeam().getSpawnPoint()) < server.getConfig().actions.resupplyRadius + ) { + server.getPlayerManager().resupply(player); + lastResupplyAt = now; + } } if (player.getMode() == PlayerMode.NORMAL && server.getConfig().actions.healthRegenPerSecond != 0 && player.getHealth() < 1) { diff --git a/server/src/main/java/nl/andrewl/aos2_server/model/ServerPlayer.java b/server/src/main/java/nl/andrewl/aos2_server/model/ServerPlayer.java index 02126d8..37666a8 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/model/ServerPlayer.java +++ b/server/src/main/java/nl/andrewl/aos2_server/model/ServerPlayer.java @@ -2,10 +2,7 @@ package nl.andrewl.aos2_server.model; import nl.andrewl.aos2_server.logic.PlayerActionManager; import nl.andrewl.aos_core.model.Player; -import nl.andrewl.aos_core.model.item.BlockItemStack; -import nl.andrewl.aos_core.model.item.GunItemStack; import nl.andrewl.aos_core.model.item.Inventory; -import nl.andrewl.aos_core.model.item.ItemTypes; import nl.andrewl.aos_core.net.client.PlayerUpdateMessage; import java.util.ArrayList; @@ -32,10 +29,6 @@ public class ServerPlayer extends Player { this.inventory = new Inventory(new ArrayList<>(), 0); this.health = 1f; this.actionManager = new PlayerActionManager(this); - inventory.getItemStacks().add(new GunItemStack(ItemTypes.RIFLE)); - inventory.getItemStacks().add(new GunItemStack(ItemTypes.AK_47)); - inventory.getItemStacks().add(new GunItemStack(ItemTypes.WINCHESTER)); - inventory.getItemStacks().add(new BlockItemStack(ItemTypes.BLOCK, 50, (byte) 1)); } public PlayerActionManager getActionManager() { @@ -84,7 +77,7 @@ public class ServerPlayer extends Player { velocity.x, velocity.y, velocity.z, orientation.x, orientation.y, actionManager.getInput().crouching(), - inventory.getSelectedItemStack().getType().getId(), + inventory.getSelectedItemStack() == null ? -1 : inventory.getSelectedItemStack().getType().getId(), mode ); }