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);
new CommandManager(this, gameManager, flag, worldBorderApi);
//Ensuring immediate respawn is on in all worlds
log.info("Enabling immediate respawn for {}.", Config.FLAG.world);
World world = Bukkit.getWorld(Config.FLAG.world);
log.info("Enabling immediate respawn for {}.", GameConfig.FLAG.world);
World world = Bukkit.getWorld(GameConfig.FLAG.world);
if (world != null) {
world.setGameRule(GameRule.DO_IMMEDIATE_RESPAWN, true);
} 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;
@Slf4j
public class GameConfig extends AbstractConfig{
public class GameConfig extends AbstractConfig {
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;
Messages(Main main) {
super(main, "config.yml");
super(main, "messages.yml");
}
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;
import com.alttd.ctf.Main;
import com.alttd.ctf.config.Config;
import com.alttd.ctf.game.GameManager;
import com.alttd.ctf.game.GamePhase;
import com.alttd.ctf.team.TeamPlayer;
@ -54,7 +55,7 @@ public class OnPlayerDeath implements Listener {
}
TeamPlayer teamPlayer = optionalTeamPlayer.get();
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;
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.team.Team;
import com.alttd.ctf.team.TeamColor;
import com.alttd.ctf.team.TeamPlayer;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.kyori.adventure.text.minimessage.MiniMessage;
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.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.scheduler.BukkitScheduler;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
@Slf4j
@ -49,11 +44,11 @@ public class Flag implements Runnable {
public Flag(Main main, GameManager gameManager) {
this.main = main;
this.gameManager = gameManager;
World world = Bukkit.getWorld(Config.FLAG.world);
World world = Bukkit.getWorld(GameConfig.FLAG.world);
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() {
@ -194,7 +189,7 @@ public class Flag implements Runnable {
if (max.isEmpty()) {
return Optional.empty();
}
if (max.get().getValue() < 100) {
if (max.get().getValue() < GameConfig.FLAG.WINNING_SCORE) {
return Optional.empty();
}
return gameManager.getTeam(max.get().getKey());
@ -209,7 +204,7 @@ public class Flag implements Runnable {
private CompletableFuture<Boolean> updateScoreBasedOnNearbyPlayers() {
CompletableFuture<Boolean> future = new CompletableFuture<>();
Bukkit.getScheduler().runTask(main, () -> {
Collection<Player> nearbyPlayers = flagLocation.getNearbyPlayers(10);
Collection<Player> nearbyPlayers = flagLocation.getNearbyPlayers(GameConfig.FLAG.CAPTURE_RADIUS);
Bukkit.getScheduler().runTaskAsynchronously(main, () -> {
boolean result = updateScoreBasedOnNearbyPlayers(nearbyPlayers);
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())));
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);
}

View File

@ -33,6 +33,7 @@ public class RunningGame implements Runnable {
@Override
public void run() {
try {
GamePhase nextPhase = GamePhase.values().length < currentPhase.ordinal() ? null : GamePhase.values()[currentPhase.ordinal() + 1];
if (phaseStartTime == null) {
phaseStartTime = Instant.now();
@ -47,6 +48,10 @@ public class RunningGame implements Runnable {
} else if (nextPhase != null) {
broadcastNextPhaseStartTime(currentPhase, nextPhase);
}
} catch (Exception e) {
log.error("Unexpected error in running game", e);
throw new RuntimeException(e);
}
}
private void nextPhaseActions(@Nullable GamePhase previousPhase, @NotNull GamePhase phase, @Nullable GamePhase nextPhase) {

View File

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

View File

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