From 2fd6d4ba30479425ef6242a0383b41194bd9a258 Mon Sep 17 00:00:00 2001 From: akastijn Date: Sun, 24 May 2026 00:13:25 +0200 Subject: [PATCH] Implement round and player state management services along with `/hg roundstate` command. --- .../java/com/alttd/hunger_games/Main.java | 12 +++- .../hunger_games/commands/BaseCommand.java | 18 ++++-- .../shared_parsers/OnlinePlayerParser.java | 22 ++++++++ .../commands/subcommands/RoundState.java | 39 +++++++++++++ .../alttd/hunger_games/config/Messages.java | 30 ++++++++++ .../data_objects/PLAYER_STATE.java | 8 +++ .../data_objects/ROUND_STATE.java | 28 ++++++++++ .../hunger_games/services/PlayerService.java | 44 +++++++++++++++ .../alttd/hunger_games/services/Round.java | 56 +++++++++++++++++++ .../hunger_games/services/RoundListener.java | 11 ++++ .../hunger_games/services/RoundService.java | 49 ++++++++++++++++ 11 files changed, 310 insertions(+), 7 deletions(-) create mode 100644 src/main/java/com/alttd/hunger_games/commands/shared_parsers/OnlinePlayerParser.java create mode 100644 src/main/java/com/alttd/hunger_games/commands/subcommands/RoundState.java create mode 100644 src/main/java/com/alttd/hunger_games/data_objects/PLAYER_STATE.java create mode 100644 src/main/java/com/alttd/hunger_games/data_objects/ROUND_STATE.java create mode 100644 src/main/java/com/alttd/hunger_games/services/PlayerService.java create mode 100644 src/main/java/com/alttd/hunger_games/services/Round.java create mode 100644 src/main/java/com/alttd/hunger_games/services/RoundListener.java create mode 100644 src/main/java/com/alttd/hunger_games/services/RoundService.java diff --git a/src/main/java/com/alttd/hunger_games/Main.java b/src/main/java/com/alttd/hunger_games/Main.java index 67096ec..1418ff4 100644 --- a/src/main/java/com/alttd/hunger_games/Main.java +++ b/src/main/java/com/alttd/hunger_games/Main.java @@ -3,6 +3,8 @@ package com.alttd.hunger_games; import com.alttd.hunger_games.commands.BaseCommand; import com.alttd.hunger_games.config.Config; import com.alttd.hunger_games.config.Messages; +import com.alttd.hunger_games.services.Round; +import com.alttd.hunger_games.services.RoundService; import lombok.extern.slf4j.Slf4j; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -11,23 +13,31 @@ import org.bukkit.plugin.java.JavaPlugin; public final class Main extends JavaPlugin { private BaseCommand command; + private Round round; + private RoundService roundService; @Override public void onEnable() { log.info("Starting HungerGames"); reloadConfigs(); + registerServices(); registerCommands(); registerEvents(); registerSchedulers(); } + private void registerServices() { + round = Round.createSingletonInstance(); + roundService = new RoundService(round); + } + @Override public void onDisable() { log.info("Disabling HungerGames"); } private void registerCommands() { - command = new BaseCommand(this); + command = new BaseCommand(this, roundService); } private void registerEvents() { diff --git a/src/main/java/com/alttd/hunger_games/commands/BaseCommand.java b/src/main/java/com/alttd/hunger_games/commands/BaseCommand.java index 19aefc8..3a99082 100644 --- a/src/main/java/com/alttd/hunger_games/commands/BaseCommand.java +++ b/src/main/java/com/alttd/hunger_games/commands/BaseCommand.java @@ -1,7 +1,9 @@ package com.alttd.hunger_games.commands; import com.alttd.hunger_games.Main; +import com.alttd.hunger_games.commands.subcommands.RoundState; import com.alttd.hunger_games.config.Messages; +import com.alttd.hunger_games.services.RoundService; import lombok.Getter; import lombok.extern.slf4j.Slf4j; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; @@ -17,7 +19,7 @@ import java.util.stream.Collectors; public class BaseCommand implements CommandExecutor, TabExecutor { private final List subCommands; - public BaseCommand(Main main) { + public BaseCommand(Main main, RoundService roundService) { PluginCommand command = main.getCommand("hungergames"); if (command == null) { subCommands = null; @@ -28,7 +30,9 @@ public class BaseCommand implements CommandExecutor, TabExecutor { command.setTabCompleter(this); command.setAliases(List.of("hg")); - subCommands = new ArrayList<>(List.of()); + subCommands = new ArrayList<>(List.of( + new RoundState(roundService) + )); } @Override @@ -42,8 +46,9 @@ public class BaseCommand implements CommandExecutor, TabExecutor { } SubCommand subCommand = getSubCommand(args[0]); - if (subCommand == null) + if (subCommand == null) { return false; + } if (!commandSender.hasPermission(subCommand.getPermission())) { commandSender.sendRichMessage(Messages.GENERIC.NO_PERMISSION, Placeholder.parsed("permission", subCommand.getPermission())); @@ -70,10 +75,11 @@ public class BaseCommand implements CommandExecutor, TabExecutor { ); } else { SubCommand subCommand = getSubCommand(args[0]); - if (subCommand != null && commandSender.hasPermission(subCommand.getPermission())) + if (subCommand != null && commandSender.hasPermission(subCommand.getPermission())) { res.addAll(subCommand.getTabComplete(commandSender, args).stream() - .filter(str -> str.toLowerCase().startsWith(args[args.length - 1].toLowerCase())) - .toList()); + .filter(str -> str.toLowerCase().startsWith(args[args.length - 1].toLowerCase())) + .toList()); + } } return res; } diff --git a/src/main/java/com/alttd/hunger_games/commands/shared_parsers/OnlinePlayerParser.java b/src/main/java/com/alttd/hunger_games/commands/shared_parsers/OnlinePlayerParser.java new file mode 100644 index 0000000..172f2a3 --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/commands/shared_parsers/OnlinePlayerParser.java @@ -0,0 +1,22 @@ +package com.alttd.hunger_games.commands.shared_parsers; + +import com.alttd.hunger_games.commands.ArgumentParser; +import com.alttd.hunger_games.config.Messages; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import java.util.Optional; + +public class OnlinePlayerParser implements ArgumentParser { + + @Override + public Optional parse(CommandSender commandSender, String playerName) { + Player player = commandSender.getServer().getPlayer(playerName); + if (player == null || !player.isOnline()) { + commandSender.sendRichMessage(Messages.GENERIC.PLAYER_NOT_FOUND, Placeholder.parsed("player", playerName)); + return Optional.empty(); + } + return Optional.of(player); + } +} diff --git a/src/main/java/com/alttd/hunger_games/commands/subcommands/RoundState.java b/src/main/java/com/alttd/hunger_games/commands/subcommands/RoundState.java new file mode 100644 index 0000000..b36422b --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/commands/subcommands/RoundState.java @@ -0,0 +1,39 @@ +package com.alttd.hunger_games.commands.subcommands; + +import com.alttd.hunger_games.commands.SubCommand; +import com.alttd.hunger_games.config.Messages; +import com.alttd.hunger_games.services.RoundService; +import lombok.RequiredArgsConstructor; +import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; +import org.bukkit.command.CommandSender; + +import java.util.List; + +@RequiredArgsConstructor +public class RoundState extends SubCommand { + + private final RoundService roundService; + + @Override + public boolean onCommand(CommandSender commandSender, String[] args) { + commandSender.sendRichMessage(Messages.ROUND_STATE.ROUND_STATE, + Placeholder.parsed("round_state", + roundService.getRoundState().getHumandReadableName())); + return true; + } + + @Override + public String getName() { + return "roundstate"; + } + + @Override + public List getTabComplete(CommandSender commandSender, String[] args) { + return List.of(); + } + + @Override + public String getHelpMessage() { + return Messages.HELP.ROUND_STATE; + } +} diff --git a/src/main/java/com/alttd/hunger_games/config/Messages.java b/src/main/java/com/alttd/hunger_games/config/Messages.java index b76ec87..39823ce 100644 --- a/src/main/java/com/alttd/hunger_games/config/Messages.java +++ b/src/main/java/com/alttd/hunger_games/config/Messages.java @@ -1,5 +1,7 @@ package com.alttd.hunger_games.config; +import org.jetbrains.annotations.NotNull; + import java.io.File; public class Messages extends AbstractConfig { @@ -24,11 +26,15 @@ public class Messages extends AbstractConfig { public static String HELP_MESSAGE_WRAPPER = "HungerGames help:\n"; public static String HELP_MESSAGE = "Show this menu: /hg help"; + public static String ROUND_STATE = "Show the current round state: /hg roundstate"; + public static String REGISTER = "Register a player for the game: /hg register "; @SuppressWarnings("unused") private static void load() { HELP_MESSAGE_WRAPPER = config.getString(prefix, "help-wrapper", HELP_MESSAGE_WRAPPER); HELP_MESSAGE = config.getString(prefix, "help", HELP_MESSAGE); + ROUND_STATE = config.getString(prefix, "round-state", ROUND_STATE); + REGISTER = config.getString(prefix, "register", REGISTER); } } @@ -46,4 +52,28 @@ public class Messages extends AbstractConfig { PLAYER_NOT_FOUND = config.getString(prefix, "player-only", PLAYER_NOT_FOUND); } } + + public static class ROUND_STATE { + private static final String prefix = "round-state."; + + public static String ROUND_STATE = "Current round state is: round_state"; + + @SuppressWarnings("unused") + private static void load() { + ROUND_STATE = config.getString(prefix, "round-state", ROUND_STATE); + } + } + + public static class REGISTER { + private static final String prefix = "register."; + + public static String FAILED_TO_REGISTER_YOU = "You were unable to join HungerGames because no HungerGames round was found"; + public static String FAILED_TO_REGISTER_PLAYER = "Unable to register player "; + + @SuppressWarnings("unused") + private static void load() { + FAILED_TO_REGISTER_YOU = config.getString(prefix, "failed", FAILED_TO_REGISTER_YOU); + FAILED_TO_REGISTER_PLAYER = config.getString(prefix, "failed-player", FAILED_TO_REGISTER_PLAYER); + } + } } diff --git a/src/main/java/com/alttd/hunger_games/data_objects/PLAYER_STATE.java b/src/main/java/com/alttd/hunger_games/data_objects/PLAYER_STATE.java new file mode 100644 index 0000000..65c3145 --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/data_objects/PLAYER_STATE.java @@ -0,0 +1,8 @@ +package com.alttd.hunger_games.data_objects; + +public enum PLAYER_STATE { + REGISTERED, + IN_GAME, + DISCONNECTED, + SPECTATING +} diff --git a/src/main/java/com/alttd/hunger_games/data_objects/ROUND_STATE.java b/src/main/java/com/alttd/hunger_games/data_objects/ROUND_STATE.java new file mode 100644 index 0000000..7e77fe7 --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/data_objects/ROUND_STATE.java @@ -0,0 +1,28 @@ +package com.alttd.hunger_games.data_objects; + +import java.util.Optional; + +public enum ROUND_STATE { + PLAYER_REGISTRATION, + COUNTDOWN, + KILL_PHASE, + FINALE, + ENDED; + + public Optional next() { + if (ordinal() == ENDED.ordinal()) { + return Optional.empty(); + } + return Optional.of(values()[ordinal() + 1]); + } + + public String getHumandReadableName() { + return switch (this) { + case PLAYER_REGISTRATION -> "Player Registration"; + case COUNTDOWN -> "Countdown"; + case KILL_PHASE -> "Kill Phase"; + case FINALE -> "Finale"; + case ENDED -> "Ended"; + }; + } +} diff --git a/src/main/java/com/alttd/hunger_games/services/PlayerService.java b/src/main/java/com/alttd/hunger_games/services/PlayerService.java new file mode 100644 index 0000000..45043c0 --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/services/PlayerService.java @@ -0,0 +1,44 @@ +package com.alttd.hunger_games.services; + +import com.alttd.hunger_games.data_objects.PLAYER_STATE; +import com.alttd.hunger_games.data_objects.ROUND_STATE; +import lombok.RequiredArgsConstructor; +import org.bukkit.entity.Player; + +import java.util.Optional; + +@RequiredArgsConstructor +public class PlayerService implements RoundListener { + + private final RoundService roundService; + private ROUND_STATE roundState; + + @Override + public void stateChange(ROUND_STATE roundState) { + this.roundState = roundState; + //TODO: Implement player state change logic + } + + @Override + public void roundReset() { + //TODO: Implement player reset logic + } + + public Optional registerPlayer(Player player) { + if (roundState == null) { + return Optional.empty(); + } + switch (roundState) { + case PLAYER_REGISTRATION -> { + } + case COUNTDOWN -> { + } + case KILL_PHASE -> { + } + case FINALE -> { + } + case ENDED -> { + } + } + } +} diff --git a/src/main/java/com/alttd/hunger_games/services/Round.java b/src/main/java/com/alttd/hunger_games/services/Round.java new file mode 100644 index 0000000..5993ed4 --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/services/Round.java @@ -0,0 +1,56 @@ +package com.alttd.hunger_games.services; + +import com.alttd.hunger_games.data_objects.ROUND_STATE; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class Round { + + private static Round instance = null; + private final List listeners = new ArrayList<>(); + private ROUND_STATE roundState = null; + + private Round() { + + } + + public static Round createSingletonInstance() throws IllegalStateException { + if (instance != null) { + throw new IllegalStateException("Round is already initialized."); + } + instance = new Round(); + return instance; + } + + public ROUND_STATE register(RoundListener listener) { + listeners.add(listener); + return roundState; + } + + public void unregister(RoundListener listener) { + listeners.remove(listener); + } + + public void reset() { + listeners.forEach(RoundListener::roundReset); + } + + public void start() { + roundState = ROUND_STATE.PLAYER_REGISTRATION; + listeners.forEach(roundListener -> roundListener.stateChange(roundState)); + } + + public void nextStage() { + Optional optionalNextRoundState = roundState.next(); + if (optionalNextRoundState.isEmpty()) { + roundState = null; + reset(); + return; + } + roundState = optionalNextRoundState.get(); + listeners.forEach(roundListener -> roundListener.stateChange(roundState)); + } + +} diff --git a/src/main/java/com/alttd/hunger_games/services/RoundListener.java b/src/main/java/com/alttd/hunger_games/services/RoundListener.java new file mode 100644 index 0000000..d8d2727 --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/services/RoundListener.java @@ -0,0 +1,11 @@ +package com.alttd.hunger_games.services; + +import com.alttd.hunger_games.data_objects.ROUND_STATE; + +public interface RoundListener { + + void stateChange(ROUND_STATE roundState); + + void roundReset(); + +} diff --git a/src/main/java/com/alttd/hunger_games/services/RoundService.java b/src/main/java/com/alttd/hunger_games/services/RoundService.java new file mode 100644 index 0000000..a2addef --- /dev/null +++ b/src/main/java/com/alttd/hunger_games/services/RoundService.java @@ -0,0 +1,49 @@ +package com.alttd.hunger_games.services; + +import com.alttd.hunger_games.data_objects.PLAYER_STATE; +import com.alttd.hunger_games.data_objects.ROUND_STATE; +import lombok.Getter; +import org.bukkit.entity.Player; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.UUID; +import java.util.stream.Collectors; + +public class RoundService implements RoundListener { + + @Getter + private ROUND_STATE roundState; + private final HashMap players = new HashMap<>(); + + public RoundService(Round round) { + this.roundState = round.register(this); + } + + public Set getPlayers(PLAYER_STATE playerState) { + return players.entrySet().stream() + .filter(entry -> entry.getValue().equals(playerState)) + .map(Map.Entry::getKey) + .collect(Collectors.toSet()); + } + + protected void setPlayerState(UUID uuid, PLAYER_STATE playerState) { + players.put(uuid, playerState); + } + + public PLAYER_STATE getPlayerState(UUID uuid) { + return players.get(uuid); + } + + @Override + public void stateChange(ROUND_STATE roundState) { + this.roundState = roundState; + } + + @Override + public void roundReset() { + players.clear(); + roundState = null; + } +}