Implement round and player state management services along with /hg roundstate command.

This commit is contained in:
akastijn 2026-05-24 00:13:25 +02:00
parent f248372fcc
commit 2fd6d4ba30
11 changed files with 310 additions and 7 deletions

View File

@ -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() {

View File

@ -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<SubCommand> 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,11 +75,12 @@ 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());
}
}
return res;
}

View File

@ -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<Player> {
@Override
public Optional<Player> 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);
}
}

View File

@ -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<String> getTabComplete(CommandSender commandSender, String[] args) {
return List.of();
}
@Override
public String getHelpMessage() {
return Messages.HELP.ROUND_STATE;
}
}

View File

@ -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 = "<gold>HungerGames help:\n<commands></gold>";
public static String HELP_MESSAGE = "<green>Show this menu: <gold>/hg help</gold></green>";
public static String ROUND_STATE = "<green>Show the current round state: <gold>/hg roundstate</gold></green>";
public static String REGISTER = "<green>Register a player for the game: <gold>/hg register <player></gold></green>";
@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 = "<green>Current round state is: <gold>round_state</gold></green>";
@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 = "<red>You were unable to join HungerGames because no HungerGames round was found</red>";
public static String FAILED_TO_REGISTER_PLAYER = "<red>Unable to register player <player></red>";
@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);
}
}
}

View File

@ -0,0 +1,8 @@
package com.alttd.hunger_games.data_objects;
public enum PLAYER_STATE {
REGISTERED,
IN_GAME,
DISCONNECTED,
SPECTATING
}

View File

@ -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<ROUND_STATE> 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";
};
}
}

View File

@ -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<PLAYER_STATE> registerPlayer(Player player) {
if (roundState == null) {
return Optional.empty();
}
switch (roundState) {
case PLAYER_REGISTRATION -> {
}
case COUNTDOWN -> {
}
case KILL_PHASE -> {
}
case FINALE -> {
}
case ENDED -> {
}
}
}
}

View File

@ -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<RoundListener> 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<ROUND_STATE> optionalNextRoundState = roundState.next();
if (optionalNextRoundState.isEmpty()) {
roundState = null;
reset();
return;
}
roundState = optionalNextRoundState.get();
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
}
}

View File

@ -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();
}

View File

@ -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<UUID, PLAYER_STATE> players = new HashMap<>();
public RoundService(Round round) {
this.roundState = round.register(this);
}
public Set<UUID> 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;
}
}