Compare commits
4 Commits
2846a88125
...
c832a6648b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c832a6648b | ||
|
|
ab6112b9da | ||
|
|
2fd6d4ba30 | ||
|
|
f248372fcc |
5
TODO.md
5
TODO.md
|
|
@ -102,10 +102,9 @@ A Minecraft survival/PvP plugin with configurable loot, shrinking world border,
|
|||
### Player States
|
||||
Track each registered player in one of these states:
|
||||
- [ ] `REGISTERED` — signed up, round not yet started
|
||||
- [ ] `ALIVE` — actively in the round
|
||||
- [ ] `DEAD` — eliminated, waiting in the safe room
|
||||
- [ ] `IN_GAME` — actively in the round
|
||||
- [ ] `DISCONNECTED` — left the server mid-round
|
||||
- [ ] `SPECTATING` — admin/non-participant observer
|
||||
- [ ] `SPECTATING` — non-participant/dead observer
|
||||
|
||||
### Disconnect Handling
|
||||
- [ ] On disconnect during COUNTDOWN: remove from round, free their spawn slot
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ 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.PlayerService;
|
||||
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;
|
||||
|
|
@ -10,24 +13,32 @@ import org.bukkit.plugin.java.JavaPlugin;
|
|||
@Slf4j
|
||||
public final class Main extends JavaPlugin {
|
||||
|
||||
private BaseCommand command;
|
||||
private RoundService roundService;
|
||||
private PlayerService playerService;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
log.info("Starting HungerGames");
|
||||
reloadConfigs();
|
||||
registerServices();
|
||||
registerCommands();
|
||||
registerEvents();
|
||||
registerSchedulers();
|
||||
}
|
||||
|
||||
private void registerServices() {
|
||||
Round round = Round.createSingletonInstance();
|
||||
roundService = new RoundService(round);
|
||||
playerService = new PlayerService(roundService);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
log.info("Disabling HungerGames");
|
||||
}
|
||||
|
||||
private void registerCommands() {
|
||||
command = new BaseCommand(this);
|
||||
BaseCommand command = new BaseCommand(this, roundService, playerService);
|
||||
}
|
||||
|
||||
private void registerEvents() {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
package com.alttd.hunger_games.commands;
|
||||
|
||||
import com.alttd.hunger_games.Main;
|
||||
import com.alttd.hunger_games.commands.subcommands.Register;
|
||||
import com.alttd.hunger_games.commands.subcommands.RoundState;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.services.PlayerService;
|
||||
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 +21,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, PlayerService playerService) {
|
||||
PluginCommand command = main.getCommand("hungergames");
|
||||
if (command == null) {
|
||||
subCommands = null;
|
||||
|
|
@ -28,7 +32,10 @@ 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),
|
||||
new Register(playerService)
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -42,8 +49,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 +78,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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
package com.alttd.hunger_games.commands.subcommands;
|
||||
|
||||
import com.alttd.hunger_games.commands.SubCommand;
|
||||
import com.alttd.hunger_games.commands.shared_parsers.OnlinePlayerParser;
|
||||
import com.alttd.hunger_games.config.Messages;
|
||||
import com.alttd.hunger_games.data_objects.PLAYER_STATE;
|
||||
import com.alttd.hunger_games.services.PlayerService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Slf4j
|
||||
public class Register extends SubCommand {
|
||||
|
||||
private final static OnlinePlayerParser ONLINE_PLAYER_PARSER = new OnlinePlayerParser();
|
||||
private final static int PLAYER_ARG = 1;
|
||||
private final PlayerService playerService;
|
||||
|
||||
public Register(PlayerService playerService) {
|
||||
this.playerService = playerService;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||
if (args.length != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Optional<Player> optionalPlayer = ONLINE_PLAYER_PARSER.parse(commandSender, args[PLAYER_ARG]);
|
||||
if (optionalPlayer.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Player playerToRegister = optionalPlayer.get();
|
||||
Optional<PLAYER_STATE> optionalPlayerState = playerService.registerPlayer(playerToRegister);
|
||||
PLAYER_STATE nullablePlayerState = optionalPlayerState.orElse(null);
|
||||
if (optionalPlayerState.isEmpty()) {
|
||||
playerToRegister.sendRichMessage(Messages.REGISTER.FAILED_TO_REGISTER_YOU);
|
||||
} else {
|
||||
notifyRegisteredPlayer(playerToRegister, nullablePlayerState);
|
||||
}
|
||||
|
||||
if (commandSender instanceof Player commandPlayer) {
|
||||
notifyPlayerCommandSender(commandPlayer, nullablePlayerState, playerToRegister);
|
||||
}
|
||||
logResult(nullablePlayerState, playerToRegister);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void notifyRegisteredPlayer(Player playerToRegister, PLAYER_STATE nullablePlayerState) {
|
||||
switch (nullablePlayerState) {
|
||||
case REGISTERED -> playerToRegister.sendRichMessage(Messages.REGISTER.SUCCESSFUL_REGISTER);
|
||||
case SPECTATING -> playerToRegister.sendRichMessage(Messages.REGISTER.GAME_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
private void logResult(PLAYER_STATE nullablePlayerState, Player playerToRegister) {
|
||||
if (nullablePlayerState == null) {
|
||||
log.info("Failed to register player {}", playerToRegister.getName());
|
||||
return;
|
||||
}
|
||||
|
||||
switch (nullablePlayerState) {
|
||||
case REGISTERED -> log.info("Registered player {} for the next round", playerToRegister.getName());
|
||||
case IN_GAME -> log.info("Player {} is already in a round", playerToRegister.getName());
|
||||
case DISCONNECTED -> log.info("Player {} is disconnected (should not be possible)", playerToRegister.getName());
|
||||
case SPECTATING -> log.info("Player {} is now spectating the game", playerToRegister.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyPlayerCommandSender(Player commandPlayer, @Nullable PLAYER_STATE playerState, Player playerToRegister) {
|
||||
if (playerState == null) {
|
||||
commandPlayer.sendRichMessage(Messages.REGISTER.FAILED_TO_REGISTER_PLAYER,
|
||||
Placeholder.component("player", playerToRegister.name()));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (playerState) {
|
||||
case REGISTERED -> commandPlayer.sendRichMessage(Messages.REGISTER.PLAYER_REGISTERED,
|
||||
Placeholder.component("player", playerToRegister.name()));
|
||||
case IN_GAME -> commandPlayer.sendRichMessage(Messages.REGISTER.PLAYER_ALREADY_IN_ROUND,
|
||||
Placeholder.component("player", playerToRegister.name()));
|
||||
case DISCONNECTED -> {
|
||||
//should not be possible
|
||||
}
|
||||
case SPECTATING -> commandPlayer.sendRichMessage(Messages.REGISTER.PLAYER_SPECTATING,
|
||||
Placeholder.component("player", playerToRegister.name()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "register";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpMessage() {
|
||||
return Messages.HELP.REGISTER;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
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.Round;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class StartRound extends SubCommand {
|
||||
|
||||
private final Round round;
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender commandSender, String[] args) {
|
||||
round.startRound();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "start";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTabComplete(CommandSender commandSender, String[] args) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpMessage() {
|
||||
return Messages.HELP.START_ROUND;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,14 @@
|
|||
package com.alttd.hunger_games.config;
|
||||
|
||||
import com.alttd.hunger_games.data_objects.GameStage;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Slf4j
|
||||
public class Config extends AbstractConfig {
|
||||
|
|
@ -31,4 +37,60 @@ public class Config extends AbstractConfig {
|
|||
private static void load() {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ROUND {
|
||||
private static final String prefix = "round.";
|
||||
|
||||
public static Duration COUNTDOWN = Duration.ofSeconds(10);
|
||||
public static List<GameStage> STAGES = List.of(
|
||||
GameStage.builder().duration(Duration.ofMinutes(5)).worldBorderSize(1000).build(),
|
||||
GameStage.builder().duration(Duration.ofMinutes(5)).worldBorderSize(750).build(),
|
||||
GameStage.builder().duration(Duration.ofMinutes(5)).worldBorderSize(500).build()
|
||||
);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static void load() {
|
||||
int countdownSeconds = config.getInt(prefix, "countdown-seconds", Math.toIntExact(COUNTDOWN.toSeconds()));
|
||||
COUNTDOWN = Duration.ofSeconds(countdownSeconds);
|
||||
|
||||
ConfigurationSection configurationSection = config.getConfigurationSection(prefix + "stages");
|
||||
Set<String> keys = configurationSection.getKeys(false);
|
||||
STAGES = getGameStages(keys, configurationSection);
|
||||
}
|
||||
|
||||
private static List<GameStage> getGameStages(Set<String> keys, ConfigurationSection configurationSection) {
|
||||
if (keys.isEmpty()) {
|
||||
int key = 0;
|
||||
for (GameStage stage : STAGES) {
|
||||
ConfigurationSection section = configurationSection.createSection(String.valueOf(key++));
|
||||
section.set("duration-seconds", stage.getDuration().toSeconds());
|
||||
section.set("world-border-size", stage.getWorldBorderSize());
|
||||
}
|
||||
return STAGES;
|
||||
}
|
||||
List<GameStage> gameStageList = new ArrayList<>();
|
||||
|
||||
for (String key : keys) {
|
||||
ConfigurationSection section = configurationSection.getConfigurationSection(key);
|
||||
if (section == null) {
|
||||
throw new IllegalStateException("Stage section is null for key [" + key + "] but is in the list of retrieved keys");
|
||||
}
|
||||
if (!section.contains("duration-seconds")) {
|
||||
log.error("Missing duration-seconds in stage {}.", key);
|
||||
continue;
|
||||
}
|
||||
if (!section.contains("world-border-size")) {
|
||||
log.error("Missing world-border-size in stage {}.", key);
|
||||
continue;
|
||||
}
|
||||
int durationSeconds = section.getInt("duration-seconds");
|
||||
int worldBorderSize = section.getInt("world-border-size");
|
||||
gameStageList.add(GameStage.builder()
|
||||
.duration(Duration.ofSeconds(durationSeconds))
|
||||
.worldBorderSize(worldBorderSize)
|
||||
.build());
|
||||
}
|
||||
return gameStageList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,17 @@ 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>";
|
||||
public static String START_ROUND = "<green>Start the game: <gold>/hg start</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);
|
||||
START_ROUND = config.getString(prefix, "start", START_ROUND);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -46,4 +54,38 @@ 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>";
|
||||
public static String PLAYER_REGISTERED = "<green>Registered <player> for the game</green>";
|
||||
public static String PLAYER_ALREADY_IN_ROUND = "<yellow><player> is already in a round</yellow>";
|
||||
public static String PLAYER_SPECTATING = "<yellow><player> is now spectating the game</yellow>";
|
||||
public static String SUCCESSFUL_REGISTER = "<green>Successfully registered you for the upcoming Hunger Games round</green>";
|
||||
public static String GAME_RUNNING = "<yellow>The game is currently running, you will need to spectate the current round</yellow>";
|
||||
|
||||
@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);
|
||||
PLAYER_REGISTERED = config.getString(prefix, "player-registered", PLAYER_REGISTERED);
|
||||
PLAYER_ALREADY_IN_ROUND = config.getString(prefix, "player-already-in-game", PLAYER_ALREADY_IN_ROUND);
|
||||
PLAYER_SPECTATING = config.getString(prefix, "player-spectating", PLAYER_SPECTATING);
|
||||
SUCCESSFUL_REGISTER = config.getString(prefix, "successful", SUCCESSFUL_REGISTER);
|
||||
GAME_RUNNING = config.getString(prefix, "game-running", GAME_RUNNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.alttd.hunger_games.data_objects;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class GameStage {
|
||||
|
||||
private final Duration duration;
|
||||
private final int worldBorderSize;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package com.alttd.hunger_games.data_objects;
|
||||
|
||||
public enum PLAYER_STATE {
|
||||
REGISTERED,
|
||||
IN_GAME,
|
||||
DISCONNECTED,
|
||||
SPECTATING
|
||||
}
|
||||
|
|
@ -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";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.alttd.hunger_games.game;
|
||||
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
@UtilityClass
|
||||
public class GameStageHandler {
|
||||
|
||||
public static void handleStageChange(int worldBorderSize) {
|
||||
//TODO change world border size and handle stage change
|
||||
}
|
||||
|
||||
public static void handleCountdownEnd() {
|
||||
//TODO free players and handle stage change
|
||||
}
|
||||
|
||||
public static void handleWarmup() {
|
||||
//TODO tp players to start area etc (might be handled by state change already, so maybe just messages)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
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.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class PlayerService implements RoundListener {
|
||||
|
||||
private final RoundService roundService;
|
||||
private ROUND_STATE roundState;
|
||||
|
||||
@Override
|
||||
public void stateChange(ROUND_STATE roundState) {
|
||||
this.roundState = roundState;
|
||||
switch (roundState) {
|
||||
case PLAYER_REGISTRATION, KILL_PHASE -> {
|
||||
//Nothing
|
||||
}
|
||||
case COUNTDOWN -> {
|
||||
setNonRegisteredPlayersToSpectator();
|
||||
//TODO: teleport players to spawn location
|
||||
}
|
||||
case FINALE -> {
|
||||
//TODO: teleport players to finale location
|
||||
}
|
||||
case ENDED -> {
|
||||
roundService.clear();
|
||||
//TODO: teleport everyone to spawn (or spectator?) location
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setNonRegisteredPlayersToSpectator() {
|
||||
Set<UUID> playerUUIDs = roundService.getPlayers(PLAYER_STATE.REGISTERED);
|
||||
List<Player> playerList = new ArrayList<>(Bukkit.getOnlinePlayers());
|
||||
List<Player> unregisteredPlayers = playerList.stream().filter(player -> !playerUUIDs.contains(player.getUniqueId())).toList();
|
||||
unregisteredPlayers.forEach(player -> roundService.setPlayerState(player.getUniqueId(), PLAYER_STATE.SPECTATING));
|
||||
}
|
||||
|
||||
public Optional<PLAYER_STATE> registerPlayer(Player player) {
|
||||
if (roundState == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Optional<PLAYER_STATE> optionalPlayerState = determinePlayerState(player.getUniqueId());
|
||||
if (optionalPlayerState.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
PLAYER_STATE playerState = optionalPlayerState.get();
|
||||
|
||||
if (playerState == PLAYER_STATE.SPECTATING) {
|
||||
//TODO: teleport player to spectator location
|
||||
}
|
||||
|
||||
roundService.setPlayerState(player.getUniqueId(), playerState);
|
||||
return optionalPlayerState;
|
||||
}
|
||||
|
||||
private Optional<PLAYER_STATE> determinePlayerState(UUID playerUuid) {
|
||||
if (roundState == null) {
|
||||
return Optional.empty();
|
||||
}
|
||||
Optional<PLAYER_STATE> optionalPlayerState = roundService.getPlayerState(playerUuid);
|
||||
if (optionalPlayerState.isPresent()) {
|
||||
PLAYER_STATE playerState = optionalPlayerState.get();
|
||||
return Optional.of(switch (playerState) {
|
||||
case DISCONNECTED, SPECTATING -> PLAYER_STATE.SPECTATING;
|
||||
case IN_GAME -> PLAYER_STATE.IN_GAME;
|
||||
case REGISTERED -> PLAYER_STATE.REGISTERED;
|
||||
});
|
||||
}
|
||||
return switch (roundState) {
|
||||
case PLAYER_REGISTRATION, COUNTDOWN -> Optional.of(PLAYER_STATE.REGISTERED);
|
||||
case KILL_PHASE, FINALE, ENDED -> Optional.of(PLAYER_STATE.SPECTATING);
|
||||
};
|
||||
}
|
||||
}
|
||||
89
src/main/java/com/alttd/hunger_games/services/Round.java
Normal file
89
src/main/java/com/alttd/hunger_games/services/Round.java
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
package com.alttd.hunger_games.services;
|
||||
|
||||
import com.alttd.hunger_games.config.Config;
|
||||
import com.alttd.hunger_games.data_objects.GameStage;
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
import com.alttd.hunger_games.game.GameStageHandler;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Round {
|
||||
|
||||
private static Round instance = null;
|
||||
private final List<RoundListener> listeners = new ArrayList<>();
|
||||
private ROUND_STATE roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
||||
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
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 stop() {
|
||||
roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||
}
|
||||
|
||||
private void nextStage() {
|
||||
Optional<ROUND_STATE> optionalNextRoundState = roundState.next();
|
||||
if (optionalNextRoundState.isEmpty()) {
|
||||
roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||
return;
|
||||
}
|
||||
roundState = optionalNextRoundState.get();
|
||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||
}
|
||||
|
||||
public void startRound() {
|
||||
if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
||||
throw new IllegalStateException("Round can not be started before player registration.");
|
||||
}
|
||||
roundState = ROUND_STATE.COUNTDOWN;
|
||||
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
||||
GameStageHandler.handleWarmup();
|
||||
Duration warmupDuration = Config.ROUND.COUNTDOWN;
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
GameStageHandler.handleCountdownEnd();
|
||||
nextStage();
|
||||
scheduleNextStage(0);
|
||||
}, warmupDuration.toSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void scheduleNextStage(int index) {
|
||||
List<GameStage> gameStageList = Config.ROUND.STAGES;
|
||||
if (gameStageList.size() <= index) {
|
||||
nextStage();
|
||||
return;
|
||||
}
|
||||
|
||||
GameStage gameStage = gameStageList.get(index);
|
||||
Duration duration = gameStage.getDuration();
|
||||
scheduledExecutorService.schedule(() -> {
|
||||
GameStageHandler.handleStageChange(gameStage.getWorldBorderSize());
|
||||
scheduleNextStage(index + 1);
|
||||
}, duration.toSeconds(), TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.alttd.hunger_games.services;
|
||||
|
||||
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
||||
|
||||
public interface RoundListener {
|
||||
|
||||
void stateChange(ROUND_STATE roundState);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
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.*;
|
||||
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);
|
||||
}
|
||||
|
||||
protected void clear() {
|
||||
players.clear();
|
||||
}
|
||||
|
||||
public Optional<PLAYER_STATE> getPlayerState(UUID uuid) {
|
||||
return Optional.ofNullable(players.get(uuid));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stateChange(ROUND_STATE roundState) {
|
||||
this.roundState = roundState;
|
||||
if (roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
||||
clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user