From 3080102f0659bf5b6d955470fd4bf1397ada33fa Mon Sep 17 00:00:00 2001 From: Michael Harvey Date: Mon, 5 Jan 2026 18:31:56 +0100 Subject: [PATCH] Address maintainer feedback: single level loading system, TimerUtil cleanup, consistent formatting --- .../fabric/mixins/client/MixinMinecraft.java | 36 +++++------- .../neoforge/NeoforgeClientProxy.java | 58 ++++++++++++++++++- 2 files changed, 68 insertions(+), 26 deletions(-) diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinMinecraft.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinMinecraft.java index b51110c5b..acf742ed4 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinMinecraft.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinMinecraft.java @@ -99,34 +99,24 @@ public abstract class MixinMinecraft } #endif - @Inject(at = @At("HEAD"), method = "updateLevelInEngines") - public void updateLevelInEngines(ClientLevel level, CallbackInfo ci) - { - // Skip normal level events when Immersive Portals is active - // IP suppresses these events and we use render-driven loading instead - if (!com.seibel.distanthorizons.common.ImmersivePortalsCompat.isImmersivePortalsActive()) - { - if (this.lastLevel != null && level != this.lastLevel) - { - ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(this.lastLevel)); - } - - if (level != null) - { - ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(level, true)); - } - } - - this.lastLevel = level; - } + // Level load/unload is handled via renderer-driven loading for Immersive Portals + // and via render hooks in MixinLevelRenderer. Avoid duplicating level + // load/unload logic here to keep a single, consistent system. @Inject(at = @At("HEAD"), method = "close()V") public void close(CallbackInfo ci) { SelfUpdater.onClose(); } - @Inject(at = @At("HEAD"), method = "tick") - private void onTick(CallbackInfo ci) + // Use a dedicated timer for cleanup instead of MC tick events + @Unique + private static final java.util.Timer CLIENT_CLEANUP_TIMER = com.seibel.distanthorizons.core.util.TimerUtil.CreateTimer("ClientLevelTickCleanup"); + + @Unique + private static final java.util.TimerTask CLIENT_CLEANUP_TASK = com.seibel.distanthorizons.core.util.TimerUtil.createTimerTask(() -> com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper.tickCleanup()); + + static { - ClientLevelWrapper.tickCleanup(); + // 20 ticks per second (50ms interval) + CLIENT_CLEANUP_TIMER.scheduleAtFixedRate(CLIENT_CLEANUP_TASK, 0, 1000 / 20); } } diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java index 741d7732b..bbdb030c5 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.neoforge; import com.seibel.distanthorizons.common.AbstractModInitializer; +import com.seibel.distanthorizons.common.ImmersivePortalsCompat; import com.seibel.distanthorizons.common.util.ProxyUtil; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; @@ -51,6 +52,7 @@ import net.minecraft.client.Minecraft; import net.neoforged.neoforge.client.event.InputEvent; import net.neoforged.bus.api.SubscribeEvent; import org.lwjgl.opengl.GL32; +import com.seibel.distanthorizons.common.ImmersivePortalsCompat; #if MC_VER < MC_1_20_6 import net.neoforged.neoforge.event.TickEvent; @@ -162,12 +164,18 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy { //LOGGER.trace("break or block attack at blockPos: " + event.getPos()); + LevelAccessor level = event.getLevel(); ChunkAccess chunk = level.getChunk(event.getPos()); - SharedApi.INSTANCE.applyChunkUpdate(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel); + this.onBlockChangeEvent(level, chunk); }); } } } + private void onBlockChangeEvent(LevelAccessor level, ChunkAccess chunk) + { + ILevelWrapper wrappedLevel = ProxyUtil.getLevelWrapper(level); + SharedApi.INSTANCE.chunkBlockChangedEvent(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel); + } @@ -234,6 +242,21 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy // handled via the same mixin as fabric for consistency #endif + if (ClientApi.RENDER_STATE.clientLevelWrapper instanceof ClientLevelWrapper) + { + ClientLevelWrapper wrapper = (ClientLevelWrapper) ClientApi.RENDER_STATE.clientLevelWrapper; + if (ImmersivePortalsCompat.isImmersivePortalsActive()) + { + if (!wrapper.isDhLevelLoaded()) + { + LOGGER.debug("IP detected - On-demand loading level " + wrapper.getDhIdentifier() + " during rendering"); + ClientApi.INSTANCE.clientLevelLoadEvent(wrapper); + } + } + + wrapper.markRendered(); + } + ClientApi.INSTANCE.renderFadeOpaque(); } @@ -248,6 +271,21 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy #else // handled via the same mixin as fabric for consistency #endif + + if (ClientApi.RENDER_STATE.clientLevelWrapper instanceof ClientLevelWrapper) + { + ClientLevelWrapper wrapper = (ClientLevelWrapper) ClientApi.RENDER_STATE.clientLevelWrapper; + if (ImmersivePortalsCompat.isImmersivePortalsActive()) + { + if (!wrapper.isDhLevelLoaded()) + { + LOGGER.debug("IP detected - On-demand loading level " + wrapper.getDhIdentifier() + " during rendering"); + ClientApi.INSTANCE.clientLevelLoadEvent(wrapper); + } + } + + wrapper.markRendered(); + } } @SubscribeEvent @@ -260,8 +298,22 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy #else // handled via the same mixin as fabric for consistency #endif - - + + if (ClientApi.RENDER_STATE.clientLevelWrapper instanceof ClientLevelWrapper) + { + ClientLevelWrapper wrapper = (ClientLevelWrapper) ClientApi.RENDER_STATE.clientLevelWrapper; + if (ImmersivePortalsCompat.isImmersivePortalsActive()) + { + if (!wrapper.isDhLevelLoaded()) + { + LOGGER.debug("IP detected - On-demand loading level " + wrapper.getDhIdentifier() + " during rendering"); + ClientApi.INSTANCE.clientLevelLoadEvent(wrapper); + } + } + + wrapper.markRendered(); + } + try { // should generally only need to be set once per game session