Repository navigation
fix(client): apply window rules on the client thread and preserve integrated-server rules - #8
Open
Siilent-Sea wants to merge 3 commits into
Conversation
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.
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 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three client-side defects, all reproduced on macOS with Minecraft 1.21.1 / NeoForge 21.1.265
while testing a large mod pack. The changes concern client initialization, client window configuration and integrated-server logout; dedicated-server runtime behavior was not tested.
1. Dead
onClientSetupreflection callRollingGate's constructor reflected onRollingGateClient.onClientSetup(ModContainer).That method no longer exists, so every client start logged a
NoSuchMethodExceptionstack trace, and the call was redundant anyway:
RollingGateClientis its ownclient-only
@Modand receivesIEventBus/ModContainerthrough its constructor.The
FMLEnvironment.dist.isClient()block and the reflection imports are removed.2.
windowResizablerule applied from a mod-loading worker threadRGRuleChangeEventListener.onWindowResizableChangecalledGLFW.glfwSetWindowAttribdirectly. Config loading can post the rule-change event on a mod-loading worker, and
GLFW/AppKit window calls must happen on Minecraft's client thread. On macOS the
worker-thread call aborts the process with a main-thread-check trap as soon as
windowResizablediffers from the current window state while the window is hidden(for example during a headless or background start).
The attribute update is now dispatched through
Minecraft#execute, so the rule isstill applied to the current window but from the render thread. The event value is
captured before dispatch.
3. Client rules reset before the integrated server saved
The client and the integrated server share the static rule fields.
ClientPlayerNetworkEvent.LoggingOutfired beforeIntegratedServerstopped, and theimmediate
CLIENT_SERVER_RULE_MANAGER.resetRulesToDefault()made the shutdown save seeserver rules as disabled. With this mod plus a fake-player mod, a resident fake player
was then written without its resident rules and did not come back on re-entry.
onLoggingOutnow skips the reset for a memory connection or a non-dedicated currentserver, and the reset happens once
ServerStoppedEventarrives, on the client thread,and only if no new connection has taken over the shared state in the meantime.
Verification
./gradlew compileJava(NeoForge 21.1.265) — success../gradlew compileJavawith the branch's owngradle.properties(1.21.0 / NeoForge 21.0.167) — success, no property overrides needed.
offline resident fake player:
hidden window applied
windowResizable;resident fake player survives a world exit and re-entry with the same UUID and the
same main-hand item.
spawn and removal, FTB Ultimine, and the window-resizable rule all passed.
Not verified: dedicated servers, multiplayer client/server lifecycle, online-mode resident
skins, and any other client rule beyond
windowResizable.Updated dependency recheck (2026-11-01)
The current upstream
releases/1.21head is stille20058ae0358a8374641edeb480b6b88c1fc27c3, the exact base of this PR. The latest compatible Modrinth artifact is still the alpha1.1.0+build.66; its constructor still refers to the absentonClientSetupmethod and its window listener still calls GLFW directly. There is no newer release-channel 1.21.1/NeoForge build that supersedes these fixes.The published Java sources were compared byte for byte with the local sources used by the tested compatibility JAR. After updating the pack's stable dependencies, the final 90-JAR singleplayer combination passed the window-rule toggle/restore regression. A further check on that final combination saved an offline resident fake player, exited and reopened the new test world, and verified the same UUID and main-hand item. These are focused regression checks, not all-rule or multiplayer acceptance.
The upstream Pull Request Check currently reports
action_requiredwith no jobs/check results. The build/runtime results above are local verification; upstream CI has not passed.