diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2026-03-17 17:57:12 +0100 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2026-03-17 17:57:12 +0100 |
| commit | c7e7f82a957b15979ed3f9e52d195850be179f88 (patch) | |
| tree | 54bd194a35b263bd9e35244e1deaaeaa9d3a52e9 /src | |
| parent | 724dbeea6672d31658d6563c1fe372a6f4cb4fbb (diff) | |
refactor(game): use ticks for timer
Diffstat (limited to 'src')
5 files changed, 239 insertions, 73 deletions
diff --git a/src/main/java/world/anhgelus/molehunt/game/Game.java b/src/main/java/world/anhgelus/molehunt/game/Game.java index 6ab5a4a..98fe2b8 100644 --- a/src/main/java/world/anhgelus/molehunt/game/Game.java +++ b/src/main/java/world/anhgelus/molehunt/game/Game.java @@ -15,6 +15,8 @@ import net.minecraft.text.Text; import net.minecraft.world.GameMode;
import net.minecraft.world.rule.GameRules;
import world.anhgelus.molehunt.Molehunt;
+import world.anhgelus.molehunt.timer.TickTask;
+import world.anhgelus.molehunt.timer.TimerAccess;
import world.anhgelus.molehunt.utils.TimeUtils;
import java.util.*;
@@ -23,16 +25,11 @@ import java.util.stream.Collectors; public class Game {
- private Timer timer = new Timer();
- public final int defaultTime = Molehunt.CONFIG.getGameDuration()*60;
- private int remaining = defaultTime;
-
+ public final int defaultTime = Molehunt.CONFIG.getGameDuration() * 60;
private final MinecraftServer server;
-
private final List<UUID> moles = new ArrayList<>();
-
private final TitleFadeS2CPacket timing = new TitleFadeS2CPacket(20, 40, 20);
-
+ private int remaining = defaultTime;
private boolean started = false;
public Game(MinecraftServer server) {
@@ -62,21 +59,17 @@ public class Game { // gamerules for the start
gamerules.setValue(GameRules.DO_IMMEDIATE_RESPAWN, true, server);
+ final var timer = TimerAccess.getTimerFromOverworld(server);
+
final var worldBorder = server.getOverworld().getWorldBorder();
worldBorder.setSize(Molehunt.CONFIG.getInitialWorldSize());
if (Molehunt.CONFIG.getBorderShrinkingStartingTimeOffset() < Molehunt.CONFIG.getGameDuration()) {
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- final var worldBorder = server.getOverworld().getWorldBorder();
- worldBorder.interpolateSize(
- Molehunt.CONFIG.getInitialWorldSize(),
- Molehunt.CONFIG.getFinalWorldSize(),
- (long) (Molehunt.CONFIG.getGameDuration() - Molehunt.CONFIG.getBorderShrinkingStartingTimeOffset()) * 60 * 1000,
- 0L
- );
- }
- }, (long) Molehunt.CONFIG.getBorderShrinkingStartingTimeOffset() * 60 * 1000);
+ timer.dds_runTask(new TickTask(() -> worldBorder.interpolateSize(
+ Molehunt.CONFIG.getInitialWorldSize(),
+ Molehunt.CONFIG.getFinalWorldSize(),
+ (long) (Molehunt.CONFIG.getGameDuration() - Molehunt.CONFIG.getBorderShrinkingStartingTimeOffset()) * 60 * 1000,
+ 0L
+ ), (long) Molehunt.CONFIG.getBorderShrinkingStartingTimeOffset() * 60 * 1000));
}
final var title = new TitleS2CPacket(Text.translatable("molehunt.game.start.suspense"));
@@ -91,45 +84,38 @@ public class Game { server.setDefaultGameMode(GameMode.SPECTATOR);
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- playerManager.getPlayerList().forEach(p -> {
- p.networkHandler.sendPacket(timing);
- if (moles.contains(p.getUuid())) {
- p.networkHandler.sendPacket(new TitleS2CPacket(Text.translatable("molehunt.game.start.mole.title")));
- p.networkHandler.sendPacket(new SubtitleS2CPacket(Text.translatable("molehunt.game.start.mole.subtitle")));
- } else {
- p.networkHandler.sendPacket(new TitleS2CPacket(Text.translatable("molehunt.game.start.survivor.title")));
- p.networkHandler.sendPacket(new SubtitleS2CPacket(Text.translatable("molehunt.game.start.survivor.subtitle")));
+ timer.dds_runTask(new TickTask(() -> {
+ playerManager.getPlayerList().forEach(p -> {
+ p.networkHandler.sendPacket(timing);
+ if (moles.contains(p.getUuid())) {
+ p.networkHandler.sendPacket(new TitleS2CPacket(Text.translatable("molehunt.game.start.mole.title")));
+ p.networkHandler.sendPacket(new SubtitleS2CPacket(Text.translatable("molehunt.game.start.mole.subtitle")));
+ } else {
+ p.networkHandler.sendPacket(new TitleS2CPacket(Text.translatable("molehunt.game.start.survivor.title")));
+ p.networkHandler.sendPacket(new SubtitleS2CPacket(Text.translatable("molehunt.game.start.survivor.subtitle")));
+ }
+ // reset health and food level
+ p.setHealth(p.getMaxHealth());
+ p.getHungerManager().setFoodLevel(20);
+ p.getHungerManager().setSaturationLevel(5.0f);
+ });
+ // reset gamerules after the start
+ gamerules.setValue(GameRules.DO_IMMEDIATE_RESPAWN, false, server);
+ // reset time and weather
+ server.getOverworld().setTimeOfDay(0);
+ server.getOverworld().resetWeather();
+ changeState(true);
+ timer.dds_runTask(new TickTask(() -> {
+ remaining--;
+ playerManager.getPlayerList().forEach(player -> {
+ if (Molehunt.timerVisibility.getOrDefault(player.getUuid(), true)) {
+ player.networkHandler.sendPacket(new OverlayMessageS2CPacket(Text.of(getRemainingText())));
}
- // reset health and food level
- p.setHealth(p.getMaxHealth());
- p.getHungerManager().setFoodLevel(20);
- p.getHungerManager().setSaturationLevel(5.0f);
});
- // reset gamerules after the start
- gamerules.setValue(GameRules.DO_IMMEDIATE_RESPAWN, false, server);
- // reset time and weather
- server.getOverworld().setTimeOfDay(0);
- server.getOverworld().resetWeather();
- changeState(true);
-
- timer.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- remaining--;
- playerManager.getPlayerList().forEach(player -> {
- if (Molehunt.timerVisibility.getOrDefault(player.getUuid(), true)) {
- player.networkHandler.sendPacket(new OverlayMessageS2CPacket(Text.of(getRemainingText())));
- }
- });
- playerManager.sendToAll(timing);
- if (remaining == 0) end();
- }
- }, 5*1000, 1000);
- }
- }, 4*1000);
+ playerManager.sendToAll(timing);
+ if (remaining == 0) end();
+ }, 5 * 1000, 1000));
+ }, 4 * 1000));
}
public void stop() {
@@ -138,8 +124,8 @@ public class Game { }
public void end() {
- timer.cancel();
- timer = new Timer();
+ final var timer = TimerAccess.getTimerFromOverworld(server);
+ timer.dds_cancel();
final var worldBorder = server.getOverworld().getWorldBorder();
// Stops the border shrinking.
@@ -153,21 +139,18 @@ public class Game { p.networkHandler.sendPacket(winnerSuspense);
p.changeGameMode(GameMode.CREATIVE);
});
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- TitleS2CPacket winner;
- if (wonByMoles()) {
- winner = new TitleS2CPacket(Text.translatable("molehunt.game.end.winners.moles.title"));
- } else {
- winner = new TitleS2CPacket(Text.translatable("molehunt.game.end.winners.survivors.title"));
- }
- pm.sendToAll(new SubtitleS2CPacket(Text.translatable("molehunt.game.end.winners.subtitle", getMolesAsString())));
- pm.sendToAll(winner);
- pm.sendToAll(timing);
- moles.clear();
+ timer.dds_runTask(new TickTask(() -> {
+ TitleS2CPacket winner;
+ if (wonByMoles()) {
+ winner = new TitleS2CPacket(Text.translatable("molehunt.game.end.winners.moles.title"));
+ } else {
+ winner = new TitleS2CPacket(Text.translatable("molehunt.game.end.winners.survivors.title"));
}
- }, 4*1000);
+ pm.sendToAll(new SubtitleS2CPacket(Text.translatable("molehunt.game.end.winners.subtitle", getMolesAsString())));
+ pm.sendToAll(winner);
+ pm.sendToAll(timing);
+ moles.clear();
+ }, 4 * 1000));
}
public Text getRemainingText() {
diff --git a/src/main/java/world/anhgelus/molehunt/mixin/WorldTimerAccess.java b/src/main/java/world/anhgelus/molehunt/mixin/WorldTimerAccess.java new file mode 100644 index 0000000..cd8a1b4 --- /dev/null +++ b/src/main/java/world/anhgelus/molehunt/mixin/WorldTimerAccess.java @@ -0,0 +1,45 @@ +package world.anhgelus.molehunt.mixin; + +import net.minecraft.server.world.ServerWorld; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import world.anhgelus.molehunt.timer.TimerAccess; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.BooleanSupplier; + +@Mixin(ServerWorld.class) +public class WorldTimerAccess implements TimerAccess { + @Unique + private final List<TickTask> tasks = new ArrayList<>(); + + @Unique + private final List<TimerAccess.TickTask> tasksToAdd = new ArrayList<>(); + + @Inject(method = "tick", at = @At("TAIL")) + private void onTick(BooleanSupplier shouldKeepTicking, CallbackInfo ci) { + tasks.stream().filter(TickTask::isRunning).forEach(TickTask::tick); + tasks.addAll(tasksToAdd); + tasksToAdd.clear(); + } + + @Override + public void dds_runTask(TimerAccess.TickTask task) { + tasksToAdd.add(task); + } + + @Override + public void dds_cancel() { + tasks.stream().filter(TickTask::isRunning).forEach(TickTask::cancel); + tasks.clear(); + } + + @Override + public List<TickTask> dds_getTasks() { + return tasks.stream().filter(TickTask::isRunning).toList(); + } +}
\ No newline at end of file diff --git a/src/main/java/world/anhgelus/molehunt/timer/TickTask.java b/src/main/java/world/anhgelus/molehunt/timer/TickTask.java new file mode 100644 index 0000000..8c74b89 --- /dev/null +++ b/src/main/java/world/anhgelus/molehunt/timer/TickTask.java @@ -0,0 +1,72 @@ +package world.anhgelus.molehunt.timer; + +/** + * Represents a complete task called each tick + */ +public class TickTask implements TimerAccess.TickTask { + public final long ticksDelay; + public final long ticksRepeat; + public final boolean repeating; + public final TimerAccess.Task task; + private boolean cancelled = false; + private long currentTicking; + + /** + * Create a new repeating TickTask + * + * @param task Task to run after the delay or the repeat time + * @param ticksDelay Delay before the first task's run + * @param ticksRepeat Repeat each tick (if the repeat is 0, it will repeat each tick, if it is below 0, it will not repeat) + * @throws IllegalArgumentException if ticksDelay is below 0 + */ + public TickTask(TimerAccess.Task task, long ticksDelay, long ticksRepeat) { + if (ticksDelay < 0) throw new IllegalArgumentException("Ticks delay must be non-negative"); + this.ticksDelay = ticksDelay; + this.ticksRepeat = ticksRepeat; + this.task = task; + repeating = ticksRepeat >= 0; + currentTicking = ticksDelay; + } + + /** + * Create a new delayed TickTask + * + * @param task Task to run after the delay or the repeat time + * @param ticksDelay Delay before the first task's run + * @throws IllegalArgumentException if ticksDelay or if ticksRepeat is below 0 + */ + public TickTask(TimerAccess.Task task, long ticksDelay) { + if (ticksDelay < 0) throw new IllegalArgumentException("Ticks delay must be non-negative"); + this.ticksDelay = ticksDelay; + this.ticksRepeat = -1; + this.task = task; + repeating = false; + currentTicking = ticksDelay; + } + + public void tick() { + if (--currentTicking > 0) return; + task.run(); + if (repeating) { + currentTicking = ticksRepeat; + } else { + cancel(); + } + } + + public long cancel() { + if (cancelled) throw new IllegalStateException("Task already cancelled"); + cancelled = true; + return currentTicking; + } + + public boolean isRunning() { + return !cancelled; + } + + @Override + public long getTickingBeforeRun() { + if (cancelled) return -1; + return currentTicking; + } +}
\ No newline at end of file diff --git a/src/main/java/world/anhgelus/molehunt/timer/TimerAccess.java b/src/main/java/world/anhgelus/molehunt/timer/TimerAccess.java new file mode 100644 index 0000000..2e02b12 --- /dev/null +++ b/src/main/java/world/anhgelus/molehunt/timer/TimerAccess.java @@ -0,0 +1,65 @@ +package world.anhgelus.molehunt.timer; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.World; + +import java.util.List; + +public interface TimerAccess { + /** + * Get the timer linked to the overworld + * + * @param server Current server + * @return TimerAccess linked to the overworld + */ + static TimerAccess getTimerFromOverworld(MinecraftServer server) { + final var timer = (TimerAccess) server.getWorld(World.OVERWORLD); + if (timer == null) + throw new NullPointerException("Impossible to get TimerAccess from the overworld (it is null)"); + return timer; + } + + /** + * Run a task (called each tick ticked) + * + * @param task Task to run + */ + void dds_runTask(TimerAccess.TickTask task); + + void dds_cancel(); + + /** + * @return All non-cancelled tasks + */ + List<TickTask> dds_getTasks(); + + interface TickTask { + /** + * Tick the task + */ + void tick(); + + /** + * Cancel the task + * + * @return the remaining ticks before the run of the Task + * @throws IllegalStateException if the task is already cancelled + */ + long cancel(); + + boolean isRunning(); + + /** + * @return the number of ticks before run of the task (if the task is cancelled, returns -1) + */ + long getTickingBeforeRun(); + } + + /** + * Represents a task to run after ticking + */ + @FunctionalInterface + interface Task { + void run(); + } +} diff --git a/src/main/resources/molehunt.mixins.json b/src/main/resources/molehunt.mixins.json index e9ec77e..2dc5bb9 100644 --- a/src/main/resources/molehunt.mixins.json +++ b/src/main/resources/molehunt.mixins.json @@ -6,7 +6,8 @@ "mixins": [
"NoJoinLeaveMessage",
"NoMsgCommand",
- "NoPortals"
+ "NoPortals",
+ "WorldTimerAccess"
],
"injectors": {
"defaultRequire": 1
|
