MassBattle Docsv1.19.20

MassBattleHashFold.h

Source/MassBattle/Public/Network/MassBattleHashFold.h

概述 Overview

共享的确定性 hash 折叠原语,是实体 fragment hash 管线(UMassBattleNetworkSubsystem)与 LockstepVarStore 折叠的单一来源。双端必须逐位一致折叠(锁步),所有函数为 inline,保证只有一份实现、不存在漂移。核心问题:含 float/double 叶的结构中 -0.0 == 0.0 相等但字节不同,哈希会不同——因此折叠前先规范化为临时缓冲(memzero padding + -0.0 → 0.0 + 反规格化数冲零),绝不修改 live 状态。所有混合经 CityHash64 系列完成。另提供写入点冲零助手 SanitizeSyncFloat 与数组路径指纹 MakePathFingerprint(数组无法进入反射驱动的哈希注册表,用滚动指纹让路径折线分叉当 tick 即报)。

Shared deterministic hash folding primitives — the single source of truth for both the per-entity fragment hash pipeline (UMassBattleNetworkSubsystem) and the LockstepVarStore fold. Both peers must fold bit-identically (lockstep), so the functions are inline: exactly one implementation to drift. Core problem: in structs with float/double leaves, -0.0 == 0.0 compares equal but hashes differently — so structs are canonicalized into a temp buffer before folding (memzero padding + -0.0 → 0.0 + subnormals flushed to 0.0), never mutating live state. All mixing goes through the CityHash64 family. The header also provides the write-site flush helper SanitizeSyncFloat and the array path fingerprint MakePathFingerprint (arrays cannot enter the reflection-driven hash registry; the rolling fingerprint makes a diverged path polyline visible at its tick).

FHashFloatLeaf

描述结构体内一个 float/double 叶的偏移。由 BuildHashFloatLeaves 预计算,供 FoldCanonicalizedFloatStruct 折叠时定位并规范化。

Describes the offset of one float/double leaf inside a struct. Precomputed by BuildHashFloatLeaves, consumed by FoldCanonicalizedFloatStruct to locate and canonicalize leaves during folding.

成员变量 Members

名称 Name类型 Type默认值说明 Description
Offsetint320叶相对结构基址的字节偏移。Byte offset of the leaf relative to the struct base.
bIsDoubleboolfalsetrue = double(或 LWC 实数分量),false = float。true = double (or LWC real component), false = float.

函数 Functions

MixBytesInto inline void (uint64& H, const void* Data, int32 Size)

把 Size 字节原始数据折叠进 H:先对数据算 CityHash64 得 FH,再以 H 为种子用 CityHash64WithSeed 混合 FH 本身。双端同一输入必然同一输出。

Folds Size raw bytes into H: first CityHash64 of the data yields FH, then CityHash64WithSeed mixes FH itself seeded with H. Identical input always yields identical output across peers.

Input
参数 Param类型 Type说明 Description
Huint64&累积 hash(引用,就地更新)。Accumulated hash (by reference, updated in place).
Dataconst void*原始字节指针。Raw byte pointer.
Sizeint32字节数。Byte count.
Output

无返回值。副作用:就地修改 H。

No return value. Side effect: mutates H in place.

IsFloatOrDoubleLeaf inline bool (const FProperty* P)

属性是否为 float/double 叶:FFloatProperty、FDoubleProperty 或 FLargeWorldCoordinatesRealProperty(UE 5.6 LWC,即 FVector.X/Y/Z 分量)。

Whether the property is a float/double leaf: FFloatProperty, FDoubleProperty, or FLargeWorldCoordinatesRealProperty (UE 5.6 LWC — the FVector.X/Y/Z components).

Input
参数 Param类型 Type说明 Description
Pconst FProperty*反射属性。Reflected property.
Output

返回是否为 float/double 叶。

Returns whether it is a float/double leaf.

ContainsFloatOrDoubleLeaves inline bool (const UStruct* Struct)

结构(递归)是否含任何 float/double 叶。此类结构折叠前必须规范化(-0.0 → 0.0),因为 -0.0 == 0.0 比较相等但哈希不同。

Whether the struct (recursively) contains any float/double leaf. Such structs must be canonicalized (-0.0 → 0.0) before folding, since -0.0 == 0.0 compares equal but hashes differently.

Input
参数 Param类型 Type说明 Description
Structconst UStruct*结构;经 ::IsValid 校验,无效(含 nullptr)返回 false。The struct; guarded by ::IsValid — an invalid struct (including nullptr) returns false.
Output

返回是否含 float/double 叶(对嵌套结构属性递归)。

Returns whether any float/double leaf exists (recursing into nested struct properties).

BuildHashFloatLeaves inline void (const UStruct* Struct, int32 BaseOffset, TArray<FHashFloatLeaf>& OutLeaves)

收集结构内全部 float/double 叶的偏移(相对 BaseOffset,递归进嵌套结构属性),用于折叠前规范化结构字节。

Collects the offsets of all float/double leaves inside the struct (relative to BaseOffset, recursing into nested struct properties) for pre-fold canonicalization.

Input
参数 Param类型 Type说明 Description
Structconst UStruct*结构;经 ::IsValid 校验,无效(含 nullptr)直接返回。The struct; guarded by ::IsValid — an invalid struct (including nullptr) returns immediately.
BaseOffsetint32基址偏移,叶偏移 = BaseOffset + 属性内部偏移。Base offset; leaf offset = BaseOffset + property internal offset.
OutLeavesTArray<FHashFloatLeaf>&输出叶列表。Output leaf list.
Output

无返回值。副作用:向 OutLeaves 追加叶条目。

No return value. Side effect: appends leaf entries to OutLeaves.

FoldCanonicalizedFloatStruct inline void (uint64& H, const void* Src, int32 Size, const TArray<FHashFloatLeaf>& FloatLeaves)

规范化的结构折叠:先 memzero 512 字节栈缓冲的 padding,拷贝 Size 字节源数据,再按叶列表把 ±0.0 与反规格化数统一冲为零(double 叶 |V| < DBL_MIN、float 叶 |V| < FLT_MIN 均置 0.0——小于 FLT_MIN 的差异语义上即零且经收缩映射永不放大,与 -0.0 同等处理),最后 MixBytesInto。绝不修改 live 状态(-0.0 必须在实例中保留)。Size 越界(>512)触发 checkf。

Canonicalized struct fold: memzero a 512-byte stack buffer's padding, copy Size source bytes, normalize ±0.0 and subnormals to zero per the leaf list (double leaves with |V| < DBL_MIN and float leaves with |V| < FLT_MIN are set to 0.0 — a sub-FLT_MIN difference is semantically zero and, via the contraction mapping, can never amplify, so it is flushed exactly like -0.0), then MixBytesInto. Never mutates live state (-0.0 must persist in the instance). An out-of-range Size (>512) triggers checkf.

Input
参数 Param类型 Type说明 Description
Huint64&累积 hash。Accumulated hash.
Srcconst void*源结构字节(只读)。Source struct bytes (read-only).
Sizeint32结构字节数,必须 ≤ 512。Struct byte count, must be ≤ 512.
FloatLeavesconst TArray<FHashFloatLeaf>&预计算 float 叶列表。Precomputed float leaf list.
Output

无返回值。副作用:就地修改 H。

No return value. Side effect: mutates H in place.

MakePathFingerprint inline uint32 (const TArray<FVector>& Points)

路径折线的滚动指纹:PathPoints 是 TArray,进不了反射驱动的哈希注册表(数组不受支持)。本函数以「点数 + 逐点坐标字节」经 CityHash64 系列折叠出指纹,在写位现算刷新(移动处理器写入路径处、UMassBattleAgentSubsystem 清空路径处、快照恢复校验折叠前的重建处),让路径折线的分叉在其发生的那一 tick 即被共识发现,而非泄漏进移动之后才可见。空数组返回 0。

Rolling fingerprint of a path polyline: PathPoints is a TArray and cannot enter the reflection-driven hash registry (arrays are unsupported). This function folds "point count + per-point coordinate bytes" through the CityHash64 family into a fingerprint, refreshed at the write sites (the move processors when a path is written, UMassBattleAgentSubsystem when the path is cleared, and the restore-validation fold right after rebuilding from the snapshot blob), so a diverged polyline becomes visible at ITS tick instead of after leaking into movement. An empty array returns 0.

Input
参数 Param类型 Type说明 Description
Pointsconst TArray<FVector>&路径点数组(FNavigating::PathPoints),指纹写入 FNavigating::PathFingerprint。Path point array (FNavigating::PathPoints); the fingerprint is stored into FNavigating::PathFingerprint.
Output

返回 32 位指纹(64 位 CityHash 结果折叠 H ^ (H >> 32));空数组返回 0。无副作用。

Returns a 32-bit fingerprint (the 64-bit CityHash result folded via H ^ (H >> 32)); 0 for an empty array. No side effects.

SanitizeSyncFloat inline float (float V)

写入点冲零助手(float 重载):|V| < FLT_MIN 时返回 0。供穿零积分器(速度 XY 指数摩擦、角速度抑制、tilt Slerp 衰减)在写回同步状态时调用,使状态本身双端逐位一致,而非仅哈希失明。语义无损:小于 FLT_MIN 的值与零处于每个阈值的同侧,放大器扫描未发现未防护除法。

Write-site flush helper (float overload): returns 0 when |V| < FLT_MIN. Called by integrators that converge through zero (velocity XY exponential friction, angular-velocity suppression, tilt Slerp decay) when writing synced state, so the state itself is bit-identical between peers, not merely hash-blind. Semantically lossless: sub-FLT_MIN values sit on the same side of every threshold as zero, and the amplifier scan found no unguarded division.

Input
参数 Param类型 Type说明 Description
Vfloat待写入同步状态的浮点值。Floating-point value about to be written into synced state.
Output

返回 V 本身;|V| < FLT_MIN 时返回 0(-0.0 亦满足该条件,一并冲为 +0.0)。

Returns V, or 0 when |V| < FLT_MIN (-0.0 also satisfies the condition and is flushed to +0.0).

SanitizeSyncFloat inline double (double V)

写入点冲零助手(double 重载):|V| < double(FLT_MIN) 时返回 0.0。注意阈值是 FLT_MIN 而非 DBL_MIN——「先 sanitize 后转 float」的写入位若按 DBL_MIN 冲零,(FLT_MIN, DBL_MIN) 带会在 FTZ 端被硬件冲零、精确端保留,恰成刀口;按 FLT_MIN 冲零后这类写入位双端逐位一致。

Write-site flush helper (double overload): returns 0.0 when |V| < static_cast<double>(FLT_MIN). Note the threshold is FLT_MIN, not DBL_MIN — at write sites that sanitize-then-cast-to-float, flushing only below DBL_MIN would leave the (FLT_MIN, DBL_MIN) band alive, which the cast then cleaves (an FTZ-mode peer's SSE flushes the subnormal float while the exact peer keeps it); flushing below FLT_MIN makes every such write bit-identical on both peers.

Input
参数 Param类型 Type说明 Description
Vdouble待写入(或写入前将转 float)的双精度值。Double value about to be written (or cast to float before writing).
Output

返回 V 本身;|V| < FLT_MIN(按 double 比较)时返回 0.0。

Returns V, or 0.0 when |V| < FLT_MIN (compared as double).

管线角色 Pipeline Role

本文件无反射类型、无 .cpp,全部为 inline,被两处消费: ① LockstepVarStore 折叠管线(LockstepVarStore.cpp):ClassifyProperty 用 ContainsFloatOrDoubleLeaves 判定 CanonicalizeFloatStruct 分类并用 BuildHashFloatLeaves 预计算叶(85-89 行);FoldInto 的 CanonicalizeFloatStruct 分支调 FoldCanonicalizedFloatStruct、其余分支调 MixBytesInto(348-404 行)。 ② 实体 fragment hash 管线(MassBattleNetworkSubsystem.cpp):GetHashFieldPolicy 用 ContainsFloatOrDoubleLeaves 判定字段分类(565 行),hash 布局折叠体 using namespace MassBattleHashFold(576 行)。UMassBattleSubsystem::FoldVarStoresIntoHash 用 MixBytesInto 对模板键作盐(612 行)。实体 hash 的逐字段折叠宏亦引用 BuildHashFloatLeaves(MassBattleMacros.h 144 行)。 hash 管线总体由 UMassBattleNetworkProcessor 每 tick 折叠、UMassBattleNetworkSubsystem 逐 tick 广播对比(失步检测)并参与快照捕获/恢复校验——见 MassBattleNetworkSubsystem.html。 ③ MakePathFingerprint / SanitizeSyncFloat 在写位消费:两个移动处理器(MassBattleAgentMoveProcessor.cpp / MassBattleAgentMoveProcessorFP.cpp)在写入/清空 FNavigating::PathPoints 时调 MakePathFingerprint,在写 FMoving::CurrentVelocity / CurrentAngularVelocity 与 tilt Slerp 结果时调 SanitizeSyncFloat;UMassBattleAgentSubsystem.cpp 清空路径时刷新指纹;MassBattleNetworkSubsystem.cpp 在恢复校验折叠前从 blob 重建的数组现算指纹。

No reflected types and no .cpp — everything is inline, consumed by two pipelines: ① The LockstepVarStore fold pipeline (LockstepVarStore.cpp): ClassifyProperty uses ContainsFloatOrDoubleLeaves to decide the CanonicalizeFloatStruct category and BuildHashFloatLeaves for precomputed leaves (lines 85-89); FoldInto's CanonicalizeFloatStruct branch calls FoldCanonicalizedFloatStruct, all others MixBytesInto (lines 348-404). ② The entity fragment hash pipeline (MassBattleNetworkSubsystem.cpp): GetHashFieldPolicy uses ContainsFloatOrDoubleLeaves to classify fields (line 565); the hash layout fold body does using namespace MassBattleHashFold (line 576). UMassBattleSubsystem::FoldVarStoresIntoHash salts template keys with MixBytesInto (line 612). The per-field fold macros also reference BuildHashFloatLeaves (MassBattleMacros.h line 144). The overall hash pipeline is folded per tick by UMassBattleNetworkProcessor, broadcast and compared per tick by UMassBattleNetworkSubsystem (desync detection), and participates in snapshot capture/restore validation — see MassBattleNetworkSubsystem.html. ③ MakePathFingerprint / SanitizeSyncFloat are consumed at write sites: both move processors (MassBattleAgentMoveProcessor.cpp / MassBattleAgentMoveProcessorFP.cpp) call MakePathFingerprint whenever they write or clear FNavigating::PathPoints, and call SanitizeSyncFloat when writing FMoving::CurrentVelocity / CurrentAngularVelocity and the tilt Slerp result; UMassBattleAgentSubsystem.cpp refreshes the fingerprint on path clear; MassBattleNetworkSubsystem.cpp recomputes it from the blob-restored array before the restore-validation fold.