Skip to content

Commit 398b284

Browse files
luwenyiclaude
andcommitted
refactor(vs): replace edge-triggered pause with level-triggered state convergence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e4bd228 commit 398b284

1 file changed

Lines changed: 22 additions & 14 deletions

File tree

  • Untiy/Assets/Samples/BoomNetwork/0.1.0/Vampire Survivors Demo/Scripts/Network

Untiy/Assets/Samples/BoomNetwork/0.1.0/Vampire Survivors Demo/Scripts/Network/VSNetworkManager.cs

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
// BoomNetwork VampireSurvivors Demo — Network Manager
22
//
3-
// DESIGN PRINCIPLE: All GameState mutations happen through exactly two
4-
// deterministic paths, both driven by FrameData from the server:
5-
//
3+
// DESIGN PRINCIPLE 1 — Deterministic paths (GameState mutation):
4+
// All GameState mutations go through exactly two paths driven by FrameData:
65
// 1. Frame events (OnPlayerJoined/Left) — embedded in FrameData,
76
// dispatched BEFORE OnFrame, same frame on all clients.
87
// 2. OnFrame → Tick → ApplyInputs — processes player inputs,
98
// auto-inits players on first input appearance.
9+
// OnFrameSyncStart only sets up the deterministic seed and Dt.
10+
// No InitPlayer, no direct GameState mutation outside frame processing.
1011
//
11-
// OnFrameSyncStart only sets up the deterministic seed and Dt.
12-
// No InitPlayer, no direct GameState mutation outside frame processing.
12+
// DESIGN PRINCIPLE 2 — Level-Triggered Pause Convergence:
13+
// Game-pause state is managed via Level-Triggered State Convergence,
14+
// NOT edge-triggered delta tracking. On every OnFrame, we compare:
15+
// - wantsPause (game logic: IsAnyPlayerUpgrading)
16+
// - isPaused (network state: IsGamePaused)
17+
// and drive toward convergence. This pattern is self-correcting and
18+
// handles same-tick consecutive state changes without any local memory.
19+
// The Update() path provides the deadlock-breaker (RequestGameResume)
20+
// for when the server is paused and OnFrame never fires.
1321

1422
using UnityEngine;
1523
using BoomNetwork.Client.FrameSync;
@@ -34,7 +42,6 @@ public class VSNetworkManager : MonoBehaviour
3442
uint _desyncFrame;
3543
byte _pendingUpgradeChoice;
3644
bool _firstInputSent;
37-
bool _wasUpgrading;
3845

3946
// Cached GUIStyles
4047
bool _stylesCached;
@@ -99,12 +106,11 @@ void Update()
99106
VSInput.Encode(_inputBuf, h, v, ability);
100107
_network.SendInput(_inputBuf);
101108

102-
// 升级选择发出后,立即请求恢复帧推送(打破死锁:OnFrame 需要服务器推帧才触发)
103-
// 同时重置 _wasUpgrading,确保下一帧能重新检测到新的升级状态(同帧可能再次触发升级)
109+
// Deadlock-breaker: while server is paused, OnFrame never fires.
110+
// RequestGameResume unblocks frame delivery after upgrade choice is sent.
104111
if (ability != 0)
105112
{
106113
Debug.Log($"[VS] Upgrade choice sent: ability={ability}, IsGamePaused={_network.Client.IsGamePaused}");
107-
_wasUpgrading = false;
108114
_network.Client.RequestGameResume();
109115
}
110116
}
@@ -192,13 +198,15 @@ void OnFrame(FrameData frame)
192198
uint hash = _sim.State.ComputeHash();
193199
_network.Client.SendFrameHash(frame.FrameNumber, hash);
194200

195-
// 检测升级暂停状态变化 → 请求服务器暂停/恢复帧推送
196-
bool upgrading = _sim.IsAnyPlayerUpgrading();
197-
if (upgrading && !_wasUpgrading)
201+
// Level-Triggered Pause Convergence (see DESIGN PRINCIPLE 2 at top of file):
202+
// Compare game's desired pause state vs network's actual pause state,
203+
// and drive toward convergence every frame. No local memory needed.
204+
// IsGamePaused is the authoritative source of truth for network state.
205+
bool wantsPause = _sim.IsAnyPlayerUpgrading();
206+
if (wantsPause && !_network.Client.IsGamePaused)
198207
_network.Client.RequestGamePause();
199-
else if (!upgrading && _wasUpgrading)
208+
else if (!wantsPause && _network.Client.IsGamePaused)
200209
_network.Client.RequestGameResume();
201-
_wasUpgrading = upgrading;
202210
}
203211

204212
void OnDesync(FrameHashMismatch mismatch)

0 commit comments

Comments
 (0)