Skip to content

fix(client): apply window rules on the client thread and preserve integrated-server rules - #8

Open
Siilent-Sea wants to merge 3 commits into
Anvil-Dev:releases/1.21from
Siilent-Sea:fix/client-thread-and-integrated-server-rules
Open

Siilent-Sea wants to merge 3 commits into
Anvil-Dev:releases/1.21from
Siilent-Sea:fix/client-thread-and-integrated-server-rules

Conversation

@Siilent-Sea

@Siilent-Sea Siilent-Sea commented Oct 31, 2026 •

Copy link
Copy Markdown

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 onClientSetup reflection call

RollingGate's constructor reflected on RollingGateClient.onClientSetup(ModContainer).
That method no longer exists, so every client start logged a NoSuchMethodException
stack trace, and the call was redundant anyway: RollingGateClient is its own
client-only @Mod and receives IEventBus/ModContainer through its constructor.

The FMLEnvironment.dist.isClient() block and the reflection imports are removed.

2. windowResizable rule applied from a mod-loading worker thread

RGRuleChangeEventListener.onWindowResizableChange called GLFW.glfwSetWindowAttrib
directly. 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
windowResizable differs 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 is
still 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.LoggingOut fired before IntegratedServer stopped, and the
immediate CLIENT_SERVER_RULE_MANAGER.resetRulesToDefault() made the shutdown save see
server 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.

onLoggingOut now skips the reset for a memory connection or a non-dedicated current
server, and the reset happens once ServerStoppedEvent arrives, 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 compileJava with the branch's own gradle.properties
    (1.21.0 / NeoForge 21.0.167) — success, no property overrides needed.
  • Runtime, macOS client, Minecraft 1.21.1, NeoForge 21.1.265, separate isolated test world,
    offline resident fake player:
    • before the fix: client aborted at startup with a GLFW/AppKit main-thread trap when the
      hidden window applied windowResizable;
    • after the fix: the window attribute follows and restores the configured value, the
      resident fake player survives a world exit and re-entry with the same UUID and the
      same main-hand item.
  • Regression in the same pack: backpack/UI, JEI recipe screen, Curios screen, fake player
    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.21 head is still e20058ae0358a8374641edeb480b6b88c1fc27c3, the exact base of this PR. The latest compatible Modrinth artifact is still the alpha 1.1.0+build.66; its constructor still refers to the absent onClientSetup method 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_required with no jobs/check results. The build/runtime results above are local verification; upstream CI has not passed.

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant