aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnhgelus Morhtuuzh <william@herges.fr>2026-03-17 18:55:42 +0100
committerAnhgelus Morhtuuzh <william@herges.fr>2026-03-17 18:55:42 +0100
commite2039260fc80c732b1b5c5cd433bb6f70dc3ba51 (patch)
tree57c757aa67f3715808a0910c216b18b448c8c5db
parent0473971208eac8116dd0761e595d3a0a83d8e4ee (diff)
refactor(): use ticks for timer
-rw-r--r--src/main/java/world/anhgelus/manhunt/Manhunt.java70
-rw-r--r--src/main/java/world/anhgelus/manhunt/mixins/WorldTimerAccess.java44
-rw-r--r--src/main/java/world/anhgelus/manhunt/timer/TickTask.java72
-rw-r--r--src/main/java/world/anhgelus/manhunt/timer/TimerAccess.java64
-rw-r--r--src/main/resources/fabric.mod.json2
-rw-r--r--src/main/resources/manhunt.mixins.json3
6 files changed, 214 insertions, 41 deletions
diff --git a/src/main/java/world/anhgelus/manhunt/Manhunt.java b/src/main/java/world/anhgelus/manhunt/Manhunt.java
index dc14731..66b8fb4 100644
--- a/src/main/java/world/anhgelus/manhunt/Manhunt.java
+++ b/src/main/java/world/anhgelus/manhunt/Manhunt.java
@@ -8,7 +8,6 @@ import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.entity.event.v1.ServerPlayerEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerEntityEvents;
-import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.command.EntitySelector;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.component.DataComponentTypes;
@@ -34,6 +33,8 @@ import net.minecraft.util.math.GlobalPos;
import net.minecraft.world.GameMode;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import world.anhgelus.manhunt.timer.TickTask;
+import world.anhgelus.manhunt.timer.TimerAccess;
import java.util.*;
@@ -47,15 +48,12 @@ public class Manhunt implements ModInitializer {
private final Set<UUID> speedrunners = new HashSet<>();
private final Map<UUID, UUID> trackedMap = new HashMap<>();
- private final Timer timer = new Timer();
private State state = State.OFF;
@Override
public void onInitialize() {
LOGGER.info("Initializing Manhunt");
MidnightConfig.init(MOD_ID, Config.class);
- LOGGER.info(Config.secondsBeforeRelease);
- LOGGER.info(Config.updateCompassEach);
final LiteralArgumentBuilder<ServerCommandSource> command = literal("manhunt");
@@ -100,11 +98,14 @@ public class Manhunt implements ModInitializer {
final LiteralArgumentBuilder<ServerCommandSource> start = literal("start");
start.requires(CommandManager.requirePermissionLevel(CommandManager.GAMEMASTERS_CHECK));
start.executes(context -> {
+ final var source = context.getSource();
if (state == State.ON) {
- context.getSource().sendFeedback(() -> Text.literal("Cannot start a manhunt if one is already started!"), false);
+ source.sendFeedback(() -> Text.literal("Cannot start a manhunt if one is already started!"), false);
return Command.SINGLE_SUCCESS;
}
- final PlayerManager pm = context.getSource().getServer().getPlayerManager();
+ final var server = source.getServer();
+ final var timer = TimerAccess.getTimerFromOverworld(server);
+ final PlayerManager pm = server.getPlayerManager();
for (final ServerPlayerEntity player : pm.getPlayerList()) {
player.setHealth(player.getMaxHealth());
player.setExperienceLevel(0);
@@ -142,26 +143,24 @@ public class Manhunt implements ModInitializer {
}
LOGGER.info("Added modifiers to {}", hunter.getDisplayName());
}
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- LOGGER.info("Removing modifier to hunters");
- for (final UUID uuid : hunters) {
- final var hunter = pm.getPlayer(uuid);
- assert hunter != null;
- var attr = hunter.getAttributeInstance(EntityAttributes.MOVEMENT_SPEED);
- if (attr != null) {
- attr.removeModifier(Identifier.of("manhunt.speed"));
- }
- attr = hunter.getAttributeInstance(EntityAttributes.GRAVITY);
- if (attr != null) {
- attr.removeModifier(Identifier.of("manhunt.gravity"));
- }
+ timer.dds_runTask(new TickTask(() -> {
+ LOGGER.info("Removing modifier to hunters");
+ for (final UUID uuid : hunters) {
+ final var hunter = pm.getPlayer(uuid);
+ assert hunter != null;
+ var attr = hunter.getAttributeInstance(EntityAttributes.MOVEMENT_SPEED);
+ if (attr != null) {
+ attr.removeModifier(Identifier.of("manhunt.speed"));
+ }
+ attr = hunter.getAttributeInstance(EntityAttributes.GRAVITY);
+ if (attr != null) {
+ attr.removeModifier(Identifier.of("manhunt.gravity"));
}
}
- }, Config.secondsBeforeRelease * 1000L);
+ pm.broadcast(Text.of("Hunters released!"), false);
+ }, Config.secondsBeforeRelease * 20L));
setTimer(pm);
- context.getSource().sendFeedback(() -> Text.literal("Game started!"), true);
+ source.sendFeedback(() -> Text.literal("Game started!"), true);
return Command.SINGLE_SUCCESS;
});
@@ -199,7 +198,7 @@ public class Manhunt implements ModInitializer {
speedrunners.remove(player.getUuid());
}
state = State.OFF;
- timer.cancel();
+ TimerAccess.getTimerFromOverworld(server).dds_cancel();
});
ServerEntityEvents.ENTITY_LOAD.register((entity, world) -> {
@@ -207,25 +206,18 @@ public class Manhunt implements ModInitializer {
EntityType.PIGLIN.spawn(world, entity.getBlockPos(), SpawnReason.MOB_SUMMONED);
entity.discard();
});
-
- ServerLifecycleEvents.SERVER_STOPPED.register((server) -> {
- timer.cancel();
- });
}
private void setTimer(PlayerManager pm) {
- timer.schedule(new TimerTask() {
- @Override
- public void run() {
- for (final UUID uuid : hunters) {
- final ServerPlayerEntity hunter = pm.getPlayer(uuid);
- if (hunter == null) continue;
- final ServerPlayerEntity tracked = pm.getPlayer(trackedMap.get(uuid));
- if (tracked == null) continue;
- updateCompass(hunter, tracked);
- }
+ TimerAccess.getTimerFromOverworld(pm.getServer()).dds_runTask(new TickTask(() -> {
+ for (final UUID uuid : hunters) {
+ final ServerPlayerEntity hunter = pm.getPlayer(uuid);
+ if (hunter == null) continue;
+ final ServerPlayerEntity tracked = pm.getPlayer(trackedMap.get(uuid));
+ if (tracked == null) continue;
+ updateCompass(hunter, tracked);
}
- }, Config.secondsBeforeRelease * 1000L, Config.updateCompassEach * 1000L);
+ }, Config.secondsBeforeRelease * 20L, Config.updateCompassEach * 20L));
}
private void updateCompass(ServerPlayerEntity player, ServerPlayerEntity tracked) {
diff --git a/src/main/java/world/anhgelus/manhunt/mixins/WorldTimerAccess.java b/src/main/java/world/anhgelus/manhunt/mixins/WorldTimerAccess.java
new file mode 100644
index 0000000..6919b3b
--- /dev/null
+++ b/src/main/java/world/anhgelus/manhunt/mixins/WorldTimerAccess.java
@@ -0,0 +1,44 @@
+package world.anhgelus.manhunt.mixins;
+
+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.manhunt.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<TimerAccess.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);
+ }
+
+ @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/manhunt/timer/TickTask.java b/src/main/java/world/anhgelus/manhunt/timer/TickTask.java
new file mode 100644
index 0000000..5449166
--- /dev/null
+++ b/src/main/java/world/anhgelus/manhunt/timer/TickTask.java
@@ -0,0 +1,72 @@
+package world.anhgelus.manhunt.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/manhunt/timer/TimerAccess.java b/src/main/java/world/anhgelus/manhunt/timer/TimerAccess.java
new file mode 100644
index 0000000..f9ed08e
--- /dev/null
+++ b/src/main/java/world/anhgelus/manhunt/timer/TimerAccess.java
@@ -0,0 +1,64 @@
+package world.anhgelus.manhunt.timer;
+
+import net.minecraft.server.MinecraftServer;
+
+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.getOverworld();
+ 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();
+ }
+} \ No newline at end of file
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index d218be5..abbd844 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -14,7 +14,7 @@
},
"license": "AGPL-3.0",
"icon": "assets/manhunt/icon.png",
- "environment": "server",
+ "environment": "*",
"entrypoints": {
"main": [
"world.anhgelus.manhunt.Manhunt"
diff --git a/src/main/resources/manhunt.mixins.json b/src/main/resources/manhunt.mixins.json
index 994072c..6613627 100644
--- a/src/main/resources/manhunt.mixins.json
+++ b/src/main/resources/manhunt.mixins.json
@@ -3,7 +3,8 @@
"package": "world.anhgelus.manhunt.mixins",
"compatibilityLevel": "JAVA_21",
"mixins": [
- "Tracker"
+ "Tracker",
+ "WorldTimerAccess"
],
"injectors": {
"defaultRequire": 1