2026-05-23 22:13:25 +00:00
|
|
|
package com.alttd.hunger_games.services;
|
|
|
|
|
|
2026-05-23 23:30:11 +00:00
|
|
|
import com.alttd.hunger_games.config.Config;
|
|
|
|
|
import com.alttd.hunger_games.data_objects.GameStage;
|
2026-05-23 22:13:25 +00:00
|
|
|
import com.alttd.hunger_games.data_objects.ROUND_STATE;
|
2026-05-23 23:30:11 +00:00
|
|
|
import com.alttd.hunger_games.game.GameStageHandler;
|
2026-06-22 00:22:02 +00:00
|
|
|
import lombok.Setter;
|
2026-05-23 22:13:25 +00:00
|
|
|
|
2026-05-23 23:30:11 +00:00
|
|
|
import java.time.Duration;
|
2026-06-22 00:02:42 +00:00
|
|
|
import java.time.Instant;
|
2026-05-23 22:13:25 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.Optional;
|
2026-05-23 23:30:11 +00:00
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
import java.util.concurrent.ScheduledExecutorService;
|
2026-06-22 00:02:42 +00:00
|
|
|
import java.util.concurrent.ScheduledFuture;
|
2026-05-23 23:30:11 +00:00
|
|
|
import java.util.concurrent.TimeUnit;
|
2026-05-23 22:13:25 +00:00
|
|
|
|
|
|
|
|
public class Round {
|
|
|
|
|
|
|
|
|
|
private static Round instance = null;
|
|
|
|
|
private final List<RoundListener> listeners = new ArrayList<>();
|
2026-05-23 23:30:11 +00:00
|
|
|
private ROUND_STATE roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
|
|
|
|
private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
2026-06-22 00:02:42 +00:00
|
|
|
private ScheduledFuture<?> countdownFuture = null;
|
2026-06-22 00:22:02 +00:00
|
|
|
private Runnable onEnd = null;
|
|
|
|
|
@Setter
|
2026-06-22 00:02:42 +00:00
|
|
|
private BossBarService bossBarService;
|
2026-05-23 22:13:25 +00:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-23 23:30:11 +00:00
|
|
|
public void stop() {
|
2026-06-22 00:02:42 +00:00
|
|
|
if (countdownFuture != null) {
|
|
|
|
|
countdownFuture.cancel(true);
|
|
|
|
|
}
|
2026-05-23 22:13:25 +00:00
|
|
|
roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
|
|
|
|
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-15 21:17:47 +00:00
|
|
|
public void endRound() {
|
2026-06-22 00:02:42 +00:00
|
|
|
if (countdownFuture != null) {
|
|
|
|
|
countdownFuture.cancel(true);
|
|
|
|
|
}
|
2026-06-15 21:17:47 +00:00
|
|
|
roundState = ROUND_STATE.ENDED;
|
|
|
|
|
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-23 23:30:11 +00:00
|
|
|
private void nextStage() {
|
2026-05-23 22:13:25 +00:00
|
|
|
Optional<ROUND_STATE> optionalNextRoundState = roundState.next();
|
|
|
|
|
if (optionalNextRoundState.isEmpty()) {
|
2026-05-23 23:30:11 +00:00
|
|
|
roundState = ROUND_STATE.PLAYER_REGISTRATION;
|
|
|
|
|
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
2026-05-23 22:13:25 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
roundState = optionalNextRoundState.get();
|
|
|
|
|
listeners.forEach(roundListener -> roundListener.stateChange(roundState));
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-23 23:30:11 +00:00
|
|
|
public void startRound() {
|
2026-05-31 14:40:16 +00:00
|
|
|
if (!roundState.equals(ROUND_STATE.PLAYER_REGISTRATION)) {
|
2026-05-23 23:30:11 +00:00
|
|
|
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;
|
2026-06-22 00:02:42 +00:00
|
|
|
|
|
|
|
|
startCountdown(warmupDuration, () -> {
|
2026-05-23 23:30:11 +00:00
|
|
|
GameStageHandler.handleCountdownEnd();
|
|
|
|
|
nextStage();
|
|
|
|
|
scheduleNextStage(0);
|
2026-06-22 00:02:42 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void startCountdown(Duration duration, Runnable onEnd) {
|
|
|
|
|
if (countdownFuture != null) {
|
|
|
|
|
countdownFuture.cancel(true);
|
|
|
|
|
}
|
2026-06-22 00:22:02 +00:00
|
|
|
this.onEnd = onEnd;
|
2026-06-22 00:02:42 +00:00
|
|
|
|
|
|
|
|
final Instant endTime = Instant.now().plus(duration);
|
|
|
|
|
countdownFuture = scheduledExecutorService.scheduleAtFixedRate(() -> {
|
|
|
|
|
Duration timeLeft = Duration.between(Instant.now(), endTime);
|
|
|
|
|
if (bossBarService != null) {
|
|
|
|
|
bossBarService.updateCountdown(timeLeft, duration, roundState);
|
|
|
|
|
}
|
|
|
|
|
if (timeLeft.isZero() || timeLeft.isNegative()) {
|
|
|
|
|
onEnd.run();
|
|
|
|
|
}
|
|
|
|
|
}, 0, 1, TimeUnit.SECONDS);
|
2026-05-23 23:30:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
2026-06-22 00:02:42 +00:00
|
|
|
|
|
|
|
|
startCountdown(duration, () -> {
|
2026-05-23 23:30:11 +00:00
|
|
|
GameStageHandler.handleStageChange(gameStage.getWorldBorderSize());
|
|
|
|
|
scheduleNextStage(index + 1);
|
2026-06-22 00:02:42 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 00:22:02 +00:00
|
|
|
public void forceNextPhase() {
|
|
|
|
|
if (countdownFuture == null || onEnd == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
countdownFuture.cancel(true);
|
|
|
|
|
if (roundState.equals(ROUND_STATE.KILL_PHASE)) {
|
|
|
|
|
onEnd.run();
|
|
|
|
|
} else {
|
|
|
|
|
nextStage();
|
|
|
|
|
if (roundState.equals(ROUND_STATE.SAFE_PHASE)) {
|
|
|
|
|
scheduleNextStage(0);
|
|
|
|
|
} else if (roundState.equals(ROUND_STATE.KILL_PHASE)) {
|
|
|
|
|
scheduleNextStage(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-05-23 23:30:11 +00:00
|
|
|
}
|
2026-06-22 00:22:02 +00:00
|
|
|
|
2026-05-23 22:13:25 +00:00
|
|
|
}
|