Skip to content

fix: scene's gliding restriction stayed active after the player left that scene - #9518

Merged
popuz merged 4 commits into
devfrom
fix/sdk/cross-scene-disabled-glider
Jul 30, 2026
Merged

fix: scene's gliding restriction stayed active after the player left that scene#9518
popuz merged 4 commits into
devfrom
fix/sdk/cross-scene-disabled-glider

Conversation

@popuz

@popuz popuz commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Pull Request Description

What does this PR change?

Fixes a cross-scene bug where a scene's gliding restriction stayed active after the player left that scene, disabling the glider everywhere until a full client restart.

Symptom (from playtest)

A scene (shroomzoom.dcl.eth) disabled the glider via the InputModifier SDK component. After teleporting to a different World that imposes no restriction, the glider remained disabled. /reload did not help — only a full client restart did.

Root cause

The gliding restriction is stored in the shared InputModifierComponent on the global player entity, and each scene's InputModifierHandlerSystem resets it when the scene stops being current. That reset is gated on lastBusMessageAction == Applied, which is driven by SendBusMessage.

SendBusMessage's "is anything restricted?" check only looked at walk/jog/run/jump/emote/all. That check was written before gliding and double jump existed (introduced in #2437), and when those flags were added (#7312) they were added to ApplyModifiers/ResetModifiers but never added to this check.

Two consequences of that omission:

  1. A scene disabling only gliding (or only double jump) never flipped lastBusMessageAction to Applied, so on scene exit the reset was skipped and the global DisableGliding flag leaked into every subsequent scene. /reload reloads the scene but not the global player entity, so it couldn't clear the stale flag; only a restart (which recreates the global world) did.
  2. The AvatarMovementsBlocked minimap indicator never appeared for gliding-only / double-jump-only restrictions, even though it correctly appeared for every other input modifier (including emote).

Fix

Generalize the check to inputModifier.EverythingEnabled so it accounts for all modifiers. This restores the originally intended semantics ("is any modifier active?") in a way that can't drift again as new flags are added. No extra state was introduced.

⚠️ Behavioral change (please verify in QA)

Because the check now includes gliding and double jump, the AvatarMovementsBlocked minimap indicator ("• Avatar movement disabled") now appears for scenes that disable only gliding or only double jump. Previously it did not appear for those cases. This is the intended, consistent behavior (it already appeared for jump/emote/etc.), but it is a user-visible change.

Tests added

  • InputModifierComponentShould — new suite covering the bit-mask primitives the fix relies on (EverythingEnabled, per-flag independence, DisableAll dominance, RemoveAllModifiers).
  • InputModifierHandlerSystemShould — new cases: reset-on-leave for gliding-only and double-jump-only scenes; PushMovementBlockedBus_WhenOnlyGlidingDisabled / ...WhenOnlyDoubleJumpDisabled (pins that the indicator now fires Applied on entry and Removed on leave); reapply-on-return; gate-clears-when-scene-empties.
  • InputModifierHandlerSystemCrossSceneShould — integration suite with two per-scene systems sharing one global player, reproducing the teleport handoff (including the exact playtest repro) and proving scenes don't clobber each other's restrictions.

Test Instructions

Run this PR:

metaforge explorer run 9518

Run with a fresh account:

metaforge account create --clear
metaforge explorer run 9518

Prerequisites

  • The Gliding feature must be enabled in this build (the glider is also gated behind FeatureId.Gliding). Confirm you can glide in a normal parcel before testing the restriction.

Test Steps (QA — the bug repro)

  1. Launch the client and go to any location where gliding works. Jump from a height and hold to open the glider — confirm the glider opens. This is the baseline.
  2. Teleport to shroomzoom.dcl.eth (this World disables the glider via InputModifier). Try to glide — the glider must NOT open here. This confirms the restriction is applied.
  3. Without restarting the client, teleport to any other World/scene that does not disable gliding (in the playtest this was the "Eternal Vortex" World; any unrestricted parcel/World works).
  4. Try to glide again.
    • Expected (fixed): the glider opens normally.
    • Before this PR (bug): the glider stayed disabled until a full client restart.
  5. Return to shroomzoom.dcl.eth and confirm the glider is disabled again, then leave once more and confirm it re-enables — the restriction should toggle cleanly on every entry/exit.

Verify the minimap indicator (behavioral change)

  1. While in shroomzoom.dcl.eth, check the minimap scene-restrictions icon/toast — it should now list "• Avatar movement disabled" (this did not appear before this PR for a gliding-only restriction).
  2. After teleporting out to an unrestricted scene, confirm that entry disappears from the indicator.

Additional Testing Notes

  • /reload while inside shroomzoom.dcl.eth should not be required to recover gliding after leaving; verify recovery happens purely on teleport out.
  • The same fix covers a scene that disables only double jump; if a test scene is available, verify double jump recovers after leaving it and that the same minimap indicator appears while inside it.

Automated tests

Run the InputModifier EditMode suites in the Unity Test Runner (filter by InputModifier):

  • InputModifierComponentShould
  • InputModifierHandlerSystemShould
  • InputModifierHandlerSystemCrossSceneShould

Quality Checklist

  • Changes have been tested locally
  • EditMode unit + integration tests added and passing
  • Performance impact considered (no per-frame or allocation changes; a SendBusMessage condition simplification)
  • Behavioral change to the minimap indicator documented above for QA
  • For SDK features: Test scene is included (existing shroomzoom.dcl.eth reproduces the restriction)

Code Review Reference

Please review our Branch & PR Standards before submitting. It explains the automated review flow, QA/DEV approval requirements, and what each label does — especially useful for first-time contributors.

@popuz popuz self-assigned this Jul 29, 2026
@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

badge

Warnings count reduced: 14075 => 14060

@popuz popuz changed the title add integration tests for input modifiers fix: disable glider input modifier was carried over cross scenes Jul 29, 2026
@popuz popuz changed the title fix: disable glider input modifier was carried over cross scenes fix: scene input modifiers were carried over across scenes (glider) Jul 29, 2026
@popuz popuz changed the title fix: scene input modifiers were carried over across scenes (glider) fix: scene's gliding restriction stayed active after the player left that scene Jul 29, 2026
@popuz
popuz marked this pull request as ready for review July 29, 2026 20:42
@popuz
popuz requested review from a team as code owners July 29, 2026 20:42
@decentraland-bot
decentraland-bot self-requested a review July 29, 2026 20:42

@decentraland-bot decentraland-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

PR Review — fix: scene's gliding restriction stayed active after the player left that scene

STEP 2 — Root-cause check ✅

Problem: A scene that disabled only gliding (or only double jump) via InputModifier leaked that restriction into every subsequent scene until a full client restart.

Root cause correctly identified and fixed. The old SendBusMessage check manually enumerated 6 flags (DisableAll, DisableWalk, DisableJog, DisableRun, DisableJump, DisableEmote) but excluded DisableGliding and DisableDoubleJump. This meant lastBusMessageAction never flipped to Applied for gliding-only scenes, so the OnSceneIsCurrentChanged(false) reset gate (lastBusMessageAction == Applied) was never satisfied, and the global DisableGliding flag persisted across scene transitions.

The fix replaces the manual enumeration with inputModifier.EverythingEnabled (which checks disabledMask == ModifierId.None), correctly covering all current and future modifier flags. This is a cause fix, not a symptom fix.

STEP 3 — Design & integration ✅

No new long-lived units introduced. The change is a single-line fix within the existing InputModifierHandlerSystem. No new systems, managers, or persistent state holders are added.

Architecture assessment: The system holds one SceneRestrictionsAction lastBusMessageAction field (a single enum scalar). This is comparable to a bool hasInitialized field and does not violate the "systems must not hold persistent collections" rule. Multiple per-scene system instances sharing one global InputModifierComponent is safe because exactly one scene is current at a time — OnSceneIsCurrentChanged(false) fires on the departing scene before (true) on the arriving one.

Semantic coupling note (non-blocking): lastBusMessageAction now serves double duty as both the UI indicator gate and the cleanup gate. If a future modifier should trigger cleanup but not the UI indicator (or vice versa), this coupling would need to be revisited. Under the current design this is fine — all modifiers should consistently trigger both.

Teardown/consumption trace: No new subscriptions, event hookups, connections, or buffers are introduced.

STEP 4 — Member audit ✅

No new public properties or accessors are added in production code. EverythingEnabled is a pre-existing property on InputModifierComponent (checks disabledMask == ModifierId.None). Its single consumer in the production diff is the SendBusMessage method — this is appropriate since EverythingEnabled is a canonical bitmask check, not a single-use derived predicate.

STEP 5 — Line-level review

Production code (1 line changed): Clean, correct, more maintainable than the manual enumeration it replaces. No bugs, no performance regressions, no resource leaks.

Behavioral change — PR description inaccuracy [P2]: The PR description states "SendBusMessage / the UI indicator behavior is unchanged" and mentions a "dedicated sceneAssertedModifiers flag" — but neither is true in the actual implementation. The fix does change the UI indicator: the minimap AvatarMovementsBlocked indicator will now appear for gliding-only and double-jump-only restrictions (previously it did not). The tests explicitly validate this new behavior (PushMovementBlockedBus_WhenOnlyGlidingDisabled, PushMovementBlockedBus_WhenOnlyDoubleJumpDisabled). The description appears to have been written for an earlier iteration of the fix. This is arguably a better, more consistent behavior — but the description should be updated to accurately reflect what changed, so QA testers know to verify the indicator behavior.

Tests (426 new lines): Well-structured, comprehensive, follow AAA pattern with NUnit + NSubstitute as required. Three test suites cover:

  • InputModifierComponentShould — bitmask primitives (EverythingEnabled, per-flag independence, DisableAll dominance, RemoveAllModifiers)
  • InputModifierHandlerSystemShould — new cases for gliding-only/double-jump-only reset, bus messages, reapply-on-return, gate-clears-when-scene-empties
  • InputModifierHandlerSystemCrossSceneShould — integration suite reproducing the exact playtest bug with two per-scene systems sharing one global player

No issues found in test code. Test helper methods (Set/Get switch in InputModifierComponentShould) are appropriate for the parameterized [TestCase] pattern.

STEP 3 (Security) ✅

No security issues found. The change is a pure logic refactor in a game input system. No auth, network, file I/O, sensitive data, or injection surfaces are involved.

STEP 6 — Complexity

SIMPLE — 1 production file changed (1 line), 3 test files added. The change modifies how a condition is evaluated in an existing ECS system method, using an existing property.

STEP 7 — QA assessment

YES — This changes runtime behavior affecting player input (glider/double-jump restrictions) and the minimap UI indicator. Manual verification of the teleport scenario is required.

STEP 8 — Non-blocking warnings

None (Main scene not modified).

STEP 9 — Verdict

REVIEW_RESULT: PASS ✅
COMPLEXITY: SIMPLE
COMPLEXITY_REASON: Single-line fix in InputModifierHandlerSystem.SendBusMessage — replaces manual flag enumeration with existing EverythingEnabled bitmask check
QA_REQUIRED: YES


Reviewed by Jarvis 🤖 · Requested by decentraland-bot via GitHub

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Auto-approved based on Jarvis review — simple fix/chore with no blocking issues. QA approval is still required.

@github-actions

github-actions Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Tests: 24492 passed, 0 failed

@DafGreco DafGreco 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.

✔️ PR reviewed and approved by QA on both platforms following instructions playing both happy and un-happy path

Regressions for this ticket had been performed in order to verify that the normal flow is working as expected:

  • [✔️ ] Backpack and wearables in world
  • [✔️ ] Emotes in world and in backpack
  • [✔️ ] Teleport with map/coordinates/Jump In
  • [ ✔️] Chat and multiplayer
  • [✔️ ] Profile card
  • [ ✔️] Camera
  • [✔️ ] Settings

Evidence on the following thread

@popuz
popuz merged commit a906590 into dev Jul 30, 2026
19 of 20 checks passed
@popuz
popuz deleted the fix/sdk/cross-scene-disabled-glider branch July 30, 2026 12:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants