From 4d0ed2a6dc2b1a083b1e8b2cbbe2b710e57f64ec Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 27 Apr 2026 07:48:06 -0500 Subject: [PATCH] fix null pointer on dedicated server shutdown --- .../core/render/RenderThreadTaskHandler.java | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderThreadTaskHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderThreadTaskHandler.java index 7d0bd380c..f1f404d9f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderThreadTaskHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderThreadTaskHandler.java @@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.objects.RollingAverage; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import org.jetbrains.annotations.Nullable; @@ -49,6 +50,7 @@ public class RenderThreadTaskHandler private long nanoSinceTasksRun = System.nanoTime(); + private final boolean running; @@ -57,7 +59,22 @@ public class RenderThreadTaskHandler //=============// //region - private RenderThreadTaskHandler() { TIMER.scheduleAtFixedRate(TimerUtil.createTimerTask(this::manualCleanupTick), MS_BETWEEN_CLEANUP_TICKS, MS_BETWEEN_CLEANUP_TICKS); } + private RenderThreadTaskHandler() + { + // we only want to run this when the client is available + IMinecraftSharedWrapper mcShared = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); + if (!mcShared.isDedicatedServer()) + { + LOGGER.debug("Starting ["+RenderThreadTaskHandler.class.getSimpleName()+"]..."); + this.running = true; + TIMER.scheduleAtFixedRate(TimerUtil.createTimerTask(this::manualCleanupTick), MS_BETWEEN_CLEANUP_TICKS, MS_BETWEEN_CLEANUP_TICKS); + } + else + { + this.running = false; + LOGGER.debug("Skipping ["+RenderThreadTaskHandler.class.getSimpleName()+"] startup due to running on a dedicated server."); + } + } //endregion @@ -70,6 +87,13 @@ public class RenderThreadTaskHandler public void queueRunningOnRenderThread(String name, Runnable renderCall) { + // don't queuing tasks if they'll never be run + if (!this.running) + { + return; + } + + // don't get the stacktrace on release to reduce GC pressure StackTraceElement[] stackTrace = null; if (ModInfo.IS_DEV_BUILD) @@ -179,8 +203,19 @@ public class RenderThreadTaskHandler // this means we could have GL jobs building up. // Run the queued tasks on MC's executor (hopefully this should always run, // even if DH's render code isn't being hit). - IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - MC.executeOnRenderThread(() -> this.runRenderThreadTasks(500 * 1_000_000L)); + IMinecraftClientWrapper mcClient = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + if (mcClient != null) + { + mcClient.executeOnRenderThread(() -> this.runRenderThreadTasks(500 * 1_000_000L)); + } + else + { + // shouldn't happen, but just in case + + // somehow the timer started when there wasn't a client wrapper + // this probably means the timer was started on a dedicated server + RATE_LIMITED_LOGGER.warn("["+RenderThreadTaskHandler.class.getSimpleName()+"] timer started when ["+IMinecraftClientWrapper.class.getSimpleName()+"] is null. This shouldn't happen but can likely be ignored."); + } } //endregion @@ -190,7 +225,7 @@ public class RenderThreadTaskHandler //===========// // debugging // //===========// - ///region + //region /** * if tasks are currently queued the debug @@ -246,7 +281,7 @@ public class RenderThreadTaskHandler }); } - ///endregion + //endregion