Cooperative pause
When a host pauses, the engine freezes vanilla ticking and locks players in place. It cannot stop your mod’s own round loop. Your tick handler is your code, running on your schedule, and the engine has no way to reach into it.
So unless you gate it, a paused match still counts down. The host pauses to scrub back twenty seconds, and while they are deciding, the round timer runs out and the game ends underneath them.
The gate
Section titled “The gate”static void tick(MinecraftServer server, MyLobby lobby) { if (RewindRecorder.isFrozen(lobby.level())) return; // paused or scrubbing ...}That is the whole fix. isFrozen covers both states that should stop your logic: paused, and mid-scrub.
Or subscribe to the events
Section titled “Or subscribe to the events”If your game has more to do than stop, for example suspending a background task or muting an ambient loop, use the events instead:
GamePausedEvent.EVENT.register((level, recording) -> myScheduler.suspend());GameResumedEvent.EVENT.register((level, recording) -> myScheduler.resume());Pausing is not rewinding
Section titled “Pausing is not rewinding”Worth being precise about, because the two get conflated and the failure mode is subtle.
Gating on isFrozen stops your logic advancing while the world is frozen. It does nothing to make your state travel backwards when the host actually scrubs. A game with the gate and no snapshot slice pauses correctly and then, on commit, keeps playing from a scoreboard that belongs to a moment the world no longer remembers.
Most games want both. The gate stops time; the slice rewinds it.
Checking cost
Section titled “Checking cost”isFrozen is a map lookup against the level, and RewindRecorder.anyFrozen() is a single volatile read if you want a cheaper early-out for a hot path shared across levels. Neither is worth caching or avoiding; call it per tick.