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 lombok.Setter; import java.time.Duration; import java.time.Instant; 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.ScheduledFuture; import java.util.concurrent.TimeUnit; public class Round { private static Round instance = null; private final List listeners = new ArrayList<>(); private ROUND_STATE roundState = ROUND_STATE.PLAYER_REGISTRATION; private ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); private ScheduledFuture countdownFuture = null; private Runnable onEnd = null; @Setter private BossBarService bossBarService; 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() { if (countdownFuture != null) { countdownFuture.cancel(true); } roundState = ROUND_STATE.PLAYER_REGISTRATION; listeners.forEach(roundListener -> roundListener.stateChange(roundState)); } public void endRound() { if (countdownFuture != null) { countdownFuture.cancel(true); } roundState = ROUND_STATE.ENDED; listeners.forEach(roundListener -> roundListener.stateChange(roundState)); } private void nextStage() { Optional 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; startCountdown(warmupDuration, () -> { GameStageHandler.handleCountdownEnd(); nextStage(); scheduleNextStage(0); }); } private void startCountdown(Duration duration, Runnable onEnd) { if (countdownFuture != null) { countdownFuture.cancel(true); } this.onEnd = onEnd; 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); } private void scheduleNextStage(int index) { List gameStageList = Config.ROUND.STAGES; if (gameStageList.size() <= index) { nextStage(); return; } GameStage gameStage = gameStageList.get(index); Duration duration = gameStage.getDuration(); startCountdown(duration, () -> { GameStageHandler.handleStageChange(gameStage.getWorldBorderSize()); scheduleNextStage(index + 1); }); } 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); } } } }