From 782c3b4381e56b13ee5ee6f7fab3072a54cebe50 Mon Sep 17 00:00:00 2001 From: MelekTaus Moribundus Date: Sun, 12 Jul 2026 15:30:31 +0200 Subject: [PATCH] Block escape/tab menu while loading locally (join sync + Loading phase) Co-Authored-By: Claude Fable 5 --- Assets/Scripts/Components/UI/OverviewMenu.cs | 4 +++ Assets/Scripts/Network/MatchManager.cs | 27 ++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/Assets/Scripts/Components/UI/OverviewMenu.cs b/Assets/Scripts/Components/UI/OverviewMenu.cs index 782b299..244af95 100644 --- a/Assets/Scripts/Components/UI/OverviewMenu.cs +++ b/Assets/Scripts/Components/UI/OverviewMenu.cs @@ -103,6 +103,10 @@ public class OverviewMenu : MonoBehaviour matchResultScreen.Close(); else if (_open) Close(resume: true); + // Loading (joining a session, or the pre-match Loading barrier): the scene/avatar is mid-setup, + // pausing or switching modes here corrupts the camera rigs - Esc/Tab do nothing. + else if (MatchManager.Instance && MatchManager.Instance.IsLoadingLocally) + { } // Paint mode: Esc exits back to the camera mode instead of opening the menu. Player Control // first; the hunter's role-scoped set has none, so it falls through to First Person. else if (gameMode && gameMode.EffectiveMode is PaintMode) diff --git a/Assets/Scripts/Network/MatchManager.cs b/Assets/Scripts/Network/MatchManager.cs index 9f88e23..511e7d7 100644 --- a/Assets/Scripts/Network/MatchManager.cs +++ b/Assets/Scripts/Network/MatchManager.cs @@ -208,12 +208,22 @@ public class MatchManager : MonoBehaviour private const string PreMatchTag = "PreMatch"; // scene props destroyed when seek starts + // Local loading trackers (see IsLoadingLocally). Two flags because a join sync fires per-scene + // LoadComplete events WHILE the synchronization is still running - one boolean would clear early. + private bool _syncingLocally; // NGO client synchronization (join) in flight + private bool _sceneOpLocally; // additive scene load/unload in flight on this machine + /// True while a match sub-phase (Hide/Seek/Resolution) is running on this machine. public bool MatchActive => _subPhase != MatchSubPhase.None; /// This machine's current sub-phase (mirror of the server's broadcast). public MatchSubPhase LocalSubPhase => _subPhase; /// The running match's mode (mirror of the server's choice). Drives per-client hider visibility. public MatchMode LocalMatchMode => _localMode; + /// True while THIS machine is loading: joining a session (NGO client synchronization / an + /// additive scene load in flight) or held in the pre-match Loading barrier. The overview menu blocks + /// itself during this - pausing/switching modes mid-setup corrupts the camera rigs. + public bool IsLoadingLocally => + _syncingLocally || _sceneOpLocally || _subPhase == MatchSubPhase.Loading; /// Seconds left in the current sub-phase for the HUD (clamped to 0; cosmetic). public float LocalPhaseRemaining => Mathf.Max(0f, _phaseEndTime - Time.time); @@ -491,6 +501,8 @@ public class MatchManager : MonoBehaviour _skipPhase = false; _hooked = false; _sceneReady = false; + _syncingLocally = false; + _sceneOpLocally = false; _hasAuthorityClient = false; _isLocalAuthority = false; Phase = MatchPhase.Lobby; @@ -523,6 +535,21 @@ public class MatchManager : MonoBehaviour // instantiation context) - NGO additive loads don't promote the active scene on their own. private void OnSceneEvent(SceneEvent e) { + // Track this machine's own in-flight loads (drives IsLoadingLocally). Begin/complete events for + // OTHER clients also arrive on the server - filter to the local client id. + if (e.ClientId == _net.LocalClientId) + { + switch (e.SceneEventType) + { + case SceneEventType.Synchronize: _syncingLocally = true; break; + case SceneEventType.SynchronizeComplete: _syncingLocally = false; break; + case SceneEventType.Load: + case SceneEventType.Unload: _sceneOpLocally = true; break; + case SceneEventType.LoadComplete: + case SceneEventType.UnloadComplete: _sceneOpLocally = false; break; + } + } + if (e.SceneEventType != SceneEventType.LoadComplete) return; if (e.SceneName != lobbySceneName && e.SceneName != matchSceneName) return; if (e.Scene.IsValid() && e.Scene.isLoaded) SceneManager.SetActiveScene(e.Scene);