diff --git a/client/src/main/resources/sound/m_hit_1.wav b/client/src/main/resources/sound/m_hit_1.wav new file mode 100644 index 0000000..bd2e544 Binary files /dev/null and b/client/src/main/resources/sound/m_hit_1.wav differ diff --git a/client/src/main/resources/sound/m_hit_2.wav b/client/src/main/resources/sound/m_hit_2.wav new file mode 100644 index 0000000..c7c979f Binary files /dev/null and b/client/src/main/resources/sound/m_hit_2.wav differ 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 bac002c..11e2f61 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java +++ b/server/src/main/java/nl/andrewl/aos2_server/ClientCommunicationHandler.java @@ -80,24 +80,8 @@ public class ClientCommunicationHandler { sendTcpMessage(new ChunkDataMessage(chunk)); } } else if (msg instanceof ChatWrittenMessage chatWrittenMessage) { - if (chatWrittenMessage.message().startsWith("/t ")) { - if (player.getTeam() != null) { - var chat = new ChatMessage( - System.currentTimeMillis(), - player.getUsername(), - chatWrittenMessage.message().substring(3) - ); - for (var teamPlayer : server.getTeamManager().getPlayers(player.getTeam())) { - server.getPlayerManager().getHandler(teamPlayer).sendTcpMessage(chat); - } - } - } else if (chatWrittenMessage.message().equalsIgnoreCase("/kd")) { - int k = player.getKillCount(); - int d = player.getDeathCount(); - float kd = d <= 0 ? 0 : (float) k / (float) d; - sendTcpMessage(ChatMessage.privateMessage("Your kill/death ratio is %.2f.".formatted(kd))); - } else if (chatWrittenMessage.message().equalsIgnoreCase("/kill")) { - server.getPlayerManager().playerKilled(player, null); + if (chatWrittenMessage.message().startsWith("/")) { + server.handleCommand(chatWrittenMessage.message(), player, this); } else { server.getPlayerManager().broadcastTcpMessage(new ChatMessage( System.currentTimeMillis(), diff --git a/server/src/main/java/nl/andrewl/aos2_server/Server.java b/server/src/main/java/nl/andrewl/aos2_server/Server.java index d37e0e1..b1612f4 100644 --- a/server/src/main/java/nl/andrewl/aos2_server/Server.java +++ b/server/src/main/java/nl/andrewl/aos2_server/Server.java @@ -1,6 +1,7 @@ package nl.andrewl.aos2_server; import nl.andrewl.aos2_server.cli.ServerCli; +import nl.andrewl.aos2_server.cli.ingame.PlayerCommandHandler; import nl.andrewl.aos2_server.config.ServerConfig; import nl.andrewl.aos2_server.logic.WorldUpdater; import nl.andrewl.aos2_server.model.ServerPlayer; @@ -37,6 +38,7 @@ public class Server implements Runnable { private final PlayerManager playerManager; private final TeamManager teamManager; private final ProjectileManager projectileManager; + private final PlayerCommandHandler commandHandler; private final World world; private final WorldUpdater worldUpdater; @@ -49,6 +51,7 @@ public class Server implements Runnable { this.playerManager = new PlayerManager(this); this.teamManager = new TeamManager(this); this.projectileManager = new ProjectileManager(this); + this.commandHandler = new PlayerCommandHandler(this); this.worldUpdater = new WorldUpdater(this, config.ticksPerSecond); if (config.world.startsWith("worlds.")) { @@ -175,6 +178,10 @@ public class Server implements Runnable { return projectileManager; } + public void handleCommand(String cmd, ServerPlayer player, ClientCommunicationHandler handler) { + commandHandler.handle(cmd, player, handler); + } + public static void main(String[] args) throws IOException { List configPaths = Config.getCommonConfigPaths(); configPaths.add(0, Path.of("server.yaml")); diff --git a/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/PlayerCommand.java b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/PlayerCommand.java new file mode 100644 index 0000000..d1a897d --- /dev/null +++ b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/PlayerCommand.java @@ -0,0 +1,12 @@ +package nl.andrewl.aos2_server.cli.ingame; + +import nl.andrewl.aos2_server.ClientCommunicationHandler; +import nl.andrewl.aos2_server.Server; +import nl.andrewl.aos2_server.model.ServerPlayer; + +/** + * Represents a component for handling a certain type of command. + */ +public interface PlayerCommand { + void handle(String[] args, ServerPlayer player, ClientCommunicationHandler handler, Server server); +} diff --git a/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/PlayerCommandHandler.java b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/PlayerCommandHandler.java new file mode 100644 index 0000000..dc7471a --- /dev/null +++ b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/PlayerCommandHandler.java @@ -0,0 +1,56 @@ +package nl.andrewl.aos2_server.cli.ingame; + +import nl.andrewl.aos2_server.ClientCommunicationHandler; +import nl.andrewl.aos2_server.Server; +import nl.andrewl.aos2_server.cli.ingame.commands.KillCommand; +import nl.andrewl.aos2_server.cli.ingame.commands.KillDeathRatioCommand; +import nl.andrewl.aos2_server.model.ServerPlayer; +import nl.andrewl.aos_core.net.client.ChatMessage; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class PlayerCommandHandler { + private static final Pattern commandSplitter = Pattern.compile("\"(.*)\"|'(.*)'|(\\S+)"); + + private final Server server; + private final Map commands; + + public PlayerCommandHandler(Server server) { + this.server = server; + commands = new HashMap<>(); + commands.put("kd", new KillDeathRatioCommand()); + commands.put("kill", new KillCommand()); + } + + public void handle(String rawCommand, ServerPlayer player, ClientCommunicationHandler handler) { + Matcher matcher = commandSplitter.matcher(rawCommand); + List matches = new ArrayList<>(); + while (matcher.find()) { + for (int i = matcher.groupCount() - 1; i >= 0; i--) { + String group = matcher.group(i); + if (group != null) { + matches.add(group); + break; + } + } + } + String mainCommandString = matches.get(0).substring(1).trim().toLowerCase(); + if (!mainCommandString.isBlank()) { + PlayerCommand command = commands.get(mainCommandString); + if (command != null) { + String[] args = new String[matches.size() - 1]; + matches.subList(1, matches.size()).toArray(args); + command.handle(args, player, handler, server); + } else { + handler.sendTcpMessage(ChatMessage.privateMessage("Unknown command: \"%s\".".formatted(mainCommandString))); + } + } else { + handler.sendTcpMessage(ChatMessage.privateMessage("Invalid or missing command.")); + } + } +} diff --git a/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/KillCommand.java b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/KillCommand.java new file mode 100644 index 0000000..8491cad --- /dev/null +++ b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/KillCommand.java @@ -0,0 +1,13 @@ +package nl.andrewl.aos2_server.cli.ingame.commands; + +import nl.andrewl.aos2_server.ClientCommunicationHandler; +import nl.andrewl.aos2_server.Server; +import nl.andrewl.aos2_server.cli.ingame.PlayerCommand; +import nl.andrewl.aos2_server.model.ServerPlayer; + +public class KillCommand implements PlayerCommand { + @Override + public void handle(String[] args, ServerPlayer player, ClientCommunicationHandler handler, Server server) { + server.getPlayerManager().playerKilled(player, null); + } +} diff --git a/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/KillDeathRatioCommand.java b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/KillDeathRatioCommand.java new file mode 100644 index 0000000..a1fe6f2 --- /dev/null +++ b/server/src/main/java/nl/andrewl/aos2_server/cli/ingame/commands/KillDeathRatioCommand.java @@ -0,0 +1,18 @@ +package nl.andrewl.aos2_server.cli.ingame.commands; + +import nl.andrewl.aos2_server.ClientCommunicationHandler; +import nl.andrewl.aos2_server.Server; +import nl.andrewl.aos2_server.cli.ingame.PlayerCommand; +import nl.andrewl.aos2_server.model.ServerPlayer; +import nl.andrewl.aos_core.net.client.ChatMessage; + +public class KillDeathRatioCommand implements PlayerCommand { + @Override + public void handle(String[] args, ServerPlayer player, ClientCommunicationHandler handler, Server server) { + float killCount = player.getKillCount(); + float deathCount = player.getDeathCount(); + float kd = 0; + if (deathCount > 0) kd = killCount / deathCount; + handler.sendTcpMessage(ChatMessage.privateMessage("Your kill/death ratio is %.2f.".formatted(kd))); + } +}