Add host-configurable match settings persisted in settings
- GameSettings: 7 match fields (mode, hunter count, hide/seek/resolve durations, radar ping interval/lifetime) with authored defaults - MatchSettingSlider (new): enum-driven panel binder, seeds from settings, saves on change; lifetime label shows one decimal (invariant culture) - MatchManager: ApplyHostMatchSettings() on StartMatch (clamped); phase message now carries ping interval so clients tick correctly Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1468921ca2
commit
270c42d85b
4 files changed
+200
-14
No files matched your search
@@ -198,6 +198,7 @@ public class MatchManager : MonoBehaviour
|
||||
private float _phaseEndTime; // Time.time when the current sub-phase ends (for the HUD countdown)
|
||||
private float _seekStartTime; // Time.time Seek began locally (anchors the ping-countdown cadence, START-aligned)
|
||||
private MatchMode _localMode = MatchMode.Classic; // mirror of the server's matchMode, carried in the phase message
|
||||
private float _localPingInterval; // mirror of the server's radarPingInterval, carried in the phase message (seeded from the inspector value in Awake)
|
||||
|
||||
private const string PreMatchTag = "PreMatch"; // scene props destroyed when seek starts
|
||||
|
||||
@@ -210,10 +211,11 @@ public class MatchManager : MonoBehaviour
|
||||
/// <summary>Seconds left in the current sub-phase for the HUD (clamped to 0; cosmetic).</summary>
|
||||
public float LocalPhaseRemaining => Mathf.Max(0f, _phaseEndTime - Time.time);
|
||||
|
||||
/// <summary>Seconds between radar ping sweeps (0 = pings disabled). Baked identically on every client
|
||||
/// (persistent MatchManager, no replication - like <see cref="EmbeddedRevealPercent"/>), so the HUD can
|
||||
/// drive the ping countdown locally.</summary>
|
||||
public float RadarPingInterval => radarPingInterval;
|
||||
/// <summary>Seconds between radar ping sweeps (0 = pings disabled). Mirror of the SERVER's value, carried
|
||||
/// in the phase message - the host overrides its inspector default from the Match Panel settings on match
|
||||
/// start, so the value can no longer be assumed identical on every install. The HUD drives the ping
|
||||
/// countdown locally from this mirror.</summary>
|
||||
public float RadarPingInterval => _localPingInterval;
|
||||
|
||||
/// <summary>Seconds until the next radar ping sweep for the HUD (counts down from <see cref="RadarPingInterval"/>
|
||||
/// and repeats). 0 outside Seek or when pings are disabled. Computed locally from the local Seek-start +
|
||||
@@ -223,9 +225,9 @@ public class MatchManager : MonoBehaviour
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_subPhase != MatchSubPhase.Seek || radarPingInterval <= 0f) return 0f;
|
||||
float intoInterval = (Time.time - _seekStartTime) % radarPingInterval;
|
||||
return radarPingInterval - intoInterval;
|
||||
if (_subPhase != MatchSubPhase.Seek || _localPingInterval <= 0f) return 0f;
|
||||
float intoInterval = (Time.time - _seekStartTime) % _localPingInterval;
|
||||
return _localPingInterval - intoInterval;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,8 +258,9 @@ public class MatchManager : MonoBehaviour
|
||||
// the other client->server ("please end the match"); the server only honors it from the authority id.
|
||||
private const string MsgAuthorityGranted = "Match_AuthorityGranted";
|
||||
private const string MsgRequestEndMatch = "Match_RequestEndMatch";
|
||||
// Server -> all clients: the sub-phase changed (byte phase + float duration). Client -> server: a hunter
|
||||
// reports a hit on a hider (ulong target client id); the server validates and marks the hider found.
|
||||
// Server -> all clients: the sub-phase changed (byte phase + float duration + byte mode + float radar ping
|
||||
// interval). Client -> server: a hunter reports a hit on a hider (ulong target client id); the server
|
||||
// validates and marks the hider found.
|
||||
private const string MsgPhase = "Match_Phase";
|
||||
private const string MsgReportHit = "Match_ReportHit";
|
||||
// Client -> server, right after the client connects: the sender's persistent per-install id (survives its
|
||||
@@ -346,6 +349,7 @@ public class MatchManager : MonoBehaviour
|
||||
{
|
||||
if (Instance && Instance != this) { Destroy(this); return; }
|
||||
Instance = this;
|
||||
_localPingInterval = radarPingInterval; // pre-match HUD default; overwritten by every phase message
|
||||
_net = GetComponent<NetworkManager>();
|
||||
if (!_net) _net = FindFirstObjectByType<NetworkManager>();
|
||||
|
||||
@@ -481,7 +485,7 @@ public class MatchManager : MonoBehaviour
|
||||
_hasAuthorityClient = false;
|
||||
_isLocalAuthority = false;
|
||||
Phase = MatchPhase.Lobby;
|
||||
ApplyPhaseLocal(MatchSubPhase.None, 0f, _localMode);
|
||||
ApplyPhaseLocal(MatchSubPhase.None, 0f, _localMode, _localPingInterval);
|
||||
_pendingHiders.Clear();
|
||||
_persistentIds.Clear();
|
||||
// Drop any in-flight loading-barrier state (StopAllCoroutines above already killed the polls).
|
||||
@@ -676,6 +680,8 @@ public class MatchManager : MonoBehaviour
|
||||
var ids = new List<ulong>(_net.ConnectedClientsIds);
|
||||
if (ids.Count == 0) return;
|
||||
|
||||
ApplyHostMatchSettings();
|
||||
|
||||
Phase = MatchPhase.Transitioning;
|
||||
|
||||
// Roll N distinct random Hunters via partial Fisher-Yates (swap a random not-yet-picked id into
|
||||
@@ -695,6 +701,20 @@ public class MatchManager : MonoBehaviour
|
||||
StartCoroutine(MatchSequence(hunterIds));
|
||||
}
|
||||
|
||||
// Server-only, called once per match start. Pulls the host-authored match rules (Esc-menu Match Panel,
|
||||
// persisted via GameSettings/Settings.json) over the inspector defaults, so the machine that starts the
|
||||
// match decides the rules. Defensively clamped - the JSON is hand-editable.
|
||||
private void ApplyHostMatchSettings()
|
||||
{
|
||||
matchMode = GameSettings.MatchModeIndex == (int)MatchMode.Conversion ? MatchMode.Conversion : MatchMode.Classic;
|
||||
hunterCount = Mathf.Max(0, GameSettings.MatchHunterCount);
|
||||
hideDuration = Mathf.Max(0f, GameSettings.MatchHideDuration);
|
||||
seekDuration = Mathf.Max(0f, GameSettings.MatchSeekDuration);
|
||||
resolveDuration = Mathf.Max(0f, GameSettings.MatchResolveDuration);
|
||||
radarPingInterval = Mathf.Max(0f, GameSettings.MatchRadarPingInterval);
|
||||
radarPingLifetime = Mathf.Max(0f, GameSettings.MatchRadarPingLifetime);
|
||||
}
|
||||
|
||||
private IEnumerator MatchSequence(HashSet<ulong> hunterIds)
|
||||
{
|
||||
_conversionRecords.Clear(); // fresh match inherits no stale statue captures (EndMatch also clears)
|
||||
@@ -1027,17 +1047,18 @@ public class MatchManager : MonoBehaviour
|
||||
|
||||
private void SetPhaseServer(MatchSubPhase phase, float duration)
|
||||
{
|
||||
ApplyPhaseLocal(phase, duration, matchMode);
|
||||
ApplyPhaseLocal(phase, duration, matchMode, radarPingInterval);
|
||||
BroadcastPhase(phase, duration, matchMode, 0, true);
|
||||
}
|
||||
|
||||
// Shared by the server's SetPhaseServer and a client's OnPhaseMessage handler.
|
||||
private void ApplyPhaseLocal(MatchSubPhase phase, float duration, MatchMode mode)
|
||||
private void ApplyPhaseLocal(MatchSubPhase phase, float duration, MatchMode mode, float pingInterval)
|
||||
{
|
||||
bool changed = _subPhase != phase;
|
||||
_subPhase = phase;
|
||||
_phaseEndTime = Time.time + duration;
|
||||
_localMode = mode;
|
||||
_localPingInterval = pingInterval;
|
||||
// Props staged only for the hide phase are gone the instant seek begins; anchor the local ping-countdown
|
||||
// cadence to the same moment (START-aligned, matching the server's sweep timer).
|
||||
if (changed && phase == MatchSubPhase.Seek)
|
||||
@@ -1064,11 +1085,12 @@ public class MatchManager : MonoBehaviour
|
||||
{
|
||||
var cmm = _net ? _net.CustomMessagingManager : null;
|
||||
if (cmm == null) return;
|
||||
using (var writer = new FastBufferWriter(sizeof(byte) + sizeof(float) + sizeof(byte), Allocator.Temp))
|
||||
using (var writer = new FastBufferWriter(sizeof(byte) + sizeof(float) + sizeof(byte) + sizeof(float), Allocator.Temp))
|
||||
{
|
||||
writer.WriteValueSafe((byte)phase);
|
||||
writer.WriteValueSafe(duration);
|
||||
writer.WriteValueSafe((byte)mode);
|
||||
writer.WriteValueSafe(radarPingInterval);
|
||||
if (toAll)
|
||||
{
|
||||
_phaseRecipients.Clear();
|
||||
@@ -1090,7 +1112,8 @@ public class MatchManager : MonoBehaviour
|
||||
reader.ReadValueSafe(out byte phase);
|
||||
reader.ReadValueSafe(out float duration);
|
||||
reader.ReadValueSafe(out byte mode);
|
||||
ApplyPhaseLocal((MatchSubPhase)phase, duration, (MatchMode)mode);
|
||||
reader.ReadValueSafe(out float pingInterval);
|
||||
ApplyPhaseLocal((MatchSubPhase)phase, duration, (MatchMode)mode, pingInterval);
|
||||
}
|
||||
|
||||
// Server-only. One radar sweep during Seek: registers every still-un-found hider as a blip on each HUNTER's
|
||||
|
||||
Reference in new issue
Block a user