UMassBattleNetworkRelay
Source/MassBattle/Public/Network/MassBattleNetworkRelay.h
概述 Overview
独立网络中继层,兼容 ListenServer(GameMode 承载)与 DedicatedServer(无头中继)。ListenServer 模式下指令经 UMassBattleSubsystem(客户端入队/出队)+ GameMode(服务端调度/广播)流转,Relay 被动挂载——仅提供 DedicatedServer 的每 tick hash 投票(FMassBattleHashAggregation 聚合各客户端上报 hash,判定 Consensus/Majority/Split/Timeout)、快照存储(灾备上传)与未来的自主时钟(Tick,当前仅 DedicatedServer 推进)。由 AMassBattleNetworkGameModeBase::BeginPlay 创建并按 GetNetMode() 初始化模式。
Independent network forwarding layer, working for both ListenServer (GameMode-hosted) and DedicatedServer (headless relay). In ListenServer mode, commands flow through UMassBattleSubsystem (client enqueue/dequeue) + GameMode (server scheduling/broadcast); the Relay hangs passively — it provides only DedicatedServer per-tick hash voting (FMassBattleHashAggregation aggregates client-reported hashes into Consensus/Majority/Split/Timeout verdicts), snapshot storage (disaster-recovery uploads), and the future autonomous clock (Tick, currently advanced only in DedicatedServer mode). Created by AMassBattleNetworkGameModeBase::BeginPlay and initialized per GetNetMode().
继承 Inheritance
EMassBattleNetworkMode UENUM
网络模式枚举,决定 Relay 的活跃行为。
Network mode enum deciding the relay's active behavior.
| 值 Value | 说明 Description |
|---|---|
Off | 单机模式(Standalone),无中继行为。Off (Standalone) — no relay behavior. |
ListenServer | 监听服务器(主机 + 模拟),Relay 被动——时钟由 Subsystem 驱动。Listen Server (host + simulation); relay passive — clock driven by the Subsystem. |
DedicatedServer | 专用服务器(仅中继,无模拟),Relay 自主时钟驱动投票与快照存储。Dedicated Server (relay only, no simulation); the relay's autonomous clock drives voting and snapshot storage. |
结构体 Structs
单个 tick 的 hash 投票聚合状态(普通 C++ struct,非反射)。客户端经 ReportClientHash 上报,超时后 Evaluate 判定结果;Consensus/Majority 记录共识 hash 与少数派列表。
Hash-voting aggregation state for a single tick (plain C++ struct, not reflected). Clients report via ReportClientHash; after the timeout Evaluate decides the verdict; Consensus/Majority record the agreed hash and the minority list.
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
TickCount | int32 | 0 | 投票对应 tick。Tick being voted on. |
ClientHashes | TMap<int32, uint64> | 空 | PlayerIndex → 上报 hash。PlayerIndex → reported hash. |
AggregationTimeout | double | 0.5 | 收齐投票的超时(秒)。Timeout to collect all votes (seconds). |
FirstReportTime | double | 0.0 | 首个上报的世界时间(超时起算锚点)。World time of the first report (timeout anchor). |
Result | EResult | Waiting | 投票结果(见下方枚举)。Voting result (see the enum below). |
AgreedHash | uint64 | 0 | 共识/多数派 hash。Consensus/majority hash. |
WrongClients | TArray<int32> | 空 | 需重同步的少数派客户端列表。Minority clients to resync. |
枚举 EResult
| 值 Value | 说明 Description |
|---|---|
Waiting | 仍在收票。Still collecting. |
Consensus | 全部客户端一致。All agree. |
Majority | 找到多数派。Majority found. |
Split | 无多数派(如 2v2)。No majority (e.g. 2v2). |
Timeout | 未收齐全部上报。Not all reports received. |
评估投票结果:TotalClientCount ≤ 0 → Timeout;仅一个 hash 且票数等于总数 → Consensus;多数票 ≥ 总数/2+1 → Majority(收集其它 hash 的投票者为 WrongClients);否则 Split(全部客户端入 WrongClients,调用方递增 FullResyncCount)。
Evaluates the voting outcome: TotalClientCount ≤ 0 → Timeout; a single hash with votes equal to the total → Consensus; the top vote count ≥ total/2+1 → Majority (voters of other hashes become WrongClients); otherwise Split (all clients go into WrongClients; the caller increments FullResyncCount).
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
TotalClientCount | int32 | 参与投票的客户端总数(≤0 直接判 Timeout)。Total client count in the vote (≤0 → Timeout). |
Output
无返回值。副作用:设置 Result/AgreedHash/WrongClients。
No return value. Side effects: sets Result/AgreedHash/WrongClients.
广播给客户端的共识 hash 条目(tick + hash 对)。
Agreed-hash entry broadcast to clients (tick + hash pair).
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
TickCount | int32 | 0 | 共识 tick。Agreed tick. |
AgreedHash | int64 | 0 | 共识 hash。Agreed hash. |
快照历史条目(普通 C++ struct):压缩快照字节 + 是否属于共识 tick 的标记。
Snapshot history entry (plain C++ struct): compressed snapshot bytes plus an agreed-tick marker.
成员变量 Members
| 名称 Name | 类型 Type | 默认值 | 说明 Description |
|---|---|---|---|
TickCount | int32 | 0 | 快照对应 tick。Tick of the snapshot. |
SnapshotData | TArray<uint8> | 空 | 压缩快照字节。Compressed snapshot bytes. |
bAgreed | bool | false | 该 tick 是否为共识 tick。Whether the tick is an agreed tick. |
方法 Methods
初始化:记录模式与世界,清空全部 DedicatedServer 状态(投票聚合、共识历史、快照历史、挂起上传、期望客户端数 ExpectedClientCount)。DedicatedServer 模式下同时重置自主时钟(CurrentTick/SimulationTime/TimeAccumulator 归零)。
Initialization: records the mode and world, clears all DedicatedServer state (voting aggregations, agreed history, snapshot history, pending uploads, and the expected client count ExpectedClientCount). In DedicatedServer mode the autonomous clock (CurrentTick/SimulationTime/TimeAccumulator) is also reset to zero.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
InMode | EMassBattleNetworkMode | 网络模式。Network mode. |
InWorld | UWorld* | 世界(供世界时间查询)。World (for world-time queries). |
Output
无返回值。副作用:设置模式、清空状态、重置时钟。
No return value. Side effects: mode set, state cleared, clock reset.
自主时钟(仅 DedicatedServer 活跃;ListenServer 下立即返回,时钟由 Subsystem 驱动)。累计 TimeAccumulator 达到 TickInterval 即推进一个 tick:CurrentTick++、SimulationTime += TickInterval,随后 EvaluateHashVoting 评估待处理投票。
Autonomous clock (active in DedicatedServer mode only; ListenServer returns immediately — the clock is driven by the Subsystem). Advances one tick once TimeAccumulator reaches TickInterval: CurrentTick++, SimulationTime += TickInterval, then EvaluateHashVoting runs.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
DeltaTime | float | 帧时间(秒),累计进 TimeAccumulator。Frame delta time in seconds, accumulated into TimeAccumulator. |
Output
无返回值。副作用:推进自主 tick 与模拟时间、评估 hash 投票。
No return value. Side effects: autonomous tick/sim-time advance, hash-voting evaluation.
自主时钟访问器:GetCurrentTick 返回当前自主 tick,GetSimulationTime 返回累计模拟时间,SetTickInterval 覆盖 TickInterval(默认 0.0333s = 30Hz)。当前仅 DedicatedServer 推进。
Autonomous clock accessors: GetCurrentTick returns the current autonomous tick, GetSimulationTime the accumulated simulation time, and SetTickInterval overrides TickInterval (default 0.0333s = 30Hz). Advanced only in DedicatedServer mode today.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
InInterval | float | 新 tick 间隔(秒),仅 SetTickInterval。New tick interval in seconds, SetTickInterval only. |
Output
GetCurrentTick/GetSimulationTime 返回值;Set 版本无返回值,副作用为覆盖间隔。
GetCurrentTick/GetSimulationTime return values; the Set version returns nothing and mutates the interval.
接收客户端 tick hash(仅 DedicatedServer;其它模式忽略)。在 HashAggregations 中按 tick 聚合(FindOrAdd),记录首个上报的世界时间作为超时锚点。由 AMassBattleNetworkGameModeBase::ReportClientHash 转发。
Receives a client tick hash (DedicatedServer only; ignored otherwise). Aggregates by tick into HashAggregations (FindOrAdd), recording the first-report world time as the timeout anchor. Forwarded by AMassBattleNetworkGameModeBase::ReportClientHash.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
PlayerIndex | int32 | 上报玩家。Reporting player. |
TickCount | int32 | hash 对应 tick。Tick of the hash. |
Hash | uint64 | 客户端模拟 hash。Client simulation hash. |
Output
无返回值。副作用:写入聚合状态(仅 DedicatedServer)。
No return value. Side effect: aggregation-state write (DedicatedServer only).
评估所有超时的 Waiting 聚合(HashAggregationTimeout 起算于首报时间)。法定人数基准 = ExpectedClientCount(>0 时),否则回退为实收报告数——传报告数会使 Consensus 检查变成自比较(单个上报者恒真),单个客户端即可伪造共识(审计 M8)。Consensus/Majority → 更新 AgreedHash/LastAgreedTick/TickHashHistory(Majority 递增 MinorityResyncCount);Split → FullResyncCount++;Timeout → 保守处理:期望上报者未齐、部分法定人数不产生共识,直接 FullResyncCount++(不再以已报告者重新评估)。完成后移除已处理聚合;TickHashHistory 超过 2 × ConsensusHashHistoryLength 时按 CurrentTick - ConsensusHashHistoryLength 阈值裁剪。
Evaluates every timed-out Waiting aggregation (HashAggregationTimeout anchored at the first report). The quorum basis is ExpectedClientCount when > 0, falling back to the reporter count — passing the reporter count made the Consensus check self-comparing (trivially true for a lone reporter) and let a single client fabricate consensus (audit M8). Consensus/Majority → updates AgreedHash/LastAgreedTick/TickHashHistory (Majority also increments MinorityResyncCount); Split → FullResyncCount++; Timeout → conservative: not all expected reporters answered, a partial quorum yields no consensus, straight to FullResyncCount++ (no re-evaluation with the reporters). Handled aggregations are removed; when TickHashHistory exceeds 2 × ConsensusHashHistoryLength it is pruned by the CurrentTick - ConsensusHashHistoryLength threshold.
Input
无参数。
No parameters.
Output
无返回值。副作用:更新共识状态与计数器、清理聚合、裁剪历史。
No return value. Side effects: consensus-state and counter updates, aggregation cleanup, history pruning.
设置 hash 投票的期望在线客户端数(写入前经 FMath::Max 钳制到 ≥0)。投票法定人数以此为基准而非实收报告数——传报告数会使 Consensus 检查变成自比较(单个上报者恒真),单个客户端即可伪造共识(审计 M8)。由 AMassBattleNetworkGameModeBase 在 Login / Logout 时以 PlayerSlotToPlayerId.Num() 调用,保持法定人数与在线数同步。
Sets the expected connected-client count for hash voting (clamped to ≥0 via FMath::Max before writing). Voting is decided against THIS, not the number of reporters — passing the reporter count made the Consensus check self-comparing (trivially true for a lone reporter) and let a single client fabricate consensus (audit M8). Called by AMassBattleNetworkGameModeBase on Login / Logout with PlayerSlotToPlayerId.Num() to keep the quorum in sync with the connected count.
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
InCount | int32 | 期望在线客户端数(负值会被钳为 0)。Expected connected-client count (negative values clamp to 0). |
Output
无返回值。副作用:写入 ExpectedClientCount;为 0(未设置)时 EvaluateHashVoting 回退用实收报告数作法定人数。
No return value. Side effect: writes ExpectedClientCount; when 0 (unset), EvaluateHashVoting falls back to the reporter count as the quorum.
存储客户端上传的压缩快照(SnapshotHistory,标记是否共识 tick)。仅 DedicatedServer 接受——其它模式(含 ListenServer)直接返回。双重内存上界(审计 H6):① 总字节预算——已存字节总和(同 tick 覆盖写先扣除旧载荷)加上新上传超过 MaxSnapshotHistoryBytes 时,拒绝本次上传并输出警告日志;② 条目数上界——历史超过 SnapshotHistoryLength 时按「最旧优先」裁剪到上限(与自主时钟无关,替代旧的按 CurrentTick 阈值裁剪——旧裁剪在自主时钟未推进时永不触发,导致无界增长)。
Stores a client-uploaded compressed snapshot (SnapshotHistory, marking agreed ticks). DedicatedServer only — other modes (including ListenServer) return immediately. Two memory bounds (audit H6): ① total byte budget — when stored bytes (a same-tick overwrite deducts the old payload first) plus the new upload exceed MaxSnapshotHistoryBytes, the upload is rejected with a warning log; ② entry cap — when history exceeds SnapshotHistoryLength, the oldest entries are pruned down to the cap (clock-agnostic, replacing the old CurrentTick-threshold prune, which never fired when the autonomous clock had not advanced and let history grow unboundedly).
Input
| 参数 Param | 类型 Type | 说明 Description |
|---|---|---|
TickCount | int32 | 快照对应 tick。Tick of the snapshot. |
CompressedSnapshot | const TArray<uint8>& | 压缩快照字节。Compressed snapshot bytes. |
Output
无返回值。副作用:写入快照历史、可能拒绝上传(预算超限)、裁剪最旧条目。
No return value. Side effects: snapshot-history write, possible rejection (over budget), oldest-entry pruning.
成员变量 Members
网络模式,默认 Off。由 Initialize 按 GetNetMode() 设置。
Network mode, default Off; set by Initialize per GetNetMode().
DedicatedServer 自主时钟间隔(秒),默认 0.0333(30Hz)。
DedicatedServer autonomous tick interval in seconds, default 0.0333 (30Hz).
共识 hash 日志的历史深度,默认 256(裁剪触发于 2× 深度)。
Tick history depth for the consensus hash log, default 256 (pruning triggers at 2× depth).
快照历史深度(DedicatedServer),默认 300(30Hz 下约 10 秒)。超过时按「最旧优先」裁剪到上限。
Snapshot history depth (DedicatedServer), default 300 (~10 seconds at 30Hz). Pruned oldest-first down to the cap when exceeded.
已存(已上传)快照的总字节预算(Dedicated 模式),默认 256 MB。超预算的上传被 UploadSnapshot 拒绝并输出警告日志——无论上传速率如何都限制内存(审计 H6)。
Total byte budget for stored (uploaded) snapshots (Dedicated mode), default 256 MB. Uploads beyond the budget are rejected by UploadSnapshot with a warning log — bounding memory regardless of upload rate (audit H6).
快照上传间隔(tick),默认 30(30Hz 下约 1 秒)。当前为配置位。
Snapshot upload interval in ticks, default 30 (~1 second at 30Hz). Currently a configuration slot.
Hash 聚合超时(秒),默认 0.5。
Hash aggregation timeout in seconds, default 0.5.
hash 投票的期望在线客户端数(仅 DedicatedServer 使用),默认 0。由 GameMode 在登录/登出时经 SetExpectedClientCount 设置;EvaluateHashVoting 以此为法定人数基准,0 = 回退用实收报告数(审计 M8)。
Expected connected-client count for hash-voting quorum (DedicatedServer only), default 0. Set by the GameMode on login/logout via SetExpectedClientCount; EvaluateHashVoting decides the vote against this, 0 = fall back to the reporter count (audit M8).
Tick → 共识 hash(仅 DedicatedServer)。
Tick → consensus hash (DedicatedServer only).
Tick → 投票聚合状态(仅 DedicatedServer)。
Tick → aggregation state (DedicatedServer only).
最新共识 hash、广播用共识条目列表、快速共识 tick 查找集合。
Latest consensus hash, broadcast entries, and the fast consensus-tick lookup set.
快照历史(tick → 条目)与挂起上传(tick → 压缩数据,当前未消费)。
Snapshot history (tick → entry) and pending uploads (tick → compressed data, currently unconsumed).
最近共识 tick 与三类统计:总失步数、少数派重同步数、全量重同步数。
Last agreed tick and three counters: total desyncs, minority resyncs, full resyncs.
缓存世界引用,Initialize 记录,供世界时间查询。
Cached world reference recorded by Initialize, used for world-time queries.
自主时钟状态:DedicatedServer 模式 Tick 推进;ListenServer 模式保持初始值(时钟由 Subsystem 驱动)。
Autonomous clock state: advanced by Tick in DedicatedServer mode; stays at initial values in ListenServer mode (clock driven by the Subsystem).
管线角色 Pipeline Role
创建与模式:AMassBattleNetworkGameModeBase::BeginPlay(bEnableNetworkSync 开启时)NewObject 创建并经 Initialize 按 GetNetMode() 置为 DedicatedServer 或 ListenServer(GameModeBase.cpp:96-101)。
hash 投票(DedicatedServer):客户端 ServerReportHash → GM->ReportClientHash(GameModeBase.cpp:852-859)→ Relay->ReportClientHash 聚合;自主 Tick 超时后 EvaluateHashVoting 产出共识,广播路径 MulticastAgreedHashes(GameStateBase,当前消费为 TODO)。GameMode 在 Login / Logout 时调用 SetExpectedClientCount(PlayerSlotToPlayerId.Num()) 同步法定人数(审计 M8)。
快照灾备(DedicatedServer):客户端 ServerUploadSnapshot → GM->ReceiveSnapshotUpload(入口验证)→ Relay->UploadSnapshot 存储(受 MaxSnapshotHistoryBytes 字节预算与条目数上限双重约束)。
ListenServer 模式:Relay 仅被动存在——指令流不经 Relay(见 MassBattleNetworkGameModeBase.html 管线角色)。
Creation and mode: AMassBattleNetworkGameModeBase::BeginPlay (when bEnableNetworkSync is on) creates the relay via NewObject and Initializes it to DedicatedServer or ListenServer per GetNetMode() (GameModeBase.cpp:96-101).
Hash voting (DedicatedServer): client ServerReportHash → GM->ReportClientHash (GameModeBase.cpp:852-859) → Relay->ReportClientHash aggregation; the autonomous Tick runs EvaluateHashVoting after the timeout; broadcast goes through MulticastAgreedHashes (GameStateBase — consumption currently TODO). The GameMode calls SetExpectedClientCount(PlayerSlotToPlayerId.Num()) on Login / Logout to keep the quorum in sync (audit M8).
Snapshot disaster recovery (DedicatedServer): client ServerUploadSnapshot → GM->ReceiveSnapshotUpload (entry validation) → Relay->UploadSnapshot storage (doubly bounded by the MaxSnapshotHistoryBytes byte budget and the entry cap).
ListenServer mode: the relay exists passively — commands never flow through it (see the pipeline role in MassBattleNetworkGameModeBase.html).