From 19e6d25582191cf0a610b9267d8817e4710cbf50 Mon Sep 17 00:00:00 2001 From: Siilent-Sea <72907147+Siilent-Sea@users.noreply.github.com> Date: Sat, 31 Oct 2026 17:09:50 +0800 Subject: [PATCH 1/3] fix: remove dead client setup reflection call The mod constructor reflected on RollingGateClient.onClientSetup(ModContainer), a method that no longer exists. Every client start logged a NoSuchMethodException stack trace, and the call was unnecessary because RollingGateClient is itself a client-only @Mod whose constructor receives the event bus and mod container. --- src/main/java/dev/anvilcraft/rg/RollingGate.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/main/java/dev/anvilcraft/rg/RollingGate.java b/src/main/java/dev/anvilcraft/rg/RollingGate.java index eb3e08f..5661525 100644 --- a/src/main/java/dev/anvilcraft/rg/RollingGate.java +++ b/src/main/java/dev/anvilcraft/rg/RollingGate.java @@ -9,16 +9,12 @@ import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.ModContainer; import net.neoforged.fml.common.Mod; -import net.neoforged.fml.loading.FMLEnvironment; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.event.RegisterCommandsEvent; import net.neoforged.neoforge.event.server.ServerStartingEvent; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - @Mod(RollingGate.MODID) public class RollingGate { public static final String MODID = "rolling_gate"; @@ -33,15 +29,6 @@ public RollingGate(@NotNull IEventBus modEventBus, @SuppressWarnings("unused") @ NeoForge.EVENT_BUS.addListener(RollingGateNetwork::onPlayerLoggedOut); NeoForge.EVENT_BUS.addListener(RollingGateNetwork::onServerStopped); SERVER_RULE_MANAGER.compileContent(); - if (FMLEnvironment.dist.isClient()) { - Class clientClass = RollingGate.class.getClassLoader().loadClass("dev.anvilcraft.rg.client.RollingGateClient"); - try { - Method setup = clientClass.getMethod("onClientSetup", ModContainer.class); - setup.invoke(null, modContainer); - } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - RollingGate.LOGGER.error(e.getMessage(), e); - } - } } @SubscribeEvent From 3b21111709250104f81c9152c1440049f922c2e4 Mon Sep 17 00:00:00 2001 From: Siilent-Sea <72907147+Siilent-Sea@users.noreply.github.com> Date: Sat, 31 Oct 2026 17:09:50 +0800 Subject: [PATCH 2/3] fix(client): apply the window resizable rule on the client thread Config loading can post the rule change event on a mod-loading worker thread. GLFW/AppKit window calls must happen on Minecraft's client thread; on macOS the worker-thread call aborts the client with a main-thread-check trap as soon as windowResizable differs while the window is hidden. Dispatch the attribute update through Minecraft#execute so the rule is still applied to the current window, but from the render thread. --- .../client/listener/RGRuleChangeEventListener.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/dev/anvilcraft/rg/event/listener/client/listener/RGRuleChangeEventListener.java b/src/main/java/dev/anvilcraft/rg/event/listener/client/listener/RGRuleChangeEventListener.java index 24b3e4b..7233098 100644 --- a/src/main/java/dev/anvilcraft/rg/event/listener/client/listener/RGRuleChangeEventListener.java +++ b/src/main/java/dev/anvilcraft/rg/event/listener/client/listener/RGRuleChangeEventListener.java @@ -21,10 +21,11 @@ public static void onRuleChange(@NotNull RGRuleChangeEvent.Client event) { @SubscribeEvent public static void onWindowResizableChange(@NotNull RGRuleChangeEvent.Client event) { if (!"windowResizable".equals(event.getRule().name())) return; - if (event.getNewValue()) { - GLFW.glfwSetWindowAttrib(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_RESIZABLE, GLFW.GLFW_TRUE); - } else { - GLFW.glfwSetWindowAttrib(Minecraft.getInstance().getWindow().getWindow(), GLFW.GLFW_RESIZABLE, GLFW.GLFW_FALSE); - } + Minecraft minecraft = Minecraft.getInstance(); + boolean resizable = event.getNewValue(); + // Config loading can post this event on a mod-loading worker. GLFW window + // operations must run on Minecraft's client thread, particularly on macOS. + minecraft.execute(() -> GLFW.glfwSetWindowAttrib(minecraft.getWindow().getWindow(), + GLFW.GLFW_RESIZABLE, resizable ? GLFW.GLFW_TRUE : GLFW.GLFW_FALSE)); } } From 4e4e357e9e3a22faf5f0ff13f20bf52d41a6a30f Mon Sep 17 00:00:00 2001 From: Siilent-Sea <72907147+Siilent-Sea@users.noreply.github.com> Date: Sat, 31 Oct 2026 17:09:50 +0800 Subject: [PATCH 3/3] fix(client): keep integrated server rules until the server stops Client and integrated server share the static rule fields. Resetting them in ClientPlayerNetworkEvent.LoggingOut ran before IntegratedServer stopped and saved, so a disconnect could persist rules as disabled and fake player residents were written without their resident rules. Skip the reset for memory connections and for a non-dedicated current server, then reset once ServerStoppedEvent arrives, on the client thread and only when no new connection has taken over the shared state. --- .../rg/client/RollingGateClient.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/java/dev/anvilcraft/rg/client/RollingGateClient.java b/src/main/java/dev/anvilcraft/rg/client/RollingGateClient.java index 392c8f6..19aacb0 100644 --- a/src/main/java/dev/anvilcraft/rg/client/RollingGateClient.java +++ b/src/main/java/dev/anvilcraft/rg/client/RollingGateClient.java @@ -19,6 +19,8 @@ import net.neoforged.neoforge.common.ModConfigSpec; import net.neoforged.neoforge.common.NeoForge; import net.neoforged.neoforge.network.PacketDistributor; +import net.neoforged.neoforge.event.server.ServerStoppedEvent; +import net.neoforged.neoforge.server.ServerLifecycleHooks; import org.jetbrains.annotations.NotNull; import java.util.Map; @@ -41,6 +43,7 @@ public RollingGateClient(@NotNull IEventBus eventBus, @NotNull ModContainer modC NeoForge.EVENT_BUS.addListener(RollingGateClient::onLoaded); NeoForge.EVENT_BUS.addListener(RollingGateClient::onLoggingIn); NeoForge.EVENT_BUS.addListener(RollingGateClient::onLoggingOut); + NeoForge.EVENT_BUS.addListener(RollingGateClient::onIntegratedServerStopped); } @SubscribeEvent @@ -57,9 +60,26 @@ public static void onLoggingIn(final @NotNull ClientPlayerNetworkEvent.LoggingIn } public static void onLoggingOut(final @NotNull ClientPlayerNetworkEvent.LoggingOut event) { + var connection = event.getConnection(); + var server = ServerLifecycleHooks.getCurrentServer(); + boolean integrated = (connection != null && connection.isMemoryConnection()) + || (server != null && !server.isDedicatedServer()); + // Server rules are static fields shared with the integrated server. It + // still needs them when saving during its later shutdown event. + if (integrated) return; RollingGateClient.CLIENT_SERVER_RULE_MANAGER.resetRulesToDefault(); } + public static void onIntegratedServerStopped(final @NotNull ServerStoppedEvent event) { + if (event.getServer().isDedicatedServer()) return; + net.minecraft.client.Minecraft.getInstance().execute(() -> { + // Do not overwrite rules already applied by a newly connected server. + if (net.minecraft.client.Minecraft.getInstance().getConnection() == null) { + CLIENT_SERVER_RULE_MANAGER.resetRulesToDefault(); + } + }); + } + @SubscribeEvent public void onLoad(final @NotNull RGValidatorNotPassedEvent event) { RollingGateClient.CLIENT_RULE_MANAGER.reInit();