Add match-start readiness barrier (Loading phase)
Gate the Lobby-to-world transition on a new MatchSubPhase.Loading before Hide: movement stays frozen and the Hide countdown does not start until every connected client has spawned all match avatars and applied their paintings. - MatchManager: Loading phase; matchLoadTimeout tunable; Match_BeginLoad broadcasts the respawned avatar NetworkObjectIds (+ generation tag), Match_Ready collects per-client ACKs (host self-checks locally); barrier coroutine in RunMatchPhases before Hide; disconnect/teardown cleanup. - GameModeManager: freeze local player (movement/paint/tab) during Loading, restored via ActiveMode.Enter() on transition to Hide (no camera jump). - NetworkPlayerSetup: treat Loading like Hide in ComputeVisible so a fast-loading client cannot reveal a hider early. - MatchHud: "Loading" phase label. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
ec395ab766
commit
fba4780c0e
4 files changed
+234
-3
No files matched your search
@@ -61,6 +61,7 @@ public class GameModeManager : MonoBehaviour
|
||||
private int _activeIndex = -1;
|
||||
private bool _paused;
|
||||
private int _resumeIndex = -1;
|
||||
private bool _loadFrozen; // true while the pre-match Loading barrier is holding the local player still
|
||||
|
||||
/// <summary>The currently active mode (null before startup).</summary>
|
||||
public GameMode ActiveMode =>
|
||||
@@ -322,6 +323,23 @@ public class GameModeManager : MonoBehaviour
|
||||
// switch key and Tick so Tab can't bootstrap a mode and no camera rig is driven until a bind.
|
||||
if (_activeIndex < 0) return;
|
||||
|
||||
// Pre-match loading barrier: hold the local player frozen (no movement, painting or mode switching)
|
||||
// until every client has finished loading and the Hide phase begins. Re-asserted each frame so nothing
|
||||
// sneaks input in; on release the active mode is re-entered to restore its intended control state
|
||||
// without a camera jump (exactly like SetPaused's resume - the mode was never Exited).
|
||||
if (MovementGated())
|
||||
{
|
||||
if (player) player.ControlEnabled = false;
|
||||
if (painter) painter.PaintingEnabled = false;
|
||||
_loadFrozen = true;
|
||||
return;
|
||||
}
|
||||
if (_loadFrozen)
|
||||
{
|
||||
_loadFrozen = false;
|
||||
ActiveMode?.Enter();
|
||||
}
|
||||
|
||||
// Resolution (the reveal window) is watch-only: force out of Paint and keep the switch key from
|
||||
// re-entering. No restore needed - the end-of-match respawn rebuilds the cycle at index 0.
|
||||
bool suppressPaint = PaintSuppressed();
|
||||
@@ -355,6 +373,15 @@ public class GameModeManager : MonoBehaviour
|
||||
match.LocalSubPhase == MatchManager.MatchSubPhase.Resolution;
|
||||
}
|
||||
|
||||
/// <summary>True while the local match is in its pre-Hide LOADING barrier, during which the local player is
|
||||
/// frozen (no movement, painting or mode switching) until every client has finished loading.</summary>
|
||||
private bool MovementGated()
|
||||
{
|
||||
var match = MatchManager.Instance;
|
||||
return match && match.MatchActive &&
|
||||
match.LocalSubPhase == MatchManager.MatchSubPhase.Loading;
|
||||
}
|
||||
|
||||
/// <summary>First non-Paint mode index at or after <paramref name="start"/> (wrapping once through the
|
||||
/// cycle); -1 if the set has no non-paint mode (a degenerate paint-only configuration).</summary>
|
||||
private int FirstNonPaintFrom(int start)
|
||||
|
||||
Reference in new issue
Block a user