Skip to content

chore: multi-scene worlds offline adapter#7206

Merged
dalkia merged 9 commits intodevfrom
chore/multi-scene-worlds-offline-adapter
Feb 25, 2026
Merged

chore: multi-scene worlds offline adapter#7206
dalkia merged 9 commits intodevfrom
chore/multi-scene-worlds-offline-adapter

Conversation

@dalkia
Copy link
Collaborator

@dalkia dalkia commented Feb 23, 2026

Pull Request Description

What does this PR change?

  • Makes the GateKeeperSceneRoom listen to the Realm adapter. If its offline, replicate the behaviour
  • Fixes the IsConnected method in GateKeeperSceneRoom. Before this PR, it was return true in a world regardless if it was connected or not; probably sending messages to an unfullfilled conection toa. Livekit room and failling silently

Test Instructions

Test Steps

  1. Try a single player multi-scene world. You should not see peers in other scenes. Chat is still going to connect, and its expected
  2. After going to a single player multi-scene world, go to any other world or to genesis. You should see peers just fine
  3. Try making your world multi player
  4. Re-enter, and it should now be multiplayer

Code Review Reference

Please review our Code Review Standards before submitting.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 23, 2026

badge

Windows and Mac build successful in Unity Cloud! You can find a link to the downloadable artifact below.

Name Link
Commit 4671a12
Logs https://github.com/decentraland/unity-explorer/actions/runs/22393791491
Download Windows https://github.com/decentraland/unity-explorer/suites/58511840658/artifacts/5652647269
Download Windows S3 https://explorer-artifacts.decentraland.org/@dcl/unity-explorer/branch/chore/multi-scene-worlds-offline-adapter/pr-19100-4671a12/Decentraland_windows64.zip
Download Mac https://github.com/decentraland/unity-explorer/suites/58511840658/artifacts/5652493323
Download Mac S3 https://explorer-artifacts.decentraland.org/@dcl/unity-explorer/branch/chore/multi-scene-worlds-offline-adapter/pr-19100-4671a12/Decentraland_macos.zip
Built on 2026-02-25T11:42:10Z

Artifact size check

Target Size (MB / MiB) Cap (MB) Δ MB vs release Δ % vs release Result
windows64 317.8 / 303.1 320 0 -0.06% ✅ OK
macos 408.1 / 389.2 410 0 0.02% ✅ OK

Comparing against: v0.126.0-alpha

Copy link
Contributor

@Ludmilafantaniella Ludmilafantaniella left a comment

Choose a reason for hiding this comment

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

Tested on Windows and Mac ✅

Test scenarios covered:

  • Single player multi-scene world: no peers visible in other scenes ✓
  • Chat doesn't connect on single player mode. Sometimes the other player appears on nearby list but I can't interact in nearby chat with him/her ✓
  • After leaving a single player multi-scene world, peers visible correctly in other worlds and Genesis ✓
  • Toggled world to multiplayer via Creator Hub, re-entered and peers are visible correctly ✓
  • Repeated the single player → multiplayer toggle several times with consistent results ✓

Note: Hit an issue mid-testing where after changing the multiplayer setting in Creator Hub, the world became inaccessible in Explorer. Traced it back to a permissions feature in Creator Hub that wasn't yet migrated to the Explorer build. Once the permissions were removed, the issue didn't reproduce. Not blocking for this PR.

Windows:

7206-mac.mp4

Mac:

7206-windows.mp4

✅Smoke test performed:

  • ✔️ Backpack and wearables in world
  • ✔️ Emotes in world and in backpack
  • ✔️ Teleport with map/coordinates/Jump In
  • ✔️ Chat and multiplayer
  • ✔️ Skybox

@claude
Copy link

claude bot commented Feb 24, 2026

Code Review

  • Read changed files
  • Analyze diff for issues
  • Post inline comments
  • Post summary

Summary

The core bug fix in IsSceneConnected is correct — the old implementation returned true for non-isolated rooms regardless of actual connection state, which is the regression described in the PR. The offline adapter check and state guard fix this properly.

Two issues found that require fixes:


Issue 1 — Orphaned background loop on realm navigation from offline world

Location: ConnectiveRoom.cs:226-229 (new SetNoConnectionRequired method) + GateKeeperSceneRoom.cs:117-121

Problem: SetNoConnectionRequired() sets roomState to Stopped while the background loop (RunAsync) is still running. StopIfNotAsync (used by RoomHub.StopLocalRoomsAsync) only stops the room when state is Running or Starting:

public static UniTask StopIfNotAsync(this IConnectiveRoom room) =>
    room.CurrentState() is IConnectiveRoom.State.Running or IConnectiveRoom.State.Starting
        ? room.StopAsync()
        : UniTask.CompletedTask;

When navigating from an offline world to another realm:

  1. StopLocalRoomsAsync()gateKeeperSceneRoom.StopIfNotAsync() → state is Stoppedskip, old loop still running
  2. StartAsync() called for new realm → state is Stopped → guard passes → creates new CancellationTokenSource (cts2), orphaning cts1 without canceling it
  3. Old loop (cts1) resumes after its 1-second heartbeat delay; IsCommsOffline is now false (new realm)
  4. Old loop proceeds into the connection flow concurrently with the new loop (cts2), both racing to call TryConnectToRoomAsync and modify currentMetaData

Fix: SetNoConnectionRequired() must cancel the current token before setting state to Stopped, or the state should not be set to Stopped when the loop is still running. A minimal fix:

protected void SetNoConnectionRequired()
{
    attemptToConnectState.Set(AttemptToConnectState.NO_CONNECTION_REQUIRED);
    // Don't set roomState to Stopped here — the loop is still running.
    // StopAsync/StopIfNotAsync should stop the loop via CancellationToken.
}

This would require CycleStepAsync to yield/wait (like the original WaitWhile) rather than returning immediately.


Issue 2 — SetNoConnectionRequired() called redundantly on every heartbeat

Location: GateKeeperSceneRoom.cs:117-121

Problem: When the realm is offline, CycleStepAsync calls SetNoConnectionRequired() and returns every heartbeat (every 1 second). SetNoConnectionRequired() unconditionally writes to roomState and attemptToConnectState on every tick, even though the state is already NO_CONNECTION_REQUIRED / Stopped after the first call.

protected override async UniTask CycleStepAsync(CancellationToken token)
{
    if (options.IsCommsOffline)
    {
        SetNoConnectionRequired();  // Called every second, redundant after first call
        return;
    }

Fix: Guard with a state check so the writes only happen on the first offline tick:

if (options.IsCommsOffline)
{
    if (attemptToConnectState.Value() is not AttemptToConnectState.NO_CONNECTION_REQUIRED)
        SetNoConnectionRequired();
    return;
}

Fix this →

View job run

dalkia and others added 3 commits February 25, 2026 07:35
…ateKeeperSceneRoom.cs

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
Signed-off-by: Juan Ignacio Molteni <juanignaciomolteni@gmail.com>
Signed-off-by: Juan Ignacio Molteni <juanignaciomolteni@gmail.com>
@dalkia
Copy link
Collaborator Author

dalkia commented Feb 25, 2026

@claude re-review

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Both previously raised issues have been addressed:

Issue 1 (orphaned loop): Resolved. SetNoConnectionRequired() now only sets attemptToConnectState — it does not touch roomState. When offline, roomState stays at Starting (since TryConnectToRoomAsync never runs), so StopIfNotAsync correctly calls StopAsync() on realm navigation. No loop orphaning.

Issue 2 (redundant writes on every heartbeat): Resolved. The guard if (AttemptToConnectState is not AttemptToConnectState.NO_CONNECTION_REQUIRED) prevents repeated calls to SetNoConnectionRequired() after the first offline tick.

The ScenesAreFixedSingleScene semantic fix is also correct: ScenesAreFixed is true for all world types (single and multi-scene alike), making the old code fail to isolate multi-scene single-player worlds. SingleScene properly distinguishes them.

Copy link

@claude claude bot left a comment

Choose a reason for hiding this comment

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

Both previously raised issues have been addressed.

Issue 1 (orphaned loop): Resolved. SetNoConnectionRequired() now only sets attemptToConnectState — it does not touch roomState. When offline, roomState stays at Starting (since TryConnectToRoomAsync never runs), so StopIfNotAsync correctly calls StopAsync() on realm navigation. No loop orphaning.

Issue 2 (redundant writes on every heartbeat): Resolved. The guard if (AttemptToConnectState is not AttemptToConnectState.NO_CONNECTION_REQUIRED) prevents repeated calls to SetNoConnectionRequired() after the first offline tick.

The ScenesAreFixed to SingleScene semantic fix is also correct: ScenesAreFixed is true for all world types (single and multi-scene alike), making the old code fail to isolate multi-scene single-player worlds. SingleScene properly distinguishes them.

@dalkia dalkia enabled auto-merge (squash) February 25, 2026 11:02
@dalkia dalkia merged commit 6fddded into dev Feb 25, 2026
11 of 12 checks passed
@dalkia dalkia deleted the chore/multi-scene-worlds-offline-adapter branch February 25, 2026 11:42
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.

3 participants