aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/world
diff options
context:
space:
mode:
authorAnhgelus Morhtuuzh <william@herges.fr>2026-03-17 17:57:12 +0100
committerAnhgelus Morhtuuzh <william@herges.fr>2026-03-17 17:57:12 +0100
commitc7e7f82a957b15979ed3f9e52d195850be179f88 (patch)
tree54bd194a35b263bd9e35244e1deaaeaa9d3a52e9 /src/main/java/world
parent724dbeea6672d31658d6563c1fe372a6f4cb4fbb (diff)
refactor(game): use ticks for timer
Diffstat (limited to 'src/main/java/world')
-rw-r--r--src/main/java/world/anhgelus/molehunt/game/Game.java127
-rw-r--r--src/main/java/world/anhgelus/molehunt/mixin/WorldTimerAccess.java45
-rw-r--r--src/main/java/world/anhgelus/molehunt/timer/TickTask.java72
-rw-r--r--src/main/java/world/anhgelus/molehunt/timer/TimerAccess.java65
4 files changed, 237 insertions, 72 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();
+ }
+}