MassBattleSnapshotStructs.h
Source/MassBattle/Public/Network/MassBattleSnapshotStructs.h
概述 Overview
Agent 快照结构体集合,实现 TemplateIdentity + RuntimeOverlay 模型:FEntitySnapshotRecord 只携带模板 Key 与 UID(身份)加运行时 overlay blob(状态),不携带 archetype 组合本身——恢复端从本地确定性模板注册表按 Key 取回模板再应用 overlay。顶层 FEntitySnapshotPayload 是经 TCP 侧通道发送的完整快照:实体列表 + UID 分配器占用计数与铸造游标 + 双随机流 + 挂起命令/deferred 生成请求/gap 命令 + 任务记录与 ID 计数器 + LockstepVarStore 快照 + 随快照本体派发的流场基底(FFlowFieldSubstrateEntry)。所有结构均为 BlueprintType,但实际序列化走 FStructSerializer(见 MassBattleSnapshotTcpTransport.html)。
Agent snapshot struct collection implementing the TemplateIdentity + RuntimeOverlay model: FEntitySnapshotRecord carries only a template Key and UID (identity) plus runtime overlay blobs (state) — not the archetype composition itself; the restore side fetches the template from the local deterministic registry by Key and applies the overlay on top. The top-level FEntitySnapshotPayload is the complete snapshot sent over the TCP side-channel: entity list, UID-allocator occupied count and mint cursor, two random streams, pending commands / deferred spawn requests / gap commands, task records and ID counters, LockstepVarStore snapshots, plus the flow-field substrates riding the snapshot itself (FFlowFieldSubstrateEntry). All structs are BlueprintType, but actual serialization goes through FStructSerializer (see MassBattleSnapshotTcpTransport.html).
FFragmentSnapshotBlob USTRUCT
单个 fragment 的序列化快照属性——按注册表布局排列的扁平字节 blob。
Per-fragment serialized snapshot properties — a flat blob of bytes ordered by the registry layout.
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
Data | TArray<uint8> | 空 | 快照属性值的扁平化 blob。Flat blob of snapshot property values. |
FFragmentSnapshotBlobEntry USTRUCT
网络安全的 fragment blob 键值对:FragmentName(fragment 结构名,如 "FMoving",或 LockstepVarStore 中的变量名)+ Blob。恢复端按名匹配布局,增删安全。
Network-safe key/value pair for fragment blobs: FragmentName (the fragment struct name, e.g. "FMoving", or a variable name in LockstepVarStore) plus Blob. Restore matches by name against the layout, so additions/removals stay safe.
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
FragmentName | FName | — | fragment 名(或变量名)。Fragment (or variable) name. |
Blob | FFragmentSnapshotBlob | — | 对应 blob。The corresponding blob. |
FVarStoreSnapshotEntry USTRUCT
LockstepVarStore 快照(每个模板实例一条):TemplateKey 解析实例,Fields 每变量一条(恢复按名匹配——BP 变量增删安全)。由 UMassBattleSubsystem::CaptureVarStoreBlobs 按确定性键序生成。
Lockstep variable store snapshot (one per template instance): TemplateKey resolves the instance; Fields holds one entry per variable (name-matched on restore — BP variable add/remove stays safe). Produced by UMassBattleSubsystem::CaptureVarStoreBlobs in deterministic key order.
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
TemplateKey | FName | — | 模板 BP 类路径(UMassBattleBPTaskTemplateBase 子类)。Template BP class path (UMassBattleBPTaskTemplateBase subclass). |
Fields | TArray<FFragmentSnapshotBlobEntry> | 空 | 每个同步变量一条,FragmentName = 变量名。One entry per synced variable — FragmentName = variable name. |
FEntityRuntimeOverlay USTRUCT
实体运行时状态的动态 blob 快照。使用 TArray 条目保证快照序列化的确定性顺序。仅注册在快照字段注册表中的 fragment 有 entry。
Dynamic blob-based snapshot of entity runtime state. Uses TArray entries for deterministic ordering in snapshot serialization. Only fragments registered in the snapshot field registry have entries.
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
FragmentBlobs | TArray<FFragmentSnapshotBlobEntry> | 空 | 每个 fragment 的快照 blob(FragmentName = fragment 结构名,如 "FMoving")。Per-fragment snapshot blobs (FragmentName = fragment struct name, e.g. "FMoving"). |
SharedFragmentBlobs | TArray<FFragmentSnapshotBlobEntry> | 空 | shared fragment 快照 blob。按实体捕获(per-chunk 值复制),在 BuildEntity 之前经 SetSharedFragment 恢复到模板 shared fragment(写时复制 + 按值去重)。Per-shared-fragment snapshot blobs. Captured per-entity (per-chunk value replicated), restored into template shared fragments BEFORE BuildEntity via SetSharedFragment (copy-on-write + value dedup). |
FEntitySnapshotRecord USTRUCT
快照 payload 中的单个实体记录。TemplateIdentity(TemplateKey + UniqueID)在本地确定性注册表中定位模板,RuntimeOverlay 携带运行时状态;EntityType 为权威主实体类别(主类型 tag 仍仅作 processor 查询索引);TagDelta 为 archetype tag 增量位掩码。
Single entity record in a snapshot payload. TemplateIdentity (TemplateKey + UniqueID) locates the template in the local deterministic registry; RuntimeOverlay carries runtime state; EntityType is the authoritative main entity category (main type tags remain processor query indexes); TagDelta is the archetype tag delta bitmask.
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
TemplateKey | FName | — | 模板标识——在本地确定性注册表中查找的键。TemplateIdentity — lookup key into the local deterministic registry. |
UniqueID | int32 | -1 | 稳定身份,用于跨实体句柄修复(-1 = 无效)。Stable identity for cross-entity handle fixups (-1 = invalid). |
EntityType | EEMainType | Agent | 权威主实体类别;主类型 tag 仍仅作 processor 查询索引。Authoritative main entity category; main type tags remain processor query indexes. |
TagDelta | uint16 | 0 | Archetype tag 增量位掩码(bit0=Appearing, bit1=Dying, bit2=Destroying)。内部位掩码——由 MB_SNAP_TAG_DELTA_* 宏捕获/应用,不对 Blueprint 暴露(UHT 不支持 BP uint16)。Archetype tag delta bitmask (bit0=Appearing, bit1=Dying, bit2=Destroying). Internal bitmask — captured/applied by the MB_SNAP_TAG_DELTA_* macros, not Blueprint-visible (uint16 is unsupported by UHT for BP). |
FlagsLow | int64 | 0 | 完整 flag 快照低位。Full flag snapshot, low word. |
FlagsHigh | int64 | 0 | 完整 flag 快照高位。Full flag snapshot, high word. |
RuntimeOverlay | FEntityRuntimeOverlay | — | 运行时 POD overlay。Runtime POD overlay. |
FDeferredAgentSpawnSnapshot USTRUCT
deferred(分批)agent 生成请求的快照形态——确定性挂起状态:命令已执行、分批未完成。resync 时恢复,防止队列进度与已累计 UID 漂移。字段镜像生成请求的完整参数(位置列表、数量、队伍、发射速度、初始朝向、地面 trace 参数、子步进进度、已累计 UID)。
Snapshot form of a deferred (batched) agent spawn request — deterministic pending state: the command executed, batches not yet emitted. Restored on resync so queue progress and accumulated UIDs do not drift. Fields mirror the full spawn-request parameter set (location list, quantity, team, launch velocity, initial rotation, ground-trace parameters, substep progress, accumulated UIDs).
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
TemplateKey | FName | — | 在 SnapshotTemplateRegistry 中查找(与实体恢复同机制)。Lookup key into SnapshotTemplateRegistry (same mechanism as entity restore). |
Locations | TArray<FVector> | 空 | 待生成的批次位置列表。Batch spawn location list. |
Quantity | int32 | 0 | 总数量。Total quantity. |
Team | int32 | 0 | 队伍。Team. |
LaunchVelocity | FVector2D | ZeroVector | 发射速度。Launch velocity. |
InitialRotation | EInitialRotation | FacePlayer | 初始朝向模式。Initial rotation mode. |
CustomRotation | FRotator | ZeroRotator | 自定义朝向(朝向模式为 Custom 时)。Custom rotation (when the mode is Custom). |
Multipliers | FSpawnerMult | — | 生成器倍率。Spawner multipliers. |
GroundTraceChannel | TEnumAsByte<ECollisionChannel> | ECC_WorldStatic | 地面 trace 碰撞通道。Ground-trace collision channel. |
GroundTraceHeightRange | FVector2D | (1000.f, -1000.f) | 地面 trace 高度范围。Ground-trace height range. |
GroundObjectTypes | TArray<TEnumAsByte<EObjectTypeQuery>> | 空 | 地面对象类型查询列表。Ground object type queries. |
ObstacleObjectTypes | TArray<TEnumAsByte<EObjectTypeQuery>> | 空 | 障碍物对象类型查询列表。Obstacle object type queries. |
MaxSlopeCosine | float | 0.f | 最大坡度余弦。Max slope cosine. |
bAutoActivated | bool | true | 是否自动激活。Whether auto-activated. |
Substeps | int32 | 0 | 子步进数(>0 时分批生成)。Substep count (>0 = batched spawn). |
bFirstTickPending | bool | false | 为 true 时本 tick 跳过处理并置 false(Substeps>0 用于延迟启动到下一帧)。When true, skip processing this tick and set to false (used for Substeps>0 to delay the start to next frame). |
CurrentSubstepIndex | int32 | 0 | 当前子步进索引。Current substep index. |
NextLocationIndex | int32 | 0 | 下一个待生成位置索引。Next location index to spawn. |
AccumulatedUIDs | TArray<int32> | 空 | 已累计句柄以 UID 序列化——恢复时反解为句柄。Accumulated handles serialized as UIDs — resolved back to handles on restore. |
FFlowFieldSubstrateEntry USTRUCT
随快照派发的一份流场基底——服务器缓存拷贝,对快照系统为不透明字节。派发时(而非周期捕获时)从服务器缓存填充到 FEntitySnapshotPayload::FlowFieldSubstrates;应用端在实体重建之前按 FieldName 找到地图上的 AFlowField 并导入,保证新加入/恢复中的客户端不会在无导航基底的状态下模拟任何 tick。结构体必须声明在 FEntitySnapshotPayload 之前(UHT 哈希序要求)。
One flow-field substrate riding a snapshot dispatch — a server-cache copy that is opaque bytes to the snapshot system. Filled into FEntitySnapshotPayload::FlowFieldSubstrates at dispatch time (never captured periodically) from the server cache; the apply side imports it into the map-placed AFlowField (matched by FieldName) BEFORE the entity restore, so a fresh/recovering client never simulates a tick without its navigation substrate. The struct must be declared before FEntitySnapshotPayload (UHT hash order).
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
FieldName | FName | — | 地图摆放的 AFlowField Actor 名——应用端的联结键。Map-placed AFlowField actor name — the import-side join key. |
Bytes | TArray<uint8> | 空 | 紧凑量化基底流(FlowFieldCanvas 格式)——对快照系统不透明。Compact quantized substrate stream (FlowFieldCanvas format) — opaque to the snapshot system. |
FEntitySnapshotPayload USTRUCT
通过 TCP 侧通道发送的实体快照 payload——快照的顶层容器。除实体列表外,携带使恢复端与捕获端逐位一致的全部全局状态:UID 分配器占用计数与单调铸造游标、双随机流、挂起命令、deferred 生成请求、gap 命令、BP 异步任务记录与任务 ID 计数器、LockstepVarStore 快照,以及随快照本体派发的流场基底。
Entity snapshot payload sent via the TCP side-channel — the top-level snapshot container. Besides the entity list it carries all global state needed to make the restore side bit-identical to the capture side: UID-allocator occupied count and monotonic mint cursor, two random streams, pending commands, deferred spawn requests, gap commands, BP async task records with task ID counters, LockstepVarStore snapshots, and the flow-field substrates riding the snapshot dispatch.
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
TickCount | int32 | 0 | 快照捕获 tick。Tick at which the snapshot was captured. |
ServerHashAtCapture | int64 | 0 | 服务器捕获 tick 的每帧 hash——resync 校准基准(恢复端折叠结果与之比对)。Server's per-tick hash at the capture tick — the resync calibration baseline (the restore-side fold is compared against it). |
UIDOccupiedCount | int32 | -1 | 捕获时通用实体分配器的占用计数——哈希折叠指纹(NetworkProcessor 折叠 GetNumOccupied,恢复校验在重建后镜像折叠);分配器位图本身不是快照状态,经 ReconcileUIDAllocator 从重建映射收敛。取代退役的 NextGlobalUID 计数器(槽位分配 + 循环利用,2026-08-16)。General-entity allocator occupied count at capture — the hash-fold fingerprint (NetworkProcessor folds GetNumOccupied; restore validation mirrors it after reconciliation). The allocator bitmap itself is not snapshot state: it converges via ReconcileUIDAllocator from the rebuilt map. Replaces the retired NextGlobalUID counter (slot allocator with recycling, 2026-08-16). |
UIDCursor | int32 | -1 | 捕获时的单调铸造前沿(高水位游标,2026-09-11)。与占用位图(从重建映射推导)不同,游标是独立锁步状态:只在确定性铸造点推进,必须原样恢复(捕获取 EntityUIDAllocator.GetCursor(),恢复经 SetCursor),恢复后(追帧/补 gap)的铸造才能落在服务器本会分配的位置。-1 = 单调化之前服务器的旧载荷(恢复端改为从占用推导游标)。Monotonic mint frontier at capture (high-watermark cursor, 2026-09-11). Unlike the occupancy bitmap (derived from the rebuilt map), the cursor is independent lockstep state: it only advances at deterministic mint points and must be restored verbatim (captured from EntityUIDAllocator.GetCursor(), restored via SetCursor) so post-restore (FF/gap) mints land exactly where the server's would. -1 = legacy payload from a pre-monotonic server (the restoring peer derives the cursor from occupancy instead). |
AgentRandomStream | FRandomStream | — | agent 随机流快照。Agent random stream snapshot. |
HashGridRandomStream | FRandomStream | — | hash 网格随机流快照。Hash-grid random stream snapshot. |
PendingCommands | TArray<FMassBattleNetCommand> | 空 | 挂起命令列表(快照应用后继续执行的命令队列状态)。Pending command list (the queue state that continues after the snapshot apply). |
DeferredSpawnRequests | TArray<FDeferredAgentSpawnSnapshot> | 空 | deferred(分批)agent 生成请求——resync 时恢复(确定性挂起状态)。Deferred (batched) agent spawn requests — restored on resync (deterministic pending state). |
Entities | TArray<FEntitySnapshotRecord> | 空 | 实体记录列表。Entity record list. |
HandleFixups | TArray<FEntityHandleFixup> | 空 | 预留给将来的跨实体句柄修复。Reserved for future cross-entity handle fixups. |
GapCommands | TArray<FMassBattleNetCommand> | 空 | 快照 tick 到当前 tick 之间已执行的命令(从 CommandHistory 填充),客户端 FF 期间重放。Commands executed between the snapshot tick and the current tick (filled from CommandHistory), replayed by the client during FF. |
TaskRecords | TArray<FAgentTaskSnapshotRecord> | 空 | BP 异步任务记录——应用时旧任务被终止,经模板重建(TemplateKey → 重新绑定 delegate)。BP async task records — old tasks are terminated on apply and rebuilt via their templates (TemplateKey → re-bind delegates). |
NextMoveToTaskID | int32 | 1 | 任务 ID 计数器——恢复以保证双端为后续任务创建命令发放相同 ID。Task ID counter — restored so both peers mint identical IDs for future task-creation commands. |
NextChaseAttackTaskID | int32 | 1 | ChaseAttack 任务 ID 计数器。ChaseAttack task ID counter. |
LockstepVarStores | TArray<FVarStoreSnapshotEntry> | 空 | LockstepVarStore 快照——每个模板实例一条(TemplateKey → 实例,Fields → 逐变量值)。在 ApplyEntitySnapshotPayload 中恢复(先于恢复 hash 校验——恢复折叠镜像采集折叠)。LockstepVarStore snapshots — one entry per template instance (TemplateKey → instance, Fields → per-variable values). Restored in ApplyEntitySnapshotPayload (before the restore hash validation — the restore fold mirrors the capture fold). |
FlowFieldSubstrates | TArray<FFlowFieldSubstrateEntry> | 空 | 随快照本体派发的流场基底(2026-09-09 基底时序保证):派发时从服务器缓存填充(绝不周期捕获);应用端在实体重建之前导入——新客户端/恢复客户端绝不会在无导航基底的状态下模拟任何 tick。命令通道补发要 K+defer tick 后才落地;窗口期寻路会分叉 PathPoints(直线回退 vs 服务器的基底 A*)。Flow-field substrates riding the snapshot itself (2026-09-09 substrate ordering guarantee): filled at dispatch time from the server cache (never captured periodically); imported by the apply side BEFORE the entity restore — a fresh/recovering client never simulates a tick without its navigation substrate. Command-channel re-delivery would land K+defer ticks after resume; pathing in that window forks PathPoints (straight-line fallback vs the server's substrate A*). |
管线角色 Pipeline Role
采集:捕获 tick(bCaptureSnapshot)由 UMassBattleNetworkProcessor 构建 FEntitySnapshotPayload——实体记录按快照注册表布局序列化(MassBattleSnapshotRegistry.h),LockstepVarStore 条目由 UMassBattleSubsystem::CaptureVarStoreBlobs 生成,UIDCursor 取自 EntityUIDAllocator.GetCursor(),缓存为 UMassBattleNetworkSubsystem::CachedLatestSnapshot 供 resync/late-join 派发。
传输:服务器侧 DispatchSnapshotViaTcp_Async(GameModeBase)在后台线程经 FMassBattleSnapshotTcpTransport::SerializeSnapshotPayload + CompressSnapshot 序列化压缩,游戏线程非阻塞发送;派发时从服务器流场缓存填充 FlowFieldSubstrates;客户端 OnSnapshotBytesReceived 回调反序列化后 QueueSnapshotApply。
恢复:ApplyEntitySnapshotPayload(NetworkSubsystem)先按 FieldName 把流场基底导入地图上的 AFlowField(早于实体重建,并写入本端缓存供重放块旁路)→ 按 TemplateKey 取模板 → 应用 RuntimeOverlay(含 SharedFragmentBlobs 的 SetSharedFragment)→ 恢复 UID 游标(原样 SetCursor)/随机流/任务记录/挂起命令 → DeserializeFromEntries 恢复 LockstepVarStore → 恢复 hash 折叠与 ServerHashAtCapture 比对(不匹配拒绝应用,fail loud)。
Capture: on capture ticks (bCaptureSnapshot) UMassBattleNetworkProcessor builds the FEntitySnapshotPayload — entity records serialize per the snapshot registry layout (MassBattleSnapshotRegistry.h); LockstepVarStore entries come from UMassBattleSubsystem::CaptureVarStoreBlobs; UIDCursor is taken from EntityUIDAllocator.GetCursor(); the result is cached as UMassBattleNetworkSubsystem::CachedLatestSnapshot for resync/late-join dispatch.
Transport: the server-side DispatchSnapshotViaTcp_Async (GameModeBase) serializes and compresses on a background thread via FMassBattleSnapshotTcpTransport::SerializeSnapshotPayload + CompressSnapshot, then sends non-blocking on the game thread; FlowFieldSubstrates are filled from the server's flow-field cache at dispatch time; the client's OnSnapshotBytesReceived callback deserializes and calls QueueSnapshotApply.
Restore: ApplyEntitySnapshotPayload (NetworkSubsystem) first imports the flow-field substrates into the map-placed AFlowField by FieldName (before the entity restore, also caching them locally for the replay block-bypass) → fetches the template by TemplateKey → applies the RuntimeOverlay (including SetSharedFragment for SharedFragmentBlobs) → restores the UID cursor (verbatim SetCursor) / random streams / task records / pending commands → restores LockstepVarStores via DeserializeFromEntries → runs the restore-hash fold and compares against ServerHashAtCapture (mismatch rejects the apply, fail loud).
补充说明 Notes
FMassBattlePlayerSpawnSnapshotRecord 已移除——玩家身份现由快照内每个 agent 的 FAgentController::OwningPlayerSlot 携带,AgentComponent 在快照应用时经 RebindToEntity 自重链,不再需要独立的每玩家记录。
FMassBattlePlayerSpawnSnapshotRecord is removed — player identity is now carried by each agent's FAgentController::OwningPlayerSlot inside the snapshot, and AgentComponents re-link themselves via RebindToEntity on snapshot apply; no separate per-player record is needed.
ObstacleSpawnManifest(late-join 障碍物生成清单)已于 2026-08-28 移除——命令路由障碍物现已是快照公民(FNetworkTag + 合成模板键,随 Entities 走)。旧清单重放死于两个 bug:ExecuteTick 把清单命令重盖为客户端本地 tick(≤ PayloadTick,被队列重建 RemoveAll 抹除)与共享 ServerExecSequence=0 的去重键碰撞(只放行第一条命令)。
ObstacleSpawnManifest (the late-join obstacle spawn manifest) was removed on 2026-08-28 — command-routed obstacles are snapshot citizens now (FNetworkTag + synthetic template keys; they ride Entities). The old manifest replay died to two bugs: ExecuteTick restamped manifest commands to the client-local tick (≤ PayloadTick → wiped by the queue-rebuild RemoveAll), and the shared ServerExecSequence=0 dedupe-key collision admitted only the first command.