Skip to content

fix(server): play-mode busy-state, auth-token rotation 401, dropped rapid clicks, and background responsiveness#171

Merged
Daliys merged 10 commits into
developmentfrom
daliys/issue-71-scene-dependencies-iterator
Jul 24, 2026
Merged

fix(server): play-mode busy-state, auth-token rotation 401, dropped rapid clicks, and background responsiveness#171
Daliys merged 10 commits into
developmentfrom
daliys/issue-71-scene-dependencies-iterator

Conversation

@Daliys

@Daliys Daliys commented Jul 23, 2026

Copy link
Copy Markdown
Member

Summary

Started as an auth-token / domain-reload / app-nap fix. While hardening the
real-time integration test suite that exercises this server, the suite surfaced
three additional server bugs that this PR now also fixes. All three are
verified against a live editor (Unity 6000.4.3f1) and covered by tests.

Fixes in this PR

1. Auth token resolution & background responsiveness (original scope)

  • Resolve the auth token from Library/NexusUnityAuthToken.txt with a sane
    fallback, and enable Application.runInBackground + AppNapBypass in
    Init() so the server stays responsive when Unity is backgrounded.

2. isPlayModeTransition never cleared during play mode

IsPlayModeTransitionCached used EditorApplication.isPlayingOrWillChange- Playmode, which stays true for the entire play session, not just the
transition. Consequences while the game was running:

  • get_server_status reported busyReason: play_mode_transition the whole time
  • acceptsWriteCommands was false for all of play mode
  • initialize() threw "editor is busy" until you left play mode

Fixed by treating a transition as "in progress" only while
isPlayingOrWillChangePlaymode != isPlaying. Verified in play mode:
busyReason: idle, writes accepted, initialize() succeeds. Play-mode entry
detection dropped ~90s → ~4.5s.

3. Auth token rotates on domain reload → permanent HTTP 401

The Python bridge (nexus_bridge/_transport.py) resolved the token once at
import
. Unity rewrites the token file on every domain reload, so after the
first script compile every authenticated method returned 401 for the rest of
the session
— while get_server_status kept returning 200 (it is
auth-exempt), which reads to a client as "the editor never came back". The
401 was even reported as "Unity Server unreachable", misdirecting debugging.

Fixed: re-read the token file and retry once on 401 (first attempt keeps the
import-time token), and report a 401 as an auth failure, not as unreachable.
apply_code_change on multiple files went from a 90s timeout → ~8.6s.
New test_transport cases pin the retry and the error message.

4. Rapid clicks silently dropped

simulate_mouse / click_object_in_game defer the button release ~100ms so
the press is held for a frame. A second click issued inside that window queued
its press while the button was still down — no press edge, so it was silently
lost with no error
. Fixed by flushing any pending release before starting a
new click. Reproduced: at 0ms gap the second click vanished; at ≥150ms both
landed.

Testing

  • Package unit tests (Editor/tests/) pass, including the new 401-retry cases.
  • The pre-push live smoke against the running server passes.
  • These fixes were found and verified by a rebuilt real-time integration suite
    (11 suites / 345 named checks) that lives in the consuming project repo. That
    suite is not part of this package PR; it is tracked separately. Highlights
    of what it exposed and now guards against are the three server bugs above.

Closes #71

Daliys and others added 3 commits July 24, 2026 10:03
IsPlayModeTransitionCached used EditorApplication.isPlayingOrWillChange-
Playmode, which stays true for the entire play session rather than just
the transition. As a result get_server_status reported busyReason=
play_mode_transition for the whole session, acceptsWriteCommands stayed
false, and initialize() threw "editor is busy" the entire time the game
ran — write commands and readiness checks were unusable in play mode.

A transition is in progress only while isPlayingOrWillChangePlaymode and
isPlaying disagree (entering: will-change but not yet playing; exiting:
still playing but no longer will-change). Compare the two flags instead.

Verified in play mode: busyReason=idle, acceptsWriteCommands=true,
initialize() succeeds. Play-mode entry detection dropped 90s -> ~4.5s.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
simulate_mouse / click_object_in_game defer the button release ~100ms so
the press is observably held for at least one frame. A second click issued
inside that window queued its press while the button was still down, so it
produced no press edge and was silently lost — no error, nothing in logs.

Track the pending release and flush it before starting a new click, so
back-to-back clicks each register as a distinct press.

Reproduced: at 0ms gap the second click vanished; at >=150ms both landed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
_transport resolved AUTH_TOKEN once at import. Unity rewrites
Library/NexusUnityAuthToken.txt on every domain reload, so the cached
token goes stale the moment a script compiles and every authenticated
method returns HTTP 401 for the rest of the session — while
get_server_status keeps returning 200 (it is auth-exempt), which reads to
a caller as "the editor never came back". The 401 was also reported as
"Unity Server unreachable", sending debugging the wrong way.

Re-read the token file and retry once on 401 (the first attempt keeps the
import-time token), and report a 401 as an auth failure rather than as
unreachable. Multi-file apply_code_change went from a 90s timeout to ~8.6s.

Adds test_transport coverage for the 401 retry and the auth-failure message.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Daliys Daliys changed the title fix(server): resolve domain reload hanging, auth token resolution, and background app nap fix(server): play-mode busy-state, auth-token rotation 401, dropped rapid clicks, and background responsiveness Jul 24, 2026
…nction)

No behavior change — verified by the full integration suite (355 checks) and
the package unit tests (38).

- MCPServer.cs (386L): split identity/version + auth-token methods into
  MCPServer.Identity.cs, and the server lifecycle (Start/Stop/Cleanup) into
  MCPServer.Networking.cs. Init and Start broken into focused helpers.
- MCPServer.Networking.cs (312L, one function miscounted at 296): split HTTP/
  WebSocket handling into MCPServer.Http.cs; broke IsAnotherMcpInstanceRunning
  and HandleHttpRequest into helpers. Moved the loopback URL to its own line
  (LoopbackUrl) so no brace ever shares a line with a "http://" literal — the
  gate strips comments before strings and mistook the "//" in the URL for a
  line comment, dropping trailing braces and inflating the function's length.
- MCPServerMethods.Snapshot.cs: extracted helpers from SerializeSceneNode and
  GetSceneDependencies.
- nexus_unity_bridge.py: extracted _handle_rpc_request / _handle_tool_call /
  _run_cli_mode from main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@Daliys
Daliys merged commit be06e3b into development Jul 24, 2026
6 checks passed
@Daliys
Daliys deleted the daliys/issue-71-scene-dependencies-iterator branch July 24, 2026 08:40
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