Add debug skip-to-next-subphase for MatchManager
Server-authoritative skip flag consumed by each phase wait loop (loading barrier, hide/resolution wait, seek phase), routed via authority like RequestEndMatch. Exposed as a [ContextMenu] and public method; single transition per trigger, cleared on teardown. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
c355dd2438
commit
0a1e286a34
1 file changed
+53
@@ -243,6 +243,8 @@ public class MatchManager : MonoBehaviour
|
||||
// barrier round is dropped). The server holds the Hide phase until every expected client has replied.
|
||||
private const string MsgBeginLoad = "Match_BeginLoad";
|
||||
private const string MsgReady = "Match_Ready";
|
||||
// Client -> server (authority only): DEBUG request to skip the rest of the current sub-phase.
|
||||
private const string MsgDebugSkip = "Match_DebugSkip";
|
||||
|
||||
// Server-only sub-phase machine. _pendingHiders holds the hiders not yet found; seek ends the instant it
|
||||
// empties. _phaseRoutine is the running Hide->Seek->Resolution coroutine (stopped if the match is ended
|
||||
@@ -255,6 +257,9 @@ public class MatchManager : MonoBehaviour
|
||||
// clientId is ephemeral (reused after a disconnect); the value is stable across that client's restarts.
|
||||
private readonly Dictionary<ulong, string> _persistentIds = new Dictionary<ulong, string>();
|
||||
private Coroutine _phaseRoutine;
|
||||
// DEBUG: set by DebugSkipToNextSubPhase; the running phase-wait loop consumes it once to break out early,
|
||||
// so RunMatchPhases advances to the next SetPhaseServer without waiting out the timer/barrier.
|
||||
private bool _skipPhase;
|
||||
private readonly List<ulong> _phaseRecipients = new List<ulong>(); // reused scratch for the phase broadcast
|
||||
private readonly List<ulong> _radarPingIds = new List<ulong>(); // reused scratch: hider NetworkObjectIds for a sweep
|
||||
private readonly List<ulong> _radarRecipients = new List<ulong>(); // reused scratch: hunter client recipients for a sweep
|
||||
@@ -330,6 +335,7 @@ public class MatchManager : MonoBehaviour
|
||||
_net.CustomMessagingManager.UnregisterNamedMessageHandler(MsgIdentity);
|
||||
_net.CustomMessagingManager.UnregisterNamedMessageHandler(MsgBeginLoad);
|
||||
_net.CustomMessagingManager.UnregisterNamedMessageHandler(MsgReady);
|
||||
_net.CustomMessagingManager.UnregisterNamedMessageHandler(MsgDebugSkip);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,6 +362,7 @@ public class MatchManager : MonoBehaviour
|
||||
_net.CustomMessagingManager.RegisterNamedMessageHandler(MsgReportHit, OnReportHitMessage);
|
||||
_net.CustomMessagingManager.RegisterNamedMessageHandler(MsgIdentity, OnIdentityMessage);
|
||||
_net.CustomMessagingManager.RegisterNamedMessageHandler(MsgReady, OnReadyMessage);
|
||||
_net.CustomMessagingManager.RegisterNamedMessageHandler(MsgDebugSkip, OnDebugSkipMessage);
|
||||
}
|
||||
|
||||
_subPhase = MatchSubPhase.None;
|
||||
@@ -402,6 +409,7 @@ public class MatchManager : MonoBehaviour
|
||||
|
||||
StopAllCoroutines();
|
||||
_phaseRoutine = null;
|
||||
_skipPhase = false;
|
||||
_hooked = false;
|
||||
_sceneReady = false;
|
||||
_hasAuthorityClient = false;
|
||||
@@ -656,11 +664,13 @@ public class MatchManager : MonoBehaviour
|
||||
// found - identical early-out to the plain WaitPhase(_pendingHiders.Count == 0) it replaces for Seek.
|
||||
private IEnumerator RunSeekPhase(float seconds)
|
||||
{
|
||||
_skipPhase = false; // drop a stale debug-skip from the previous phase; only this phase may set it
|
||||
float t = seconds;
|
||||
float pingTimer = radarPingInterval; // first sweep one interval into Seek
|
||||
while (t > 0f)
|
||||
{
|
||||
if (_pendingHiders.Count == 0) yield break;
|
||||
if (ConsumeSkip()) yield break; // DEBUG: jump straight to Resolution
|
||||
|
||||
if (radarPingInterval > 0f)
|
||||
{
|
||||
@@ -680,26 +690,69 @@ public class MatchManager : MonoBehaviour
|
||||
// Waits up to 'seconds' of server real time, returning early the moment 'until' (if supplied) is true.
|
||||
private IEnumerator WaitPhase(float seconds, System.Func<bool> until)
|
||||
{
|
||||
_skipPhase = false; // drop a stale debug-skip from the previous phase; only this phase may set it
|
||||
float t = seconds;
|
||||
while (t > 0f)
|
||||
{
|
||||
if (until != null && until()) yield break;
|
||||
if (ConsumeSkip()) yield break; // DEBUG: jump to the next sub-phase
|
||||
t -= Time.deltaTime;
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
|
||||
// DEBUG: true (once) if a skip-to-next-sub-phase was requested; consumed so a single request fires exactly
|
||||
// one transition. Only ever set server-side (DebugSkipServer) and only these server-run wait loops read it.
|
||||
private bool ConsumeSkip()
|
||||
{
|
||||
if (!_skipPhase) return false;
|
||||
_skipPhase = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>DEBUG. Instantly advances the running match to the next sub-phase
|
||||
/// (Loading -> Hide -> Seek -> Resolution -> back to lobby), skipping the rest of the current phase's timer
|
||||
/// or loading barrier. Routed through match authority: the host/server acts directly, a remote authority
|
||||
/// client asks the server. No-op if this machine is not the authority or no sub-phase is running.</summary>
|
||||
[ContextMenu("Debug/Skip To Next Sub-Phase")]
|
||||
public void DebugSkipToNextSubPhase()
|
||||
{
|
||||
if (_net == null) return;
|
||||
if (_net.IsServer) { DebugSkipServer(); return; }
|
||||
if (_isLocalAuthority) SendEmptyNamedMessage(MsgDebugSkip, NetworkManager.ServerClientId);
|
||||
}
|
||||
|
||||
// Server-only. Flags the running phase-wait loop to break early so RunMatchPhases moves to the next sub-phase.
|
||||
private void DebugSkipServer()
|
||||
{
|
||||
if (_net == null || !_net.IsServer) return;
|
||||
if (_subPhase == MatchSubPhase.None) return; // nothing running to skip
|
||||
_skipPhase = true;
|
||||
Debug.Log($"[MatchManager] DEBUG: skipping the rest of {_subPhase}.", this);
|
||||
}
|
||||
|
||||
// Server handler: an authority client asked to skip to the next sub-phase.
|
||||
private void OnDebugSkipMessage(ulong senderClientId, FastBufferReader reader)
|
||||
{
|
||||
if (_net == null || !_net.IsServer) return;
|
||||
if (!IsMatchAuthority(senderClientId)) return;
|
||||
DebugSkipServer();
|
||||
}
|
||||
|
||||
// --- Pre-match loading barrier ---------------------------------------------------------------------
|
||||
// Server-only. Opens the barrier (snapshot the just-respawned avatars + the clients that must load them,
|
||||
// switch everyone to Loading, tell each client which avatars to wait for), then spins until every still-
|
||||
// connected expected client has reported ready - or matchLoadTimeout elapses.
|
||||
private IEnumerator RunLoadingBarrier()
|
||||
{
|
||||
_skipPhase = false; // drop any debug-skip left over from the previous phase/match; only this phase may set it
|
||||
BeginLoadingBarrierServer();
|
||||
|
||||
float t = matchLoadTimeout;
|
||||
while (!AllClientsReady())
|
||||
{
|
||||
if (ConsumeSkip()) break; // DEBUG: force the match to start now, without waiting for every client
|
||||
|
||||
// The host is a client too and never messages itself: fold its own local readiness in directly.
|
||||
if (LocalClientReady(_matchAvatarIds)) _readyClients.Add(_net.LocalClientId);
|
||||
|
||||
|
||||
Reference in new issue
Block a user