Refactor configuration handling and add world border support

Moved FLAG settings from Config to GameConfig for better modularity. Introduced world border configuration with types and sizes for different game phases. Updated related classes to utilize new configuration structure and added error handling to prevent runtime failures.
This commit is contained in:
Teriuihi 2025-02-08 23:00:04 +01:00
parent d7432c9b89
commit 74cf3589c0
11 changed files with 95 additions and 50 deletions

View File

@ -52,8 +52,8 @@ public class Main extends JavaPlugin {
flag = new Flag(this, gameManager); flag = new Flag(this, gameManager);
new CommandManager(this, gameManager, flag, worldBorderApi); new CommandManager(this, gameManager, flag, worldBorderApi);
//Ensuring immediate respawn is on in all worlds //Ensuring immediate respawn is on in all worlds
log.info("Enabling immediate respawn for {}.", Config.FLAG.world); log.info("Enabling immediate respawn for {}.", GameConfig.FLAG.world);
World world = Bukkit.getWorld(Config.FLAG.world); World world = Bukkit.getWorld(GameConfig.FLAG.world);
if (world != null) { if (world != null) {
world.setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true); world.setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true);
} else { } else {

View File

@ -41,21 +41,4 @@ public class Config extends AbstractConfig{
} }
} }
public static class FLAG {
private static final String prefix = "flag.";
public static String world = "world";
public static double x = 0;
public static double y = 0;
public static double z = 0;
@SuppressWarnings("unused")
private static void load() {
world = config.getString(prefix, "world", world);
x = config.getDouble(prefix, "x", x);
y = config.getDouble(prefix, "y", y);
z = config.getDouble(prefix, "z", z);
}
}
} }

View File

@ -8,7 +8,7 @@ import java.time.Duration;
import java.util.HashMap; import java.util.HashMap;
@Slf4j @Slf4j
public class GameConfig extends AbstractConfig{ public class GameConfig extends AbstractConfig {
static GameConfig config; static GameConfig config;
@ -45,4 +45,55 @@ public class GameConfig extends AbstractConfig{
} }
} }
@SuppressWarnings("unused")
public static class WORLD_BORDER {
private static final String prefix = "world-border.";
private static final HashMap<GamePhase, WorldBorderSettings> GAME_PHASE_WORLD_BORDER = new HashMap<>();
public static HashMap<GamePhase, WorldBorderSettings> getGAME_PHASE_WORLD_BORDER() {
return new HashMap<>(GAME_PHASE_WORLD_BORDER);
}
@SuppressWarnings("unused")
private static void load() {
GAME_PHASE_WORLD_BORDER.clear();
for (GamePhase phase : GamePhase.values()) {
String stringType = config.getString(prefix, phase.name().toLowerCase() + ".type", WorldBorderType.PLAYER.name());
double size = config.getDouble(prefix, phase.name().toLowerCase() + ".size", 10);
WorldBorderType worldBorderType;
try {
worldBorderType = WorldBorderType.valueOf(stringType);
} catch (IllegalArgumentException e) {
log.error("Invalid world border type [{}] in game phase world border config", stringType);
continue;
}
GAME_PHASE_WORLD_BORDER.put(phase, new WorldBorderSettings(worldBorderType, size));
log.debug("Set {} phase world border type to {} blocks", phase.name(), size);
}
}
}
public static class FLAG {
private static final String prefix = "flag.";
public static String world = "world";
public static double x = 0;
public static double y = 0;
public static double z = 0;
public static double CAPTURE_RADIUS = 7;
public static int WINNING_SCORE = 50;
@SuppressWarnings("unused")
private static void load() {
world = config.getString(prefix, "world", world);
x = config.getDouble(prefix, "x", x);
y = config.getDouble(prefix, "y", y);
z = config.getDouble(prefix, "z", z);
CAPTURE_RADIUS = config.getDouble(prefix, "capture-radius", CAPTURE_RADIUS);
WINNING_SCORE = config.getInt(prefix, "winning-score", WINNING_SCORE);
}
}
} }

View File

@ -8,7 +8,7 @@ public class Messages extends AbstractConfig {
static Messages config; static Messages config;
Messages(Main main) { Messages(Main main) {
super(main, "config.yml"); super(main, "messages.yml");
} }
public static void reload(Main main) { public static void reload(Main main) {

View File

@ -0,0 +1,4 @@
package com.alttd.ctf.config;
public record WorldBorderSettings(WorldBorderType type, double size) {
}

View File

@ -0,0 +1,6 @@
package com.alttd.ctf.config;
public enum WorldBorderType {
FLAG,
PLAYER
}

View File

@ -1,6 +1,7 @@
package com.alttd.ctf.events; package com.alttd.ctf.events;
import com.alttd.ctf.Main; import com.alttd.ctf.Main;
import com.alttd.ctf.config.Config;
import com.alttd.ctf.game.GameManager; import com.alttd.ctf.game.GameManager;
import com.alttd.ctf.game.GamePhase; import com.alttd.ctf.game.GamePhase;
import com.alttd.ctf.team.TeamPlayer; import com.alttd.ctf.team.TeamPlayer;
@ -54,7 +55,7 @@ public class OnPlayerDeath implements Listener {
} }
TeamPlayer teamPlayer = optionalTeamPlayer.get(); TeamPlayer teamPlayer = optionalTeamPlayer.get();
event.setRespawnLocation(player.getWorld().getSpawnLocation()); event.setRespawnLocation(player.getWorld().getSpawnLocation());
Bukkit.getScheduler().runTaskLater(main, () -> teamPlayer.getGameClass().apply(teamPlayer, worldBorderApi, gamePhase.get(), true), 10 * 20);//10 x 20 ticks aka 10 seconds Bukkit.getScheduler().runTaskLater(main, () -> teamPlayer.getGameClass().apply(teamPlayer, worldBorderApi, gamePhase.get(), true), Config.SETTINGS * 20);//10 x 20 ticks aka 10 seconds
} }
} }

View File

@ -1,13 +1,12 @@
package com.alttd.ctf.flag; package com.alttd.ctf.flag;
import com.alttd.ctf.Main; import com.alttd.ctf.Main;
import com.alttd.ctf.config.Config; import com.alttd.ctf.config.GameConfig;
import com.alttd.ctf.game.GameManager; import com.alttd.ctf.game.GameManager;
import com.alttd.ctf.team.Team; import com.alttd.ctf.team.Team;
import com.alttd.ctf.team.TeamColor; import com.alttd.ctf.team.TeamColor;
import com.alttd.ctf.team.TeamPlayer; import com.alttd.ctf.team.TeamPlayer;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.MiniMessage; import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder; import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
@ -21,13 +20,9 @@ import org.bukkit.boss.KeyedBossBar;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot; import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitScheduler;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Slf4j @Slf4j
@ -49,11 +44,11 @@ public class Flag implements Runnable {
public Flag(Main main, GameManager gameManager) { public Flag(Main main, GameManager gameManager) {
this.main = main; this.main = main;
this.gameManager = gameManager; this.gameManager = gameManager;
World world = Bukkit.getWorld(Config.FLAG.world); World world = Bukkit.getWorld(GameConfig.FLAG.world);
if (world == null) { if (world == null) {
throw new IllegalStateException(String.format("Tried to spawn flag in world [%s] that doesn't exist", Config.FLAG.world)); throw new IllegalStateException(String.format("Tried to spawn flag in world [%s] that doesn't exist", GameConfig.FLAG.world));
} }
this.flagLocation = new Location(world, Config.FLAG.x, Config.FLAG.y, Config.FLAG.z); this.flagLocation = new Location(world, GameConfig.FLAG.x, GameConfig.FLAG.y, GameConfig.FLAG.z);
} }
private BossBar createBossBar() { private BossBar createBossBar() {
@ -194,7 +189,7 @@ public class Flag implements Runnable {
if (max.isEmpty()) { if (max.isEmpty()) {
return Optional.empty(); return Optional.empty();
} }
if (max.get().getValue() < 100) { if (max.get().getValue() < GameConfig.FLAG.WINNING_SCORE) {
return Optional.empty(); return Optional.empty();
} }
return gameManager.getTeam(max.get().getKey()); return gameManager.getTeam(max.get().getKey());
@ -209,7 +204,7 @@ public class Flag implements Runnable {
private CompletableFuture<Boolean> updateScoreBasedOnNearbyPlayers() { private CompletableFuture<Boolean> updateScoreBasedOnNearbyPlayers() {
CompletableFuture<Boolean> future = new CompletableFuture<>(); CompletableFuture<Boolean> future = new CompletableFuture<>();
Bukkit.getScheduler().runTask(main, () -> { Bukkit.getScheduler().runTask(main, () -> {
Collection<Player> nearbyPlayers = flagLocation.getNearbyPlayers(10); Collection<Player> nearbyPlayers = flagLocation.getNearbyPlayers(GameConfig.FLAG.CAPTURE_RADIUS);
Bukkit.getScheduler().runTaskAsynchronously(main, () -> { Bukkit.getScheduler().runTaskAsynchronously(main, () -> {
boolean result = updateScoreBasedOnNearbyPlayers(nearbyPlayers); boolean result = updateScoreBasedOnNearbyPlayers(nearbyPlayers);
future.complete(result); future.complete(result);
@ -288,7 +283,7 @@ public class Flag implements Runnable {
bossBar.setTitle(String.format("Team %s is capturing the flag", PlainTextComponentSerializer.plainText().serialize(team.get().getName()))); bossBar.setTitle(String.format("Team %s is capturing the flag", PlainTextComponentSerializer.plainText().serialize(team.get().getName())));
lastWinningTeamId = highestKey; lastWinningTeamId = highestKey;
} }
bossBar.setProgress(Math.min(100, teamFlagPointCount.get(highestKey)) / 100.0); bossBar.setProgress(Math.min(GameConfig.FLAG.WINNING_SCORE, teamFlagPointCount.get(highestKey)) / (double) GameConfig.FLAG.WINNING_SCORE);
bossBar.setVisible(teamFlagPointCount.get(highestKey) > 0); bossBar.setVisible(teamFlagPointCount.get(highestKey) > 0);
} }

View File

@ -33,19 +33,24 @@ public class RunningGame implements Runnable {
@Override @Override
public void run() { public void run() {
GamePhase nextPhase = GamePhase.values().length < currentPhase.ordinal() ? null : GamePhase.values()[currentPhase.ordinal() + 1]; try {
if (phaseStartTime == null) { GamePhase nextPhase = GamePhase.values().length < currentPhase.ordinal() ? null : GamePhase.values()[currentPhase.ordinal() + 1];
phaseStartTime = Instant.now(); if (phaseStartTime == null) {
nextPhaseActions(null, currentPhase, nextPhase); phaseStartTime = Instant.now();
} nextPhaseActions(null, currentPhase, nextPhase);
}
if (Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) { if (Duration.between(phaseStartTime, Instant.now()).compareTo(phaseDurations.get(currentPhase)) >= 0) {
GamePhase previousPhase = currentPhase; GamePhase previousPhase = currentPhase;
currentPhase = GamePhase.values()[currentPhase.ordinal() + 1]; currentPhase = GamePhase.values()[currentPhase.ordinal() + 1];
nextPhaseActions(previousPhase, currentPhase, nextPhase); nextPhaseActions(previousPhase, currentPhase, nextPhase);
phaseStartTime = Instant.now(); phaseStartTime = Instant.now();
} else if (nextPhase != null) { } else if (nextPhase != null) {
broadcastNextPhaseStartTime(currentPhase, nextPhase); broadcastNextPhaseStartTime(currentPhase, nextPhase);
}
} catch (Exception e) {
log.error("Unexpected error in running game", e);
throw new RuntimeException(e);
} }
} }

View File

@ -1,6 +1,6 @@
package com.alttd.ctf.game.phases; package com.alttd.ctf.game.phases;
import com.alttd.ctf.config.Config; import com.alttd.ctf.config.GameConfig;
import com.alttd.ctf.flag.Flag; import com.alttd.ctf.flag.Flag;
import com.alttd.ctf.game.GamePhase; import com.alttd.ctf.game.GamePhase;
import com.alttd.ctf.game.GamePhaseExecutor; import com.alttd.ctf.game.GamePhaseExecutor;
@ -32,7 +32,7 @@ public class EndedPhase implements GamePhaseExecutor {
HashMap<Team, Integer> wins = flag.getWins(); HashMap<Team, Integer> wins = flag.getWins();
Bukkit.broadcast(Component.join(JoinConfiguration.separator(Component.newline()), getWinnerMessages(wins))); Bukkit.broadcast(Component.join(JoinConfiguration.separator(Component.newline()), getWinnerMessages(wins)));
new Thread(() -> { new Thread(() -> {
World world = Bukkit.getWorld(Config.FLAG.world); World world = Bukkit.getWorld(GameConfig.FLAG.world);
if (world == null) { if (world == null) {
log.error("Invalid flag world defined"); log.error("Invalid flag world defined");
return; return;

View File

@ -1,3 +1,3 @@
#Sat Feb 08 21:33:29 CET 2025 #Sat Feb 08 21:57:04 CET 2025
buildNumber=29 buildNumber=30
version=0.1 version=0.1