MassBattle Docsv1.19.20

UMassBattleNetworkProcessor

UCLASS Processor

Source/MassBattle/Public/Processors/MassBattleNetworkProcessor.h

概述 Overview

锁步网络的哈希与快照捕获核心。每 tick 用单一统一查询 UnifiedNetworkQuery(以 FNetworkTag 锚定 + Any<大类 tag> 门限,全部 fragment Optional、按 chunk 分派)覆盖全部网络实体类别(Agent / Debuff / FxEntity / Projectile / Loot / Host / Obstacle),逐实体计算注册表驱动的 EntityHash 写入 FNetworking,折叠成帧哈希 LastTickHash 供失步检测;周期性(SnapshotIntervalTicks)把实体运行时状态按快照注册表序列化成 FEntitySnapshotRecord blob,供 resync 恢复。查询结构体(FMassBattleQuery / FEntityQuery)本身也以 wire 格式序列化,保证双端查询配置一致。执行顺序:UID 注册已在生成时完成(BuildEntityDeferRegistered),Execute 内仅做可选审计(默认关闭),再以单个并行波按 chunk 分派大类捕获。

The hash-and-snapshot capture core of the lockstep network. Every tick, a single unified query UnifiedNetworkQuery (anchored on FNetworkTag, gated by Any<main-type tag>, every fragment Optional with per-chunk dispatch) covers all networked entity categories (Agent / Debuff / FxEntity / Projectile / Loot / Host / Obstacle); each entity's registry-driven EntityHash is computed into FNetworking and folded into the frame hash LastTickHash for desync detection. Periodically (SnapshotIntervalTicks), entity runtime state is serialized into FEntitySnapshotRecord blobs per the snapshot registry for resync restore. Query structs (FMassBattleQuery / FEntityQuery) are also serialized to a wire format so both peers share identical query configs. Execution order: UID registration happens at spawn time (BuildEntityDeferRegistered); Execute only runs an optional audit (off by default), then a single parallel wave dispatches per-chunk category capture.

继承 Inheritance

UMassProcessor → UMassBattleNetworkProcessor

公开方法 Public Methods

UMassBattleNetworkProcessor 构造函数

构造函数。设置 bAutoRegisterWithProcessingPhases = false(不注册进 Mass 处理阶段,由 UMassBattleSubsystem::Tick 在帧末派发)、bRequiresGameThreadExecution = true、ExecutionFlags = Standalone | Server | Client。

Constructor. Sets bAutoRegisterWithProcessingPhases = false (dispatched by UMassBattleSubsystem::Tick at end of frame, not in SimStages), bRequiresGameThreadExecution = true, and ExecutionFlags = Standalone | Server | Client.

Input

无参数。

No parameters.

Output

无返回值。副作用:注册为手动派发、游戏线程专用的处理器。

No return value. Side effect: registers as a manually-dispatched, game-thread-only processor.

ConfigureQueries virtual void

初始化唯一的统一实体查询 UnifiedNetworkQuery(见成员变量)。组合:All<FNetworkTag>(锚定——所有锁步实体终身持有、从不移除)+ Any<FAgentTag, FDebuffTag, FBatchedFxTag, FProjectileTag, FLootTag, FHostControllerTag, FObstacleTag>(大类门限——与旧六个分类查询的 tag 锚点精确同集,匹配集零漂移,无大类 tag 的实体被挡在查询外)+ All<FNetworking>(MARW) + 手写 Optional 并集:身份/样式(FEntityFlagFragment / FSlowDebuff / FTemporalDmgDebuff / FFxVisualizing / FStyleType)、弹丸(FProjectileParams / FProjectileParamsRT + 三个移动变种,archetype 上互斥)、loot(FLootParams / FLootParamsRT + 三个移动变种,互斥)、host(FHostHierarchy + 六个 config fragment)、障碍物(FObstacleNetBinding / FBoxObstGridData / FCapsuleObstGridData / FSegmentObstGridData / FSplineObstacle / FCollider,三个互斥原型按网格数据片段逐 chunk 分派),以及经 MB_FOR_EACH_SNAPSHOT_AGENT_FRAGMENT(MB_SNAP_QUERY_OPTIONAL) 注册的全部 agent 注册表行(Optional-ReadWrite)。互斥变种靠 per-chunk View.Num()>0 选择活动分支——视图非空依赖组合不变量(该大类的 fragment 存在于其每个 archetype),违反时在分支无守卫索引处崩溃而非静默掉出同步。统一查询 + 单并行波取代旧六个分类查询的六次顺序并行波(每波调度+屏障开销,小类别在波尾让 worker 闲置)。末尾调用 Net->PreBuildSnapshotLayoutCache() 与 PreBuildHashLayoutCache() 预构建布局缓存;bEnablePerEntityHashDiag 开启时布防 UID 反查失败诊断门(DiagUIDFailGate)。

Initializes the single unified entity query UnifiedNetworkQuery (see members). Composition: All<FNetworkTag> (the anchor — every lockstep entity carries it for life, never removed) + Any<FAgentTag, FDebuffTag, FBatchedFxTag, FProjectileTag, FLootTag, FHostControllerTag, FObstacleTag> (the main-type gate — the exact union of the legacy six per-category queries' tag anchors, so the match set is unchanged and entities without a main-type tag stay out) + All<FNetworking>(MARW) + a hand-written Optional union: identity/style rows (FEntityFlagFragment / FSlowDebuff / FTemporalDmgDebuff / FFxVisualizing / FStyleType), projectile rows (FProjectileParams / FProjectileParamsRT + three mutually exclusive moving variants per archetype), loot rows (FLootParams / FLootParamsRT + three mutually exclusive moving variants), host rows (FHostHierarchy + the six config fragments), obstacle rows (FObstacleNetBinding / FBoxObstGridData / FCapsuleObstGridData / FSegmentObstGridData / FSplineObstacle / FCollider — three disjoint archetypes dispatched per chunk by the grid-data fragment), plus every agent registry row registered through MB_FOR_EACH_SNAPSHOT_AGENT_FRAGMENT(MB_SNAP_QUERY_OPTIONAL) (Optional-ReadWrite). Mutually exclusive variants select the active branch by per-chunk View.Num()>0 — view non-emptiness rests on the composition invariant (a category's fragments exist on every archetype of that category); a violation crashes loudly at the branch's unguarded indexing instead of silently dropping entities out of sync. One query + one parallel wave replaces the legacy six queries and their six sequential parallel waves (per-wave dispatch + barrier overhead; small categories left workers idle at each wave tail). Ends by calling Net->PreBuildSnapshotLayoutCache() and PreBuildHashLayoutCache(); when bEnablePerEntityHashDiag is on it also arms the UID-lookup failure diag gate (DiagUIDFailGate).

Input
参数 Param类型 Type说明 Description
EntityManagerconst TSharedRef<FMassEntityManager>Mass 实体管理器,查询初始化目标。Mass entity manager; query initialization target.
Output

无返回值。副作用:统一查询注册进处理器;预构建快照/哈希布局缓存;bEnablePerEntityHashDiag 开启时布防 UID 反查诊断门。

No return value. Side effects: the unified query registered; snapshot/hash layout caches pre-built; UID-lookup diag gate armed when bEnablePerEntityHashDiag is on.

Execute virtual void

每 tick 执行入口。流程:① 取 UMassBattleSubsystem,检查 ESubFrame::Subtick3 子帧门控;② 取 UMassBattleNetworkSubsystem,计算快照门控(bForceSnapshotNextTick / ForceSnapshotEveryTickRemaining 强制,或按 SnapshotIntervalTicks 周期),命中时 Reset() 快照缓冲并预分配;③ 每 tick Reset() 哈希收集器(成员地址稳定保证 TLS 缓存正确失效);④ UID 注册审计(可选):UID 注册已前移至生成时(BuildEntityDeferRegistered——注册 SetCommand 与构建命令同缓冲,引擎固定类型序 Create < Set 保证仅在构建后执行,"可见 ⇔ 已注册"结构性成立);本 pass 不再补写,仅在 bAuditUIDRegistration(默认关)开启时对统一查询串行检测缺口(跳过 agent chunk——agent 在 spawn 时注册),漏报打 [UID_REG_MISS] Error;WITH_EDITOR 下另有节流的字段注册审计(见 AuditFieldRegistration);⑤ 单分派波(并行):对统一查询一次遍历,每 chunk 一次 DoesArchetypeHaveTag 分派到七个类别块(Agent / Debuff / Host / FxEntity / Projectile / Loot / Obstacle)——chunk 是同质 archetype 切片,零逐实体开销,一个并行波取代旧六次顺序波;类别块沿用旧各查询逻辑(字段集/折叠顺序/捕获策略不变)。其中 Agent 块:注册表驱动逐实体 hash 折叠(顺序由注册表固定),写 FNetworking.EntityHash,收集 FEntityNetworkEntry;快照时刻捕获 FEntitySnapshotRecord(TemplateKey / UniqueID / TagDelta / FlagsLow+FlagsHigh / 各 fragment blob,blob 用原始数据不做规范化——-0.0f 必须保留,否则恢复时物理分叉);⑥ 其余类别块:Debuff / FxEntity / Projectile / Loot 各自 chunk-has 变种分派后做同样的 hash + 快照捕获;Projectile 的伤害/减益变种与 Loot 的 FLootVisualizing 走 per-entity GetFragmentPtr;Obstacle 块按 Box / Capsule / Spline 网格数据片段(chunk 视图非空)分派三种互斥障碍物原型做 hash + 快照;⑦ Fold:Merge() 合并各线程槽 → bDeterministic 时按 (Category, UniqueID) 排序 → 逐条 CityHash64WithSeed 折叠(类别盐,同 UID 跨类别可区分)→ MB.FoldVarStoresIntoHash(模板实例按键排序、各自加盐)→ 快照时刻 CaptureVarStoreBlobs → 写 Net.LastTickHash;⑧ bLogPerEntityHashes 时把本 tick 逐实体条目拷入 PerEntityHashHistory(上限 128 tick)并 Editor 下逐条打 [HASH_ENT] 日志。

Per-tick entry. Flow: ① resolve UMassBattleSubsystem, gate on ESubFrame::Subtick3; ② resolve UMassBattleNetworkSubsystem, compute the snapshot gate (bForceSnapshotNextTick / ForceSnapshotEveryTickRemaining force, or periodic via SnapshotIntervalTicks), and Reset() + pre-reserve the snapshot buffer when capturing; ③ Reset() the hash gatherer every tick (stable member address keeps TLS cache invalidation correct); ④ UID registration audit (optional): UID registration has moved to spawn time (BuildEntityDeferRegistered — the registration SetCommand shares the build's command buffer and the engine's fixed command-type order Create < Set guarantees it runs only after the build, making "visible ⇔ registered" structural); this pass no longer heals — it only DETECTS gaps over the unified query while bAuditUIDRegistration is on (default off), skipping agent chunks (agents register at spawn) and logging [UID_REG_MISS] Error on a miss; in WITH_EDITOR builds a throttled field-registration audit also runs (see AuditFieldRegistration); ⑤ single dispatch wave (parallel): one pass over the unified query, one DoesArchetypeHaveTag test per chunk dispatching into seven category blocks (Agent / Debuff / Host / FxEntity / Projectile / Loot / Obstacle) — chunks are homogeneous archetype slices, zero per-entity cost, one parallel wave replacing the legacy six sequential waves; category bodies carry over the legacy per-query logic verbatim (field sets / fold orders / capture policies unchanged). The Agent block: registry-driven per-entity hash fold (order fixed by the registry) into FNetworking.EntityHash, collecting FEntityNetworkEntry; on snapshot ticks, capture FEntitySnapshotRecord (TemplateKey / UniqueID / TagDelta / FlagsLow+FlagsHigh / per-fragment blobs — blobs use raw entity data, no canonicalization: -0.0f must survive, otherwise restore diverges physics); ⑥ remaining category blocks: Debuff / FxEntity / Projectile / Loot do the same hash + snapshot capture with per-chunk variant dispatch; projectile damage/debuff variants and FLootVisualizing go through per-entity GetFragmentPtr; the Obstacle block dispatches the three mutually exclusive obstacle archetypes (Box / Capsule / Spline) by the grid-data fragment (non-empty chunk view) for the same hash + snapshot; ⑦ Fold: Merge() slots → sort by (Category, UniqueID) when bDeterministic → fold each entry via CityHash64WithSeed (category salt keeps equal UIDs across categories distinct) → MB.FoldVarStoresIntoHash (template instances sorted by key, each salted) → on snapshot ticks CaptureVarStoreBlobs → write Net.LastTickHash; ⑧ when bLogPerEntityHashes, copy this tick's per-entity entries into PerEntityHashHistory (capped at 128 ticks) and log [HASH_ENT] per entity in Editor builds.

Input
参数 Param类型 Type说明 Description
EntityManagerFMassEntityManager&实体管理器。Entity manager.
ContextFMassExecutionContext&执行上下文,提供 fragment 视图与实体遍历。Execution context providing fragment views and entity iteration.
Output

无返回值。副作用:写入全部命中实体的 FNetworking.EntityHash;填充 Net.NetworkHashEntries / Net.EntitySnapshotBuffer(快照 tick)/ Net.PendingVarStoreSnapshot;更新 Net.LastTickHash 与 Net.PerEntityHashHistory;审计开启时输出诊断日志。

No return value. Side effects: writes FNetworking.EntityHash on every matched entity; fills Net.NetworkHashEntries / Net.EntitySnapshotBuffer (on snapshot ticks) / Net.PendingVarStoreSnapshot; updates Net.LastTickHash and Net.PerEntityHashHistory; emits diagnostic logs when an audit is enabled.

成员变量 Members

UnifiedNetworkQuery FMassEntityQuery (private)

全部网络大类的单一实体查询(Agent / Debuff / FxEntity / Projectile / Loot / Host / Obstacle)。以 FNetworkTag 锚定(所有锁步实体终身持有、从不移除),Any<大类 tag> 门限与旧六个分类查询的 tag 锚点精确同集(匹配集零漂移);全部 fragment 注册为 Optional,捕获时每 chunk 一次 DoesArchetypeHaveTag 分派类别分支。FNetworking 可写(MARW)。取代旧的 AgentNetworkQuery / DebuffNetworkQuery / FxEntityNetworkQuery / ProjectileNetworkQuery / LootNetworkQuery / HostNetworkQuery 六个分类查询。

The single entity query for ALL networked categories (Agent / Debuff / FxEntity / Projectile / Loot / Host / Obstacle). Anchored on FNetworkTag (every lockstep entity carries it for life, never removed); the Any<main-type tag> gate is the exact union of the legacy six per-category queries' tag anchors (zero match-set drift); every fragment registers as Optional, and capture dispatches the category branch per chunk with one DoesArchetypeHaveTag test. FNetworking is writable (MARW). Replaces the legacy six per-category queries AgentNetworkQuery / DebuffNetworkQuery / FxEntityNetworkQuery / ProjectileNetworkQuery / LootNetworkQuery / HostNetworkQuery.

AuditFieldRegistration void (private, WITH_EDITOR)

编辑器专属的字段注册审计 pass(打包构建整体剔除)。由 bDisableFieldRegistrationAudit 门控、FieldAuditIntervalTicks(默认 32)节流,仅网络模式执行;串行、纯诊断、绝不写片段(锁步中立)。对统一查询每 chunk 取一个代表实体(chunk 同质),每轮每片段类型只采一例(集合去重),把 archetype 上的 fragment 与快照/哈希注册表比对:(a) 整片段缺失——存在于网络实体但双注册表皆无且未豁免——立即打 [FIELD_REG_AUDIT] Error;(b) 已注册片段遍历全部 UPROPERTY,对未注册字段做影子比较(仅当本轮与上轮采样同一实体才比较,避免代表实体跨 chunk 漂移造成幻影"变更"),运行时被改但未注册同样打 Error。报告附带修复指引(在 MassBattleSnapshotRegistry.h 的匹配大类组加 SYNC 注册行,或加入豁免清单)。

Editor-only field-registration audit pass (compiled out in packaged builds). Gated by bDisableFieldRegistrationAudit, throttled by FieldAuditIntervalTicks (default 32), networked mode only; serial, diagnostics-only, never writes fragments (lockstep-neutral). Walks one representative entity per chunk over the unified query (chunks are homogeneous) with one sample per fragment type per pass (set-dedup), comparing each archetype fragment against the snapshot/hash registries: (a) whole-fragment gaps — present on a networked entity, absent from BOTH registries and not excluded — report an immediate [FIELD_REG_AUDIT] Error; (b) for registered fragments, every UPROPERTY is walked and unregistered fields are shadow-compared (compared only when this pass sampled the SAME entity as the last, so flapping representatives don't produce phantom "changes"); a runtime change to an unregistered field also logs an Error. Reports include fix guidance (add a SYNC line to the matching category group in MassBattleSnapshotRegistry.h, or list as excluded).

管线角色 Pipeline Role

不在 SimStages 中,由 UMassBattleSubsystem::Tick() 在 post-sim 队列变异(子系统 ExecuteTick / ProcessEventQueues / 回收 / 销毁 / FlushCommandBuffer)之后派发,使 hash+快照反映真正的帧末状态。只跑 Subtick3 子帧(每逻辑 tick 一次)。与 UMassBattleNetworkSubsystem 配合:前者收集 NetworkHashEntries(→ LastTickHash)与 EntitySnapshotBuffer,后者负责跨端比较哈希、在失步时把快照发给对端并重建实体(恢复端 helper RestoreMassBattleQueryBytes / RestoreEntityQueryBytes 的字节格式必须与本文件捕获端严格镜像,漂移由 ReadOffset 尾部校验兜底)。

Not in SimStages — dispatched by UMassBattleSubsystem::Tick() AFTER post-sim queue mutation (subsystem ExecuteTick / ProcessEventQueues / recycle / destroy / FlushCommandBuffer) so hash + snapshot reflect the true end-of-frame state. Runs only on the Subtick3 sub-frame (once per logic tick). Pairs with UMassBattleNetworkSubsystem: this processor fills NetworkHashEntries (→ LastTickHash) and EntitySnapshotBuffer; the subsystem compares hashes across peers and, on desync, sends the snapshot to rebuild entities (restore-side helpers RestoreMassBattleQueryBytes / RestoreEntityQueryBytes must byte-mirror this file's capture side; drift is caught by the ReadOffset tail check).