AMassBattleNetworkGameStateBase
Source/MassBattle/Public/Network/MassBattleNetworkGameStateBase.h
概述 Overview
锁步联网基础设施 GameState:提供多播 RPC 传输(命令/同步/失步/共识 hash/全量重同步)、统一锁步命令执行(ExecuteNetCommand,内置 Agent/Projectile/Loot/Obstacle 全套 Spawn/Despawn/SetAgentTransform 标签,双端同一实现保证确定性)、失步检测(客户端存储服务器 hash,post-tick 边界比对)、快照模板管理(玩家角色模板预缓存)与蓝图指令扩展点(ReceiveLockstepCommandFromServer)。GameState 是所有复制客户端都存在的对象,因此是命令多播的自然载体。
Lockstep networking infrastructure GameState: provides multicast RPC transport (commands / sync / desync / agreed hashes / full resync), unified lockstep command execution (ExecuteNetCommand — the built-in Agent/Projectile/Loot/Obstacle Spawn/Despawn/SetAgentTransform tags run the same implementation on both peers for determinism), desync detection (the client stores server hashes and compares at the post-tick boundary), snapshot template management (player-character template pre-caching), and a Blueprint command extension point (ReceiveLockstepCommandFromServer). The GameState exists on every replicated client, making it the natural carrier for command multicast.
继承 Inheritance
方法 Methods
设置 bReplicates = true。复制属性:仅 PlayerCharacterClass(见 GetLifetimeReplicatedProps)。
Sets bReplicates = true. Replicated property: only PlayerCharacterClass (see GetLifetimeReplicatedProps).
Input
无参数。
No parameters.
Output
无返回值。副作用:设置复制标志。
No return value. Side effect: replication flag.
绑定 OnTickCompleted → OnSimTickCompleted(必须无条件——无此绑定 TickHashHistory 恒空,失步检测永不触发);权威端缓存 CachedGameMode;客户端置 MB->bNetworkedMode = true 并预缓存玩家角色快照模板。
Binds OnTickCompleted → OnSimTickCompleted (must happen unconditionally — without it TickHashHistory stays empty and desync checks never fire); the authority caches CachedGameMode; clients set MB->bNetworkedMode = true and pre-cache the player-character snapshot template.
Input
无参数。
No parameters.
Output
无返回值。副作用:绑定回调、缓存 GameMode、客户端设置网络模式与模板缓存。
No return value. Side effects: callback binding, GameMode cache, client network-mode flag and template caching.
可靠多播:服务器向全体客户端投递锁步命令。落地(双端都会执行)先做模板预注册,再 EnqueueNetCommand 入队:对 AgentComponent.Spawn 经 NamePayload[0] 的 DataAsset 路径预注册 agent 快照模板(幂等、无实体状态——快照重同步可恢复尚无法生成的 agent);ProjectileComponent.Spawn / LootComponent.Spawn 镜像模板预注册;Obstacle.Spawn 不触碰任何 UID 分配——障碍物 UID 由障碍物槽位位图分配,接收端仅标记命令盖章的槽。实体 UID 一律在命令执行点铸造(确定性出队序 (ExecuteTick, ServerExecSequence),双端一致);历史上的四处 NextGlobalUID 预占 bump 已于 2026-08-15 移除(RPC 到达时机曾是非确定性游标写入者)。
Reliable multicast: delivers a lockstep command from the server to all clients. The implementation (runs on both peers) first does template pre-registration, then EnqueueNetCommand: for AgentComponent.Spawn it pre-registers the agent snapshot template from the DataAsset path in NamePayload[0] (idempotent, no entity state — snapshot resync can rebuild agents whose AgentComponent isn't ready yet); ProjectileComponent.Spawn / LootComponent.Spawn mirror the template pre-registration; Obstacle.Spawn touches no UID allocation — obstacle UIDs come from the obstacle slot bitmap and receivers only mark the command-stamped slot. Entity UIDs are minted at command EXECUTION (deterministic dequeue order (ExecuteTick, ServerExecSequence), identical on both peers); the four historical NextGlobalUID pre-reserve bumps were removed on 2026-08-15 (RPC-arrival time used to be a nondeterministic cursor writer).
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
Cmd | const FMassBattleNetCommand& | 锁步命令。The lockstep command. |
Output
无返回值。副作用:可能预注册模板、把命令写入本地命令队列。
No return value. Side effects: possible template pre-registration, command enqueue.
可靠多播——tick 同步的保证送达通道。客户端落地(仅 NM_Client):SetServerSyncState(ServerTick, InputBufferMs, TimeDilation) 后调用 UpdateNetworkClientPacing。此 RPC 是客户端 ServerTickCount 的唯一来源——缺失则调速(加速/减速/暂停/追帧)完全失效。由服务器 AMassBattleNetworkGameModeBase::OnSimTickCompleted 每 tick 调用。
Reliable multicast — guaranteed delivery for tick sync. Client implementation (NM_Client only): SetServerSyncState(ServerTick, InputBufferMs, TimeDilation) then UpdateNetworkClientPacing. This RPC is the sole source of ServerTickCount on clients — without it pacing (speed up / slow down / pause / catch-up) is dead. Called every tick by AMassBattleNetworkGameModeBase::OnSimTickCompleted.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
ServerTick | int32 | 服务端当前 tick。Current server tick. |
InputBufferMs | float | 服务端动态 K(毫秒)。Server dynamic K in ms. |
TimeDilation | float | 服务端时间膨胀系数。Server time dilation factor. |
Output
无返回值。副作用(客户端):更新同步状态并触发调速逻辑。
No return value. Side effects (client): sync-state update and pacing logic.
可靠多播失步检测 hash。客户端落地(仅 NM_Client):仅存储服务器 hash 到 ServerHashHistory 并调用 ValidateSnapshotsAgainstServerHash——比对本身推迟到 EvaluateDeferredDesyncChecks 的确定性 post-tick 边界(此处比对会与本地 hash 记录竞态,tick T 的校验在 tick T 中段到达,早于 tick T 的 hash 写入,恒得 HasHash=0)。随后立即以服务端 hash 上报 ServerAckTick——防止服务器因缺少 ack 误报失步(尤其 join 握手期);用户主动暂停(IsUserPaused,注意非综合 IsGamePaused——网络恢复暂停有明确下一步,必须继续 ack)时停止 ack,让服务器的 PacingTimeoutTicks 踢出倒计时对卡死客户端生效。由服务器 flush tick 调用。
Reliable multicast desync-check hash. Client implementation (NM_Client only): stores only the server hash into ServerHashHistory and calls ValidateSnapshotsAgainstServerHash — the comparison itself is deferred to EvaluateDeferredDesyncChecks at the deterministic post-tick boundary (comparing here would race local hash recording: the check for tick T arrives mid-tick-T, before tick T's hash is written, always yielding HasHash=0). It then immediately acks with the server hash via ServerAckTick — so the server doesn't flag missing acks as desync (especially during the join handshake); user-initiated pause (IsUserPaused — deliberately not the composite IsGamePaused, since network-recovery pauses have a defined next step and must keep acking) stops acking, letting the server's PacingTimeoutTicks kick countdown run against a stuck client. Called by the server on flush ticks.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
TickCount | int32 | hash 对应 tick。Tick of the hash. |
ExpectedHash | int64 | 服务端期望 hash。Server expected hash. |
Output
无返回值。副作用(客户端):写入 ServerHashHistory、快照 hash 校验、立即 ack。
No return value. Side effects (client): ServerHashHistory write, snapshot hash validation, immediate ack.
非可靠多播——与可靠版逻辑相同(落地即 EnqueueNetCommand),仅经非可靠通道分发,无预占/预注册逻辑。
Unreliable multicast — same logic as the reliable version (enqueues on receipt), just dispatched via the unreliable channel; no pre-reservation/pre-registration logic.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
Cmd | const FMassBattleNetCommand& | 锁步命令。The lockstep command. |
Output
无返回值。副作用:命令入队。
No return value. Side effect: command enqueue.
DedicatedServer:广播共识 hash 列表。当前落地仅遍历条目,具体消费标注为 TODO(dedicated server mode 未完成)。
DedicatedServer: broadcast of the consensus hash list. The current implementation only iterates the entries; actual consumption is marked TODO (dedicated server mode incomplete).
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
InAgreedHashes | const TArray<FAgreedHashEntry>& | 共识 hash 条目列表。Agreed hash entry list. |
Output
无返回值,当前无实际副作用。
No return value; currently no real side effects.
DedicatedServer:分裂投票或超时触发的全量重同步。客户端落地调用 QueueSnapshotApply(Payload, LastAgreedTick) 排队应用快照。
DedicatedServer: full resync triggered by a split vote or timeout. Clients call QueueSnapshotApply(Payload, LastAgreedTick) on receipt to queue the snapshot apply.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
LastAgreedTick | int32 | 最近共识 tick。Last agreed tick. |
Payload | const FEntitySnapshotPayload& | 快照负载。Snapshot payload. |
Output
无返回值。副作用:客户端排队快照应用。
No return value. Side effect: client queues the snapshot apply.
统一锁步命令执行器(双端同一实现)。先解包 TCP 数据块:按 DataBlockId 查缓存块并校验 DataBlockCrc——块缺失/损坏 = 失步(RecordDesync)并返回;成功后 UnpackLockstepPayload 还原三个 payload 数组,消费块。随后按 CommandTag 分派内置标签:AgentComponent.Spawn(LockstepNetID 定位组件,未注册 +3 tick 延迟重试,已绑定重盖章 OwningPlayerSlot,否则 SpawnLockstepAgentAtTransform 本地分配 UID 并写 FAgentController)、ProjectileComponent.Spawn / LootComponent.Spawn(镜像,幂等靠已绑定检查)、AgentComponent.SetAgentTransform(逐轴 Component→Entity 同步:网格解码位置写 FLocating 并清线速度、四元数写 FRotating、缩放写 FScaling)、Game.Pause/Game.Resume(置/清 bSimulationPaused)、Player.Leave 与三个 *.Despawn(UID 查实体并 SetFlag PendingDestroy,下 tick 由销毁队列处理)、Obstacle.Spawn(按路径解析 Box/Capsule/Spline 组件,未到达 +3 延迟重试,UID 与几何来自载荷)、Obstacle.SyncTransform(Spline 写 WorldBase,其余写 FLocating/FRotating)、Obstacle.Destroy(多 UID 置销毁标记)。末尾始终调用 ReceiveLockstepCommandFromServer 通知 BP(按标签过滤自定义逻辑)。
Unified lockstep command executor (identical implementation on both peers). First unwraps TCP data blocks: fetches the block by DataBlockId and validates DataBlockCrc — a missing/corrupt block is a desync (RecordDesync) and returns; on success UnpackLockstepPayload restores the three payload arrays and the block is consumed. Then dispatches by CommandTag on the built-in tags: AgentComponent.Spawn (locates the component via LockstepNetID; unregistered → defer retry +3 ticks; already bound → restamp OwningPlayerSlot; else SpawnLockstepAgentAtTransform with a local UID allocation and writes FAgentController), ProjectileComponent.Spawn / LootComponent.Spawn (mirrored; idempotency via the already-bound check), AgentComponent.SetAgentTransform (per-axis Component→Entity sync: grid-decoded position into FLocating with linear velocity cleared, quaternion into FRotating, scale into FScaling), Game.Pause/Game.Resume (set/clear bSimulationPaused), Player.Leave and the three *.Despawn tags (UID lookup + SetFlag PendingDestroy, destroyed next tick by the destroy queue), Obstacle.Spawn (resolves Box/Capsule/Spline components by path; +3 defer retry until the replicated component arrives; UID and geometry come from the payload), Obstacle.SyncTransform (spline writes WorldBase, others write FLocating/FRotating), Obstacle.Destroy (multi-UID destroy flag). ReceiveLockstepCommandFromServer is always called at the end for BP (filter by tag for custom logic).
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
Cmd | const FMassBattleNetCommand& | 待执行命令(带 DataBlockId 时先解包)。The command to execute (unwrapped first when carrying a DataBlockId). |
Output
无返回值。副作用:实体生成/销毁/变换写入、UID 分配、命令入队(延迟重试)、BP 通知;块损坏时记录失步。
No return value. Side effects: entity spawn/destroy/transform writes, UID allocation, command re-enqueue (defer retry), BP notification; desync recorded on corrupt blocks.
延迟失步校验——由 UMassBattleSubsystem::Tick 在确定性 post-tick 边界(TickHashHistory 写入后)调用(MassBattleSubsystem.cpp:1616)。按升序遍历 ServerHashHistory:对每个已追上(tick ≤ 当前 tick)且本地 hash 已记录的 tick,校验就绪(TryGetTickHashForTick 命中且非 FF/非抑制/超过恢复暂停 tick)时比对,不一致则 RecordDesync 并进入恢复——bLastRecoveryWasLocal 时直接 EnterDesyncRecovery(本地未收敛),否则先 TryLocalSnapshotRecovery,失败才 EnterDesyncRecovery;首失配即停止本帧后续校验。每个 tick 无论校验与否都上报 ack(已校验报本地 hash,未校验报服务端期望值)。hash=0(空世界)是双端合法值,不特判跳过。开启 bLogPerEntityHashes 时输出 [HASH_TICK] 与首个失步 tick 的逐实体 [HASH_ENT] 转储(对照服务器同 tick 日志定位失步实体)。末尾清理已追上条目(严格 <,当前 tick 条目保留供 FF 完成块读取),并在本地恢复首次校验通过后清除 bLastRecoveryWasLocal。
Deferred desync validation — called by UMassBattleSubsystem::Tick at the deterministic post-tick boundary (after TickHashHistory is written) (MassBattleSubsystem.cpp:1616). Iterates ServerHashHistory in ascending order: for each caught-up tick (tick ≤ current tick) whose local hash is recorded, when validation is ready (TryGetTickHashForTick hit and not FF / not suppressed / past the resume tick) it compares; on mismatch it calls RecordDesync and enters recovery — straight to EnterDesyncRecovery when bLastRecoveryWasLocal (local recovery didn't converge), otherwise TryLocalSnapshotRecovery first and only then EnterDesyncRecovery; the first divergence stops further validation this frame. Every tick acks regardless (local hash when validated, server expected otherwise). hash=0 (empty world) is legitimate on both peers — not special-cased away. With bLogPerEntityHashes on, it prints [HASH_TICK] and a per-entity [HASH_ENT] dump of the first diverging tick (compare with the server's same-tick log to identify the diverging entity). Finally it prunes caught-up entries (strict < — the current-tick entry survives for the FF-complete block) and clears bLastRecoveryWasLocal once a local recovery passes its first validation.
Input
无参数。
No parameters.
Output
无返回值。副作用:逐 tick ack、失步记录与恢复触发、诊断日志、清理 hash 历史。
No return value. Side effects: per-tick acks, desync recording and recovery triggers, diagnostic logs, hash-history pruning.
蓝图指令扩展点:ExecuteNetCommand 末尾对每条命令(内置标签如 AgentComponent.Spawn 处理完之后)在所有端无条件触发。BP 按 CommandTag 过滤实现自定义游戏逻辑——服务器与客户端都在命令的确定性 ExecuteTick 收到它,因此在此创建的任务与生成保持锁步一致。
Blueprint command extension point: fired unconditionally on every peer at the end of ExecuteNetCommand for each command (after the plugin's built-in tags like AgentComponent.Spawn are handled). BP filters by CommandTag to implement custom game logic — both server and clients receive it at the command's deterministic ExecuteTick, so tasks and spawns created here stay in lockstep.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
Cmd | const FMassBattleNetCommand& | 正在执行的锁步命令(标签、各负载、ExecuteTick)。The lockstep command being executed (tag, payloads, ExecuteTick). |
Output
无返回值(事件)。
No return value (event).
确保玩家角色快照模板已注册:命中 bPlayerSnapshotTemplateReady 直接返回 true;未命中且 PlayerCharacterClass 已复制则现场 TryCachePlayerSnapshotTemplate 并返回其结果,否则 false。使用预缓存标记避免对无 World 的 CDO 调用 CreateTemplateFromConfig(其 GetMassBattleAgentSubsystem() 返回 nullptr,模板恒无效)。客户端开始 TCP 快照重同步前调用。
Ensures the player-character snapshot template is registered: returns true immediately on bPlayerSnapshotTemplateReady; otherwise tries TryCachePlayerSnapshotTemplate now if PlayerCharacterClass has replicated and returns its result, else false. Uses the pre-cached flag instead of calling CreateTemplateFromConfig on the CDO (which has no World — GetMassBattleAgentSubsystem() returns nullptr and the template is always invalid). Called before a client begins TCP snapshot resync.
Input
无参数。
No parameters.
Output
返回模板是否已就绪。副作用:可能现场预缓存模板。
Returns whether the template is ready. Side effect: possible on-demand template caching.
预缓存 PlayerCharacterClass 快照模板:从类 CDO 取 UMassBattleAgentComponent 及其 AgentConfigAsset(缺失即返回),用 World 的 UMassBattleAgentSubsystem 生成模板数据(MakeAgentTemplateDataFromDataAsset),写入 FNetworking.Key = DataAsset 路径,经 RegisterNetworkTemplateData 注册到快照模板注册表,置 bPlayerSnapshotTemplateReady = true。由 BeginPlay(客户端)与 OnRep_PlayerCharacterClass 调用。
Pre-caches the PlayerCharacterClass snapshot template: takes UMassBattleAgentComponent and its AgentConfigAsset from the class CDO (returns if missing), builds the template data with the World's UMassBattleAgentSubsystem (MakeAgentTemplateDataFromDataAsset), sets FNetworking.Key = DataAsset path, registers via RegisterNetworkTemplateData, and sets bPlayerSnapshotTemplateReady = true. Called from BeginPlay (client) and OnRep_PlayerCharacterClass.
Input
无参数。
No parameters.
Output
无返回值。副作用:注册模板、置就绪标记(前置条件不满足时静默返回)。
No return value. Side effects: template registration, ready flag (silently returns when preconditions fail).
客户端侧 post-tick 回调(私有):暂停期间(总闸或失步,且非追帧)跳过;否则当前仅空转——指令出队已移至 MassBattleSubsystem::Tick 模拟前,玩家-Agent 重绑由组件驱动(快照应用时 RebindToEntity),此处无额外逻辑。
Client-side post-tick callback (private): skipped during pause (master or desync) unless fast-forwarding; otherwise currently a no-op — command dequeue moved to pre-sim in MassBattleSubsystem::Tick, and player-Agent rebinds are component-driven (RebindToEntity on snapshot apply), so nothing remains here.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
TickCount | int32 | 刚完成的 tick。Just-completed tick. |
TickHash | int64 | 该 tick 的确定性 hash(当前未使用)。Deterministic hash of the tick (currently unused). |
Output
无返回值,当前无副作用。
No return value; currently no side effects.
成员变量 Members
玩家 Player.SpawnAgent 使用的角色类,GameMode 在 BeginPlay 时从 DefaultPawnClass 同步并复制到客户端。客户端在 OnRep 中预缓存快照模板。
Character class for Player.SpawnAgent, synced from DefaultPawnClass by the GameMode at BeginPlay and replicated. Clients pre-cache the snapshot template in OnRep.
玩家角色快照模板已注册就绪标记,EnsurePlayerCharacterSnapshotTemplate 以此门控 resync。
Flag that the player-character snapshot template is registered and ready; EnsurePlayerCharacterSnapshotTemplate gates resync on it.
服务器端缓存的 GameMode,用于转发 ExecuteNetCommand(当前由命令分派直接读取权威 GameMode)。
Cached GameMode on the server, for forwarding ExecuteNetCommand (command dispatch currently reads the auth GameMode directly).
管线角色 Pipeline Role
命令下行链路:AMassBattleNetworkGameModeBase::OnSimTickCompleted 排序后调 MulticastCommandToClients(GameModeBase.cpp:222)——落地先预占 UID/预注册模板再 EnqueueNetCommand;命令在 UMassBattleSubsystem::Tick 模拟前出队并经 AMassBattleNetworkGameStateBase::ExecuteNetCommand 统一执行,末尾 ReceiveLockstepCommandFromServer 通知 BP。
失步检测链路(客户端):服务器 flush tick MulticastDesyncCheck(GameModeBase.cpp:259)→ 存储 ServerHashHistory + 立即 ack → UMassBattleSubsystem::Tick 的确定性 post-tick 边界调 EvaluateDeferredDesyncChecks(MassBattleSubsystem.cpp:1616)比对并触发恢复(TryLocalSnapshotRecovery / EnterDesyncRecovery)。MulticastSyncTick(GameModeBase.cpp:249)每 tick 驱动客户端调速。
late-join/resync:客户端 ClientBeginTcpSnapshotResync 先经 EnsurePlayerCharacterSnapshotTemplate 确认模板,再由 AMassBattleNetworkSubsystem 的 OnSnapshotBytesReceived 回调反序列化并 QueueSnapshotApply——应用成功后 RebindToEntity 重链组件。
Command downlink: AMassBattleNetworkGameModeBase::OnSimTickCompleted sorts then calls MulticastCommandToClients (GameModeBase.cpp:222) — on receipt it pre-reserves UIDs / pre-registers templates then EnqueueNetCommand; commands dequeue pre-sim in UMassBattleSubsystem::Tick and execute through AMassBattleNetworkGameStateBase::ExecuteNetCommand, ending with the ReceiveLockstepCommandFromServer BP notification.
Desync detection (client): server flush ticks MulticastDesyncCheck (GameModeBase.cpp:259) → store ServerHashHistory + immediate ack → the deterministic post-tick boundary in UMassBattleSubsystem::Tick calls EvaluateDeferredDesyncChecks (MassBattleSubsystem.cpp:1616) to compare and trigger recovery (TryLocalSnapshotRecovery / EnterDesyncRecovery). MulticastSyncTick (GameModeBase.cpp:249) drives client pacing every tick.
Late-join/resync: ClientBeginTcpSnapshotResync first confirms the template via EnsurePlayerCharacterSnapshotTemplate, then AMassBattleNetworkSubsystem's OnSnapshotBytesReceived callback deserializes and QueueSnapshotApplys — components re-link via RebindToEntity after a successful apply.