Skip to content

fix: keep refresh honest while play mode defers compilation - #14

Merged
niqibiao merged 1 commit into
mainfrom
fix/refresh-playmode-reliability
Aug 15, 2026
Merged

niqibiao merged 1 commit into
mainfrom
fix/refresh-playmode-reliability

Conversation

@niqibiao

Copy link
Copy Markdown
Owner

Summary

Play mode defers script compilation under the editor's Script Changes While Playing preference, and the refresh phase machine had no defense against that: two timeout paths could report a fake ready, exiting play mode re-ran service init which forced every active phase to ready while the deferred compile was still pending, and the Compiling phase's only exit was a one-shot delayCall that was dropped whenever the editor was still busy.

  • Track a main-thread heartbeat (Interlocked ticks, pumped by editor update / a runtime MonoBehaviour) and cache isUpdating / isPlaying / the Script Changes While Playing preference on the main thread.
  • Report four additive /health fields: isUpdating, isPlaying, mainThreadHeartbeatAgeMs, scriptChangesWhilePlaying. Protocol version is unchanged; old clients are unaffected. The service only reports the preference — policy stays client-side.
  • Suspend the two refresh timeout exits while play mode can defer the requested compile, sliding their baselines so the timers restart cleanly the moment edit mode returns.
  • Exit the Compiling phase on the observed idle level (with a short quiet window so a ready blip cannot precede an imminent assembly reload) instead of the one-shot delayCall.
  • Narrow service-init reconciliation to a persisted Reloading phase only. Init reruns on every play mode transition, and forcing all active phases to ready was the primary fake-ready path.
  • Bump the package to 2.1.0.

Test plan

Verified live against a Unity 2022.3 editor across all three preference values: deferred-compile hold surfaces honestly and completes once play mode exits (no fake ready while isCompiling is true), stop-and-recompile and recompile-and-continue flows reach ready normally, a deliberate compile error terminates the cycle with compileFailed instead of hanging in Compiling, and the Editor.log build lines agree with every reported outcome.

🤖 Generated with Claude Code

- Track a main-thread heartbeat and cache isUpdating/isPlaying plus the
  Script Changes While Playing preference; report them from /health.
- Suspend the two refresh timeout exits while play mode can defer the
  requested compile, sliding their baselines so the timers restart the
  moment edit mode returns.
- Exit the Compiling phase on the observed idle level instead of a
  one-shot delayCall continuation that was dropped when the editor was
  still busy, leaving the phase stuck.
- Narrow service-init reconciliation to a persisted Reloading phase.
  Init reruns on every play mode transition, and forcing every active
  phase to ready reported ready while a deferred compile was pending.
- Bump the package to 2.1.0 (additive health fields, same protocol).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@cursor

cursor Bot commented Aug 15, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@niqibiao
niqibiao merged commit 6378e4a into main Aug 15, 2026
1 check passed
@niqibiao
niqibiao deleted the fix/refresh-playmode-reliability branch August 15, 2026 13:28

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: efdb2ef903

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

#else
MainThreadRequestRunner.InitializeRuntime();
RecordMainThreadHeartbeat();
EnsureRuntimeHealthHeartbeat();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Create the runtime heartbeat only once

When InitializeForRuntime is called more than once—such as after a shutdown or from initialization code attached to multiple scenes—this call creates another hidden DontDestroyOnLoad object before InitializeInternal can apply its existing s_Initialized guard. Because the heartbeat objects are never found or destroyed, every repeated initialization permanently adds another per-frame Update; create the component only when absent or move its creation behind the initialization guard.

Useful? React with 👍 / 👎.

Comment on lines +1221 to +1226
if (s_CompilingQuietUpdates == 0)
{
s_CompilingQuietSinceEditorTime = EditorApplication.timeSinceStartup;
}

MarkRefreshReady("Script compilation finished without assembly reload");
s_CompilingQuietUpdates++;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset the quiet-window state for each compilation

After one compilation reaches ready, s_CompilingQuietUpdates and s_CompilingQuietSinceEditorTime retain that operation's values. If a subsequent fast compilation starts and finishes through the compilation callbacks between two editor-update samples, the isCompiling branch never resets them, so this increment uses an already-satisfied count and old timestamp and can mark the new operation ready on its first idle update, before a pending reload is observed. Reset both fields when a compilation starts or when a new refresh operation is created.

Useful? React with 👍 / 👎.

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