From ee9ef9abd869395ca6814a6091db3352f88e42dc Mon Sep 17 00:00:00 2001 From: Anatoly Kopyl Date: Sat, 18 Apr 2026 00:21:07 +0300 Subject: [PATCH] Reset at MSK timezone --- gradle.properties | 2 +- .../client/RespawnBackoffClient.java | 25 +++++++++++++++++-- .../mixin/GameRendererMixin.java | 22 ++++++++++++++++ .../net/respawnbackoff/mixin/GuiMixin.java | 23 ----------------- .../net/respawnbackoff/RespawnBackoffMod.java | 15 ++++++----- .../resources/respawn_backoff.mixins.json | 2 +- 6 files changed, 56 insertions(+), 33 deletions(-) create mode 100644 src/client/java/net/respawnbackoff/mixin/GameRendererMixin.java delete mode 100644 src/client/java/net/respawnbackoff/mixin/GuiMixin.java diff --git a/gradle.properties b/gradle.properties index 26708a9..f43c7a0 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ minecraft_version=1.21.1 loader_version=0.18.5 loom_version=1.15-SNAPSHOT -mod_version=1.0.0 +mod_version=1.0.1 maven_group=net.respawnbackoff fabric_api_version=0.116.10+1.21.1 diff --git a/src/client/java/net/respawnbackoff/client/RespawnBackoffClient.java b/src/client/java/net/respawnbackoff/client/RespawnBackoffClient.java index ec8fada..41bb7fc 100644 --- a/src/client/java/net/respawnbackoff/client/RespawnBackoffClient.java +++ b/src/client/java/net/respawnbackoff/client/RespawnBackoffClient.java @@ -5,6 +5,7 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; import net.minecraft.client.Minecraft; import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.network.chat.Component; import net.respawnbackoff.CooldownSyncPayload; @@ -27,8 +28,28 @@ public class RespawnBackoffClient implements ClientModInitializer { } - /** Invoked from a mixin at the end of {@link net.minecraft.client.gui.Gui#render} (HUD + chat). Pause UI and toasts draw later. */ - public static void renderPenaltyOverlay(GuiGraphics graphics) { + /** + * Invoked from a mixin at the end of {@link net.minecraft.client.renderer.GameRenderer#render} so chat, + * Jade, and other late overlays sit underneath the blackout. Skipped while any screen is open. + */ + public static void renderPenaltyOverlayEndOfFrame() { + if (!overlayActive) { + return; + } + Minecraft client = Minecraft.getInstance(); + if (client.player == null || client.screen != null) { + return; + } + MultiBufferSource.BufferSource bufferSource = client.renderBuffers().bufferSource(); + GuiGraphics graphics = new GuiGraphics(client, bufferSource); + try { + renderPenaltyOverlay(graphics); + } finally { + graphics.flush(); + } + } + + private static void renderPenaltyOverlay(GuiGraphics graphics) { if (!overlayActive) { return; } diff --git a/src/client/java/net/respawnbackoff/mixin/GameRendererMixin.java b/src/client/java/net/respawnbackoff/mixin/GameRendererMixin.java new file mode 100644 index 0000000..5b80bf8 --- /dev/null +++ b/src/client/java/net/respawnbackoff/mixin/GameRendererMixin.java @@ -0,0 +1,22 @@ +package net.respawnbackoff.mixin; + +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.renderer.GameRenderer; +import net.respawnbackoff.client.RespawnBackoffClient; + +/** + * Draw the penalty blackout after the full frame (vanilla HUD, chat, and typical mod overlays like + * Jade). Skip while a {@link net.minecraft.client.gui.screens.Screen} is open so pause menus stay unobstructed. + */ +@Mixin(GameRenderer.class) +public class GameRendererMixin { + @Inject(method = "render", at = @At("RETURN")) + private void respawn_backoff$penaltyOverlayLastInFrame(DeltaTracker deltaTracker, boolean tick, CallbackInfo ci) { + RespawnBackoffClient.renderPenaltyOverlayEndOfFrame(); + } +} diff --git a/src/client/java/net/respawnbackoff/mixin/GuiMixin.java b/src/client/java/net/respawnbackoff/mixin/GuiMixin.java deleted file mode 100644 index 8ecdaab..0000000 --- a/src/client/java/net/respawnbackoff/mixin/GuiMixin.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.respawnbackoff.mixin; - -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -import net.minecraft.client.DeltaTracker; -import net.minecraft.client.gui.Gui; -import net.minecraft.client.gui.GuiGraphics; -import net.respawnbackoff.client.RespawnBackoffClient; - -/** - * Draw the penalty screen after the in-game HUD (including chat). Vanilla then draws the pause - * menu and toasts on top, so we no longer paint over {@link net.minecraft.client.gui.screens.PauseScreen} widgets. - */ -@Mixin(Gui.class) -public class GuiMixin { - @Inject(method = "render", at = @At("RETURN")) - private void respawn_backoff$afterInGameHud(GuiGraphics graphics, DeltaTracker deltaTracker, CallbackInfo ci) { - RespawnBackoffClient.renderPenaltyOverlay(graphics); - } -} diff --git a/src/main/java/net/respawnbackoff/RespawnBackoffMod.java b/src/main/java/net/respawnbackoff/RespawnBackoffMod.java index 5da57e2..abc6f08 100644 --- a/src/main/java/net/respawnbackoff/RespawnBackoffMod.java +++ b/src/main/java/net/respawnbackoff/RespawnBackoffMod.java @@ -1,7 +1,7 @@ package net.respawnbackoff; import java.time.LocalDate; -import java.time.ZoneOffset; +import java.time.ZoneId; import java.util.Collections; import java.util.Optional; import java.util.Set; @@ -25,6 +25,9 @@ import net.minecraft.world.level.GameType; public class RespawnBackoffMod implements ModInitializer { public static final String MOD_ID = "respawn_backoff"; + /** Calendar day for resetting the death-chain exponent: midnight MSK (UTC+3). */ + private static final ZoneId DAILY_RESET_ZONE = ZoneId.of("Europe/Moscow"); + public static final AttachmentType RESPAWN_BACKOFF = AttachmentRegistry.create( ResourceLocation.fromNamespaceAndPath(MOD_ID, "state"), builder -> builder @@ -78,12 +81,12 @@ public class RespawnBackoffMod implements ModInitializer { }); } - private static long currentUtcEpochDay() { - return LocalDate.now(ZoneOffset.UTC).toEpochDay(); + private static long currentResetEpochDay() { + return LocalDate.now(DAILY_RESET_ZONE).toEpochDay(); } static void onPlayerDeath(ServerPlayer player) { - long today = currentUtcEpochDay(); + long today = currentResetEpochDay(); RespawnBackoffData data = player.getAttachedOrElse(RESPAWN_BACKOFF, RespawnBackoffData.DEFAULT); int exponent = data.exponent(); @@ -166,11 +169,11 @@ public class RespawnBackoffMod implements ModInitializer { } /** - * Next death uses the minimum wait (1 minute): exponent 0 and today's UTC day recorded. + * Next death uses the minimum wait (1 minute): exponent 0 and today's calendar day (MSK) recorded. * Does not end an active on-screen countdown; use {@link #skipCooldown} for that. */ public static void resetBackoffChain(ServerPlayer player) { - long today = currentUtcEpochDay(); + long today = currentResetEpochDay(); RespawnBackoffData data = player.getAttachedOrElse(RESPAWN_BACKOFF, RespawnBackoffData.DEFAULT); RespawnBackoffData next = new RespawnBackoffData( 0, diff --git a/src/main/resources/respawn_backoff.mixins.json b/src/main/resources/respawn_backoff.mixins.json index 7f4bd1d..38850ac 100644 --- a/src/main/resources/respawn_backoff.mixins.json +++ b/src/main/resources/respawn_backoff.mixins.json @@ -6,7 +6,7 @@ "ServerPlayerGameModeMixin" ], "client": [ - "GuiMixin" + "GameRendererMixin" ], "injectors": { "defaultRequire": 1