MassBattle Docsv1.19.20

FMassBattleSnapshotTcpTransport

Source/MassBattle/Public/Network/MassBattleSnapshotTcpTransport.h

概述 Overview

FEntitySnapshotPayload 传输的原始 TCP 侧通道(纯 C++ 类,非 UObject),替代基于 RPC 的快照发送以避免 DataChannel !IsBunchTooLarge 限制。应用层帧协议:每帧 = uint32 大小前缀 + 二进制头部(FMassBattleSnapshotTcpHeader,Magic "MBTS" + Version 1)+ 载荷;1 MiB 分块传输、收端重组(TBitArray 去重)、CRC 校验、zlib 压缩。服务器监听 + 多客户端认证(令牌 + 玩家槽位),客户端非阻塞连接;另承载 lockstep 大负载自动分流(TCP 数据块)的上下行传输。可靠性设计:发送有界重试(EWOULDBLOCK 100 次 ≈ 1s)、应用层心跳(15s)+ 对端超时(60s)检测半开连接、恶意头大小上限(分配前校验)、挂起认证连接上限(16,抗连接洪泛)、死 socket 自愈闭环(发送失败 → 挂起 → 客户端重连 → 认证后补发)、下行块认证前补发(每次广播暂存,新认证槽位冲刷)。另内置调试用网络模拟(进程级:延迟/抖动/丢帧/限速,仅影响数据帧,控制帧直发)。

Raw TCP side-channel for FEntitySnapshotPayload transfer (plain C++ class, not a UObject), replacing RPC-based snapshot delivery to avoid the DataChannel !IsBunchTooLarge limit. Application-layer frame protocol: each frame = uint32 size prefix + binary header (FMassBattleSnapshotTcpHeader, Magic "MBTS" + Version 1) + payload; 1 MiB chunked transfer, receiver-side reassembly (TBitArray dedup), CRC validation, zlib compression. The server listens and authenticates multiple clients (token + player slot); clients connect non-blocking. It also carries the lockstep auto-flood (TCP data block) traffic in both directions. Reliability design: bounded send retries (100 × EWOULDBLOCK ≈ 1s), application-layer heartbeats (15s) plus a peer timeout (60s) for half-open detection, hostile-header size caps (validated before allocation), a pending-auth connection cap (16, against connection floods), a dead-socket self-healing loop (send failure → hang → client reconnect → auth → flush), and pre-auth downlink-block backfill (every broadcast parks a copy; newly authenticated slots get it flushed). It also embeds debug network emulation (process-global latency/jitter/drop/bandwidth; data frames only — control frames bypass).

常量 Constants

常量 Constant值 Value说明 Description
MB_SNAPSHOT_TCP_CHUNK_SIZE1 MiB每个 TCP 帧负载的最大字节数(应用层分块)。Maximum bytes per TCP frame payload (app-level chunk).
MB_SNAPSHOT_TCP_MAX_SNAPSHOT_BYTES256 MB重组接受的最大声明压缩大小——限制恶意头字段导致的分配。Maximum declared compressed size accepted for reassembly — bounds allocations from hostile header values.
MB_SNAPSHOT_TCP_MAX_UNCOMPRESSED_BYTES512 MB解压接受的最大声明未压缩大小。Maximum declared uncompressed size accepted for decompression.
MB_SNAPSHOT_TCP_MAX_TRANSFERS4并行入站传输的最大数量(快照与下行 lockstep 各一组)。Maximum concurrent inbound transfers (one set each for snapshots and downlink lockstep).
MB_SNAPSHOT_TCP_MAX_UPLOAD_TRANSFERS32并发上行重组条目上限——发送方是不可信客户端,无数量上限时每个 (slot, BlockId) 一帧即可无限增殖 UploadReassemblyStates;32 远超合法用量(客户端同时只有 1-2 条大命令在途)。Cap on concurrent uplink reassembly entries — the sender is an untrusted client; without a count bound, one frame per (slot, BlockId) grows UploadReassemblyStates forever. 32 is far beyond legit use (a client has 1-2 large commands in flight).
MB_SNAPSHOT_TCP_MAX_FRAMECHUNK + 256TCP 帧的最大大小(头部 + 最大块)。Maximum TCP frame size (header + max chunk).
MB_SNAPSHOT_TCP_SENDFRAME_RETRY_LIMIT100SendFrame 在 EWOULDBLOCK 下的最大重试次数(10ms × 100 ≈ 1s)——把阻塞式发送从"无限"收敛为"有界"。Max SendFrame EWOULDBLOCK retries (~1s @ 10ms) — bounds a wedged send instead of blocking forever.
MB_SNAPSHOT_TCP_CLIENT_CONNECT_TIMEOUT_SECONDS5.0客户端连接/认证截止——EINPROGRESS 悬挂超时即销毁并重试。Client connect/auth deadline — a connect stuck in EINPROGRESS beyond this is torn down and re-attempted.
MB_SNAPSHOT_TCP_CLIENT_RECONNECT_THROTTLE_SECONDS1.0客户端自动重连的节流间隔。Throttle between automatic client reconnects.
MB_SNAPSHOT_TCP_AUTH_TIMEOUT_SECONDS10.0服务器侧认证超时——窗口内未发送 ClientAuth 的 socket 被销毁。Server-side auth timeout — a socket that never sends ClientAuth within this window is destroyed.
MB_SNAPSHOT_TCP_PENDING_SNAPSHOT_TIMEOUT_SECONDS30.0挂起出站快照过期(与重组超时一致)。Pending outbound snapshot expiry (matches the reassembly timeout).
MB_SNAPSHOT_TCP_LOCKSTEP_REASSEMBLY_TIMEOUT_SECONDS30.0lockstep 下行重组超时。Lockstep downlink reassembly timeout.
MB_SNAPSHOT_TCP_MAX_PENDING_AUTH16服务器侧同时挂起(未认证)连接的上限——限制裸连接洪泛的 FD 与映射增长(审计 H5);已认证 socket 数量本就受 ExpectedPlayerSlots 约束。超出的连接先 accept 再立即关闭(排空内核 accept 队列,合法对端不会因积压满而停顿);拒绝日志每秒至多一条。Server-side cap on simultaneously PENDING (not yet authenticated) connections — bounds FD and map growth from raw connection floods (audit H5); authenticated sockets are already bounded by ExpectedPlayerSlots. Excess connections are accepted then immediately closed (drains the kernel accept queue so legit peers never stall on a full backlog); rejection logs are throttled to at most one per second.
MB_SNAPSHOT_TCP_HEARTBEAT_INTERVAL_SECONDS15.0心跳发送间隔。UE 5.6 FSocket 不暴露 keepalive,recv 探测只能检测 FIN/RST——半开连接(对端崩溃无 RST / NAT 静默丢弃)会永久"已连接"并使 resync 通道死锁。Heartbeat send interval. UE 5.6 FSocket exposes no keepalive and the recv probe only detects FIN/RST — a half-open connection (peer crash without RST / silent NAT drop) would stay "connected" forever and deadlock the resync channel.
MB_SNAPSHOT_TCP_PEER_TIMEOUT_SECONDS60.0对端超时——在此时长内未收到对端任何类型的帧即拆除连接。Peer timeout — the connection is torn down when no frame of ANY type arrives from the peer within this window.

EMassBattleSnapshotTcpMsgType

帧消息类型(普通 enum class,非 UENUM)。注意 MessageType 0 被保留为服务器认证拒绝信号(SendAuthFailure):拒绝帧不携带 token——客户端仅凭此类型识别,检查先于其 token 门槛(审计 C2:绝不向被拒的未认证对端回传会话 token)。

Frame message types (plain enum class, not UENUM). Note that MessageType 0 is reserved as the server auth-rejection signal (SendAuthFailure): the rejection frame carries NO token — the client identifies it by this type alone, BEFORE its token gate (audit C2: never echo the session token to a rejected, unauthenticated peer).

值 Value说明 Description
ClientAuth = 1客户端→服务器:用 token+PlayerSlot 认证。client→server: authenticate with token+PlayerSlot.
SnapshotChunk = 2服务器→客户端:多块快照传输中的单个块。server→client: one chunk of a multi-chunk snapshot transfer.
LockstepDataChunk = 3服务器→客户端:大 lockstep 负载块的单个分片(下行自动分流)。server→client: one chunk of a large lockstep-payload block (downlink auto-flood).
LockstepUploadChunk = 4客户端→服务器:大 lockstep 负载块的单个分片(上行自动分流)。client→server: one chunk of a large lockstep-payload block (uplink auto-flood).
Heartbeat = 5双向:半开连接活性探测。both directions: liveness probe for half-open connections.

FMassBattleSnapshotTcpHeader

每个 TCP 帧内的二进制头部,线路上由 uint32 帧大小前缀引导。SnapshotId 在 lockstep 块路径被复用为 BlockId,TargetTick 复用为 ExecuteTick。

Binary header inside every TCP frame, preceded by a uint32 frame-size prefix on the wire. SnapshotId is reused as BlockId on the lockstep-block path; TargetTick is reused as ExecuteTick.

成员变量 Members

名称 Name类型 Type默认值说明 Description
Magicuint320x4D425453魔数 "MBTS";校验失败即丢弃帧。Magic "MBTS"; frames failing the check are dropped.
Versionuint161协议版本。Protocol version.
MessageTypeuint160消息类型(EMassBattleSnapshotTcpMsgType;0 = 认证拒绝)。Message type (EMassBattleSnapshotTcpMsgType; 0 = auth rejection).
SnapshotIdint320每次传输的唯一 ID(lockstep 路径复用为 BlockId)。Unique per-transfer ID (reused as BlockId on the lockstep path).
PlayerSlotint32-1目标玩家槽位。Target player slot.
PayloadTickint320快照捕获时的 tick。Tick when the snapshot was captured.
TargetTickint320应用快照的目标 tick(lockstep 路径复用为 ExecuteTick)。Tick to apply the snapshot at (reused as ExecuteTick on the lockstep path).
Tokenint320会话认证令牌。Session auth token.
ChunkIndexint320从 0 开始的块索引。0-based chunk index.
ChunkCountint320总块数。Total chunks.
TotalCompressedSizeint320组装后的压缩字节数(分配前校验上限)。Assembled compressed byte count (capped before allocation).
TotalUncompressedSizeint320解压后的字节数。Decompressed byte count.
ChunkPayloadSizeint320本帧头之后的字节数。Bytes following this header in this frame.
Crcuint320组装后压缩数据的 CRC32。CRC32 of the assembled compressed blob.

FMassBattleTcpReassembly

入站传输重组状态:记录元数据、按块位图去重、累计已收字节。供快照、下行 lockstep 与服务器上行三类重组复用。

Inbound transfer reassembly state: records metadata, dedups via a chunk bitmap, and accumulates received bytes. Shared by snapshot, downlink lockstep, and server-side uplink reassembly.

成员变量 Members

名称 Name类型 Type默认值说明 Description
SnapshotIdint320传输 ID(lockstep 路径为 BlockId)。Transfer ID (BlockId on the lockstep path).
PayloadTick / TargetTickint32 ×20捕获 tick / 应用(执行)tick,首块初始化时记录。Capture / apply (execute) tick, recorded at first-chunk init.
ChunkCount / TotalCompressedSize / TotalUncompressedSizeint32 ×30元数据,首块初始化;后续块须与之一致(不一致取消)。Metadata, initialized on the first chunk; later chunks must match (mismatch cancels).
ExpectedCrcuint320预期 CRC。Expected CRC.
BytesReceivedint320累计已收字节。Accumulated received bytes.
CompressedBufferTArray<uint8>空重组缓冲。Reassembly buffer.
ReceivedChunksTBitArray<>空块到位位图(去重)。Per-chunk received bitmap (dedup).
FirstChunkTimedouble0.0首块墙钟时间(超时清扫锚点)。First-chunk wall-clock time (timeout-sweep anchor).
bActiveboolfalse槽位是否被占用。Whether the slot is occupied.

方法 Methods

Reset void ()

把全部字段复位到默认值并释放缓冲。

Resets all fields to defaults and releases buffers.

Input

无参数。

No parameters.

Output

无返回值。副作用:全部成员复位。

No return value. Side effect: all members reset.

IsComplete bool () const

传输是否完成:占用中且位图置位数等于 ChunkCount。

Whether the transfer is complete: active and set-bit count equals ChunkCount.

Input

无参数。

No parameters.

Output

返回是否完成。

Returns whether complete.

委托 Delegates

委托 Delegate签名 Signature说明 Description
OnSnapshotBytesReceivedFOnTcpSnapshotBytesReceived (int32 SnapshotId, int32 PayloadTick, int32 TargetTick, const TArray<uint8>& UncompressedBytes)客户端:完整快照接收、重组、解压、CRC 校验通过后触发。Fired when a complete snapshot is received, reassembled, decompressed, and CRC-validated.
OnLockstepDataBlockReceivedFOnLockstepDataBlockReceived (int32 BlockId, int32 ExecuteTick, const TArray<uint8>& DecompressedBytes)客户端:完整下行 lockstep 块重组、解压、CRC 校验通过后触发(自动分流)。Fired on the client when a complete downlink lockstep data block is validated (auto-flood).
OnLockstepDataBlockUploadedFOnLockstepDataBlockUploaded (int32 PlayerSlot, int32 BlockId, int32 ExecuteTick, const TArray<uint8>& DecompressedBytes)服务器:完整上行 lockstep 块重组、解压、CRC 校验通过后触发。Fired on the server when a complete uplink lockstep data block is validated.

方法 Methods

StartServer bool (int32 Port, int32 InSessionToken, const FString& BindAddress = TEXT("127.0.0.1"))

在指定端口启动 TCP 监听(权威端调用)。非阻塞、可复用、监听队列 8、收发缓冲各 1 MiB。Port=0 时 OS 分配空闲端口并读回实际端口(GetServerPort 返回真实值供客户端连接)。已在运行时返回 false。由 UMassBattleNetworkSubsystem::InitializeSnapshotTcpTransport 调用(游戏端口 +100~+109 范围尝试,失败回退 Port=0)。

Starts the TCP listener on the given port (call on the authority). Non-blocking, reusable, backlog 8, 1 MiB receive/send buffers. With Port=0 the OS assigns a free port which is read back (GetServerPort returns the real value for clients). Returns false when already running. Called by UMassBattleNetworkSubsystem::InitializeSnapshotTcpTransport (tries game port +100~+109, falls back to Port=0).

Input
参数 Param类型 Type说明 Description
Portint32监听端口;0 = OS 分配。Listen port; 0 = OS-assigned.
InSessionTokenint32会话认证令牌。Session auth token.
BindAddressconst FString&绑定地址,默认 127.0.0.1;解析失败回退 Any。Bind address, default 127.0.0.1; parse failure falls back to Any.
Output

返回是否启动成功。副作用:创建监听 socket、记录端口与令牌。

Returns whether the server started. Side effects: listen socket creation, port/token recording.

ExpectPlayerSlot bool (int32 PlayerSlot)

注册玩家槽位,让服务器知道该期待该连接——仅将槽位加入 HandleClientAuth 白名单,不排队快照。由 AMassBattleNetworkGameModeBase::PostLogin 的 TCP 预连接握手调用:使客户端在首个大载荷 lockstep 命令(自动分流)或 resync 真正需要 TCP socket 之前就先建好连接。

Registers a player slot so the server knows to expect it — whitelists the slot for HandleClientAuth WITHOUT queueing a snapshot. Called by the PostLogin pre-connect handshake in AMassBattleNetworkGameModeBase so a client establishes its TCP socket before the first large-payload lockstep command (auto-flood) or resync actually needs it.

Input
参数 Param类型 Type说明 Description
PlayerSlotint32要预注册的玩家槽位。Player slot to pre-register.
Output

返回是否注册成功;槽位为负或已释放(FreedPlayerSlots,玩家已登出——非合法认证目标)时返回 false。副作用:槽位加入预期认证集合。

Returns whether the registration succeeded; false for negative slots or freed ones (FreedPlayerSlots, player logged out — not a legitimate auth target). Side effect: the slot joins the expected-auth set.

SetEmulationSettings / GetEmulationSettings static void / static FMassBattleNetEmulationSettings

网络模拟设置(调试):进程级全局——本进程内所有传输实例(PIE 同时承载服务器 + 客户端传输)在发送时读取,一次调用覆盖全部出站 TCP 数据帧。包含 Latency/Jitter(到期队列,Tick 内按每 socket FIFO 冲刷)、DropPercent(帧级概率丢弃——压力测试缺块 → desync → resync 恢复链)、BandwidthLimitKBs(令牌桶限速)。丢失事件在冲刷时掷出;语义按 EMassBattleNetLossMode:RetransmitLate = 丢帧等待 RetransmitDelayMs 后重发,对其 socket 造成队头阻塞(真实的 Reliable/TCP——迟到而非丢失);PermanentDiscard = 永不到达(desync 恢复压力测试)。控制帧(认证/心跳/认证拒绝)永远直发——握手时序保持真实。TCP 刻意不提供重复/乱序旋钮:TCP 设计上就去重且保序,这类效果请用 UE-UDP 路径旋钮。镜像 cvar(仅 TCP 路径):MassBattle.NetEmulation.*;FuncLib 的 BP 封装(SetNetworkEmulation)同时设置 UE 的 net.PktLag 系列,两条传输一起受影响。

Network-emulation settings (debug): process-global — every transport instance in this process (PIE hosts server + client transports) reads them at send time, so one call covers all outbound TCP data frames. Applies Latency/Jitter (due-time queue, per-socket FIFO flush in Tick), DropPercent (frame-level probabilistic drop — stresses missing-block → desync → resync recovery), and BandwidthLimitKBs (token-bucket throttle). Loss events roll at flush time; semantics per EMassBattleNetLossMode: RetransmitLate = a lost frame waits RetransmitDelayMs then retries, head-of-line blocking its socket (realistic Reliable/TCP — late, not lost); PermanentDiscard = never arrives (desync-recovery stress). Control frames (auth / heartbeat / auth-reject) always bypass — handshake timing stays real. TCP deliberately has NO duplicate/reorder knobs: TCP dedupes and preserves order by design; use the UE-UDP path knobs for those effects. Mirror cvars (TCP path only): MassBattle.NetEmulation.*; the FuncLib BP wrapper (SetNetworkEmulation) also sets UE's net.PktLag family so both transports are impaired together.

Input
参数 Param类型 Type说明 Description
Settingsconst FMassBattleNetEmulationSettings&模拟参数(Latency/Jitter/Drop/LossMode/Retransmit/Bandwidth/Seed;仅 SetEmulationSettings)。Emulation parameters (Latency/Jitter/Drop/LossMode/Retransmit/Bandwidth/Seed; SetEmulationSettings only).
Output

Get 返回当前设置快照。副作用:Set 覆写全部进程级模拟状态并重置随机流/令牌桶(RandomSeed=0 时以墙钟重新播种)。

Get returns a snapshot of the current settings. Side effect: Set overwrites all process-global emulation state and reseeds the RNG / token bucket (a RandomSeed of 0 reseeds from wall clock).

ConnectClient bool (const FString& Host, int32 Port, int32 InPlayerSlot, int32 InSessionToken)

作为客户端连接指定 host:port(远程客户端调用)。残留死 socket(SCS_NotConnected/SCS_ConnectionError)先销毁再建新连接;非阻塞 connect,EINPROGRESS 为预期。保存端点供 Tick 节流自动重连,设置 5s 连接/认证截止。已在连接中返回 false。

Connects as a client to the given host:port (call on remote clients). Stale sockets (SCS_NotConnected/SCS_ConnectionError) are destroyed before establishing a fresh connection; non-blocking connect with EINPROGRESS expected. The endpoint is saved for Tick's throttled auto-reconnect and a 5s connect/auth deadline is set. Returns false when already connected.

Input
参数 Param类型 Type说明 Description
Hostconst FString&服务器地址。Server address.
Portint32服务器端口。Server port.
InPlayerSlotint32本客户端玩家槽位(认证用)。This client's player slot (auth).
InSessionTokenint32会话令牌。Session token.
Output

返回连接是否发起成功(非阻塞,实际连接状态后续由 Tick 状态机推进)。

Returns whether the connect was initiated (non-blocking; the actual state advances via the Tick state machine).

Shutdown void ()

关闭并释放全部 socket:客户端、已认证玩家、待认证(销毁而非仅丢弃——堵住 FD 泄漏)、监听;清空全部状态与重组槽。析构函数调用。

Shuts down and releases all sockets: client, authenticated players, pending-auth (destroyed, not just dropped — plugs FD leaks), and the listener; clears all state and reassembly slots. Called by the destructor.

Input

无参数。

No parameters.

Output

无返回值。副作用:销毁全部 socket 并清空状态。

No return value. Side effects: all sockets destroyed, state cleared.

ReleasePlayerSlot void (int32 PlayerSlot)

释放玩家槽位持有的全部 transport 资源(由 GameMode::FreePlayerSlot 调用):移除预期认证注册、槽位进入 FreedPlayerSlots、移除挂起快照、清理该槽位 socket。迟到的 SendSnapshot 对已释放槽位变为无操作而不是重新注册。

Releases all transport resources owned by a player slot (called by GameMode::FreePlayerSlot): removes the expected-auth registration, adds the slot to FreedPlayerSlots, removes pending snapshots, and cleans up the slot's socket. Late SendSnapshot calls no-op for freed slots instead of re-registering them.

Input
参数 Param类型 Type说明 Description
PlayerSlotint32要释放的槽位。Slot to release.
Output

无返回值。副作用:注册表/队列/socket 清理。

No return value. Side effects: registry/queue/socket cleanup.

ReusePlayerSlot void (int32 PlayerSlot)

重新注册被重新分配给新玩家的槽位(由 GameMode::AllocatePlayerSlot 调用):清除 FreedPlayerSlots 守卫,使 SendSnapshot 重新加入预期认证注册、认证得以再次通过。

Re-registers a slot re-allocated to a new player (called by GameMode::AllocatePlayerSlot): clears the FreedPlayerSlots guard so SendSnapshot re-adds the slot to expected auth and auth can succeed again.

Input
参数 Param类型 Type说明 Description
PlayerSlotint32要复用的槽位。Slot to reuse.
Output

无返回值。副作用:清除 freed 守卫。

No return value. Side effect: freed guard cleared.

Tick void ()

服务 tick(由 UMassBattleNetworkSubsystem::TickSnapshotTcpTransport 调用):服务器侧——accept 新连接(挂起认证上限 16,超出先 accept 再立即关闭,审计 H5)、轮询已认证与待认证 socket(drain 数据、recv 探测 FIN/RST、认证超时 10s、心跳广播 15s、半开检测 60s)、驱逐过期挂起快照(30s);客户端侧——认证拒绝之外的节流自动重连(1s)、连接状态机(连接错误/超截止/已连接:认证发送、drain 全部数据、探测、心跳、对端超时)、驱逐过期重组槽(快照 30s / lockstep 30s / 上行 30s)。网络模拟开启时,本 tick 还按每 socket FIFO 冲刷到期延迟帧并执行令牌桶限速。

Service tick (called by UMassBattleNetworkSubsystem::TickSnapshotTcpTransport): server side — accepts connections (pending-auth cap 16; excess accepted then immediately closed, audit H5), polls authenticated and pending-auth sockets (drain, recv FIN/RST probe, 10s auth timeout, 15s heartbeat broadcast, 60s half-open detection), evicts stale pending snapshots (30s); client side — throttled auto-reconnect except after auth rejection (1s), the connection state machine (connection error / deadline / connected: auth send, full drain, probe, heartbeat, peer timeout), and eviction of stale reassembly slots (30s each for snapshot / lockstep / uplink). When network emulation is active, this tick also flushes due delayed frames per socket (FIFO) and applies the token-bucket bandwidth limit.

Input

无参数。

No parameters.

Output

无返回值。副作用:收发帧、回调触发、死 socket 清理、超时驱逐。

No return value. Side effects: frame I/O, callback firing, dead-socket cleanup, timeout eviction.

SendSnapshot int32 (int32 PlayerSlot, int32 PayloadTick, int32 TargetTick, const TArray<uint8>& CompressedPayload, int32 UncompressedSize, uint32 Crc)

向指定玩家发送或排队 snapshot 负载,内部自动分块。槽位已释放则 no-op(返回新 SnapshotId,不重新注册);socket 未认证时挂起队列等待认证后 FlushPendingSnapshotForPlayer 补发。发送失败:清理死 socket + 挂起快照并返回成功——RPC 照发,客户端重连认证替换 socket 后补发,自愈闭环。挂起用 latest-wins(乱序异步派发回调不得覆盖更新的排队快照)。

Sends or queues a snapshot payload to a specific player, chunking internally. A freed slot no-ops (returns the new SnapshotId without re-registering); an unauthenticated socket queues the snapshot until auth completes and FlushPendingSnapshotForPlayer delivers it. On send failure: the dead socket is cleaned up, the snapshot hung, and success is still returned — the RPC goes out anyway, the client reconnects, auth replaces the socket, and the snapshot is delivered: a self-healing loop. Pending uses latest-wins (out-of-order async dispatch callbacks never overwrite a newer queued snapshot).

Input
参数 Param类型 Type说明 Description
PlayerSlotint32目标玩家槽位。Target player slot.
PayloadTickint32快照捕获 tick。Snapshot capture tick.
TargetTickint32应用目标 tick。Apply target tick.
CompressedPayloadconst TArray<uint8>&已压缩负载。Compressed payload.
UncompressedSizeint32解压字节数(头部元数据)。Uncompressed byte count (header metadata).
Crcuint32压缩数据 CRC。Compressed-data CRC.
Output

返回 SnapshotId(正数)——注意:发送失败也返回正数(自愈闭环设计);槽位无效时返回 -1。副作用:注册预期认证、挂起或分块发送。

Returns the SnapshotId (positive) — note: send failures also return positive (the self-healing design); -1 for invalid slots. Side effects: expected-auth registration, hang or chunked send.

BroadcastLockstepDataBlock bool (int32 BlockId, int32 ExecuteTick, const TArray<uint8>& CompressedData, int32 UncompressedSize, uint32 Crc)

向所有已认证客户端广播大 lockstep 负载块(下行自动分流)。每次广播都暂存一份副本(FIFO,上限 16 条,最旧淘汰)——后认证的客户端从未见过该块,而引用命令(引擎复制)仍会到达:没有补发时加入端在 ExecuteTick 冻结 10 秒等一个从未发送的块,随后落入 resync 循环(beginplay 的 FF.RebuildFlowField 早于客户端 TCP 认证数秒是典型案例);每个新认证槽位都会冲刷收到的暂存块。BlockId 为权威 id;ExecuteTick 为匹配引用命令的触发 tick。无已认证客户端返回 false(暂存副本在认证时补给加入端);发送失败的 socket 清理(下次广播不再命中)。

Broadcasts a large lockstep-payload block to ALL authenticated clients (downlink auto-flood). Every broadcast parks a copy (FIFO, capped at 16, oldest evicted) — a client authenticating later never saw it, while the reference command (engine replication) still reaches it: without the backfill the joiner froze 10s at ExecuteTick waiting for a block that was never sent, then fell into the resync loop (beginplay-time FF.RebuildFlowField firing seconds before the client's TCP auth is the canonical case); every newly authenticated slot gets the parked blocks flushed to it. BlockId is the authoritative id; ExecuteTick is when the matching reference command fires. Returns false with no authenticated clients (the parked copy serves the joiner at auth); failed sockets are cleaned up (so the next broadcast skips them).

Input
参数 Param类型 Type说明 Description
BlockIdint32权威块 ID。Authoritative block ID.
ExecuteTickint32引用命令的执行 tick。Execute tick of the reference command.
CompressedDataconst TArray<uint8>&压缩负载。Compressed payload.
UncompressedSizeint32解压字节数。Uncompressed byte count.
Crcuint32压缩数据 CRC。Compressed-data CRC.
Output

返回是否至少向一个客户端发出。副作用:分块发送、死 socket 清理。

Returns whether at least one client received the send. Side effects: chunked sends, dead-socket cleanup.

SendLockstepDataBlockToServer bool (int32 BlockId, int32 ExecuteTick, const TArray<uint8>& CompressedData, int32 UncompressedSize, uint32 Crc)

客户端向服务器上传大 lockstep 负载块(上行自动分流)。BlockId 为本地分配(AllocLocalBlockId),服务器汇聚时重映射为权威 ServerBlockId。

Uploads a large lockstep-payload block from client to server (uplink auto-flood). BlockId is locally allocated (AllocLocalBlockId); the server remaps it to the authoritative ServerBlockId on convergence.

Input
参数 Param类型 Type说明 Description
BlockIdint32本地块 ID。Local block ID.
ExecuteTickint32执行 tick(上行时通常为发送方当前 tick)。Execute tick (usually the sender's current tick on uplink).
CompressedDataconst TArray<uint8>&压缩负载。Compressed payload.
UncompressedSizeint32解压字节数。Uncompressed byte count.
Crcuint32压缩数据 CRC。Compressed-data CRC.
Output

返回是否发出(客户端 socket 缺失时 false)。

Returns whether the send was issued (false without a client socket).

CompressSnapshot static TArray<uint8> (const TArray<uint8>& UncompressedData)

zlib 压缩(FArchiveSaveCompressedProxy, NAME_Zlib);空输入返回空数组;压缩错误返回空数组。

zlib compression (FArchiveSaveCompressedProxy, NAME_Zlib); empty input returns an empty array; compression errors return an empty array.

Input
参数 Param类型 Type说明 Description
UncompressedDataconst TArray<uint8>&未压缩字节。Uncompressed bytes.
Output

返回压缩字节;失败返回空数组。

Returns compressed bytes; empty on failure.

DecompressSnapshot static TArray<uint8> (const TArray<uint8>& CompressedData, int32 UncompressedSize)

zlib 解压。分配前限制声明未压缩大小(≤ 512MB)——恶意声明不得触发 OOM;空/非法输入返回空数组。

zlib decompression. Caps the declared uncompressed size (≤ 512MB) before allocating — hostile declarations must not OOM; empty/invalid input returns an empty array.

Input
参数 Param类型 Type说明 Description
CompressedDataconst TArray<uint8>&压缩字节。Compressed bytes.
UncompressedSizeint32声明解压大小(≤0 或超限返回空)。Declared uncompressed size (≤0 or over the cap → empty).
Output

返回解压字节;失败返回空数组。

Returns decompressed bytes; empty on failure.

SerializeSnapshotPayload static TArray<uint8> (const FEntitySnapshotPayload& Payload)

经 FMemoryWriter + SerializeItem 把 payload 序列化为字节(反射驱动)。

Serializes the payload to bytes via FMemoryWriter + SerializeItem (reflection-driven).

Input
参数 Param类型 Type说明 Description
Payloadconst FEntitySnapshotPayload&待序列化 payload。Payload to serialize.
Output

返回序列化字节。

Returns the serialized bytes.

DeserializeSnapshotPayload static bool (const TArray<uint8>& Bytes, FEntitySnapshotPayload& OutPayload)

经 FMemoryReader + SerializeItem 反序列化;空输入或读取错误返回 false。

Deserializes via FMemoryReader + SerializeItem; empty input or read errors return false.

Input
参数 Param类型 Type说明 Description
Bytesconst TArray<uint8>&序列化字节。Serialized bytes.
OutPayloadFEntitySnapshotPayload&输出 payload。Output payload.
Output

返回是否成功。副作用:成功时覆写 OutPayload。

Returns whether it succeeded. Side effect: overwrites OutPayload on success.

IsServerActive / IsClientConnected / GetServerPort / GetNextSnapshotId bool () const / bool () const / int32 () const / int32 ()

状态访问器:IsServerActive = 监听 socket 存在;IsClientConnected 仅当 socket 报告 SCS_Connected(UE 的 GetConnectionState 把连接中映射为 SCS_NotConnected、把对端优雅关闭映射为 SCS_Connected,无法检测远端关闭——由 Tick 的 recv 探测承担;为 false 时 RPC 处理器经 ConnectClient 重建,其会拆除残留死 socket);GetServerPort 返回实际端口;GetNextSnapshotId 递增分配传输 ID。

Status accessors: IsServerActive = listen socket exists; IsClientConnected only when the socket reports SCS_Connected (UE's GetConnectionState maps a pending connect to SCS_NotConnected and a gracefully-closed peer to SCS_Connected — it cannot detect remote closure; that duty belongs to Tick's recv probe; a false here makes RPC handlers re-establish via ConnectClient, which tears down stale sockets); GetServerPort returns the actual port; GetNextSnapshotId allocates transfer IDs incrementally.

Input

无参数。

No parameters.

Output

按访问器返回对应值;GetNextSnapshotId 副作用为递增内部计数器。

Returns the corresponding value per accessor; GetNextSnapshotId increments the internal counter as a side effect.

管线角色 Pipeline Role

创建与绑定:UMassBattleNetworkSubsystem::InitializeSnapshotTcpTransport 按网络模式创建(服务器:游戏端口 +100~+109 尝试 + Port=0 回退;客户端:惰性创建)并绑定三个委托——服务器侧 OnLockstepDataBlockUploaded 把上行块暂存并汇聚等待的引用命令(NetworkSubsystem.cpp:177-191);客户端侧 OnSnapshotBytesReceived 反序列化 + QueueSnapshotApply(:207-223)、OnLockstepDataBlockReceived 缓存下行块(:226-230)。 每帧驱动:UMassBattleNetworkSubsystem::TickSnapshotTcpTransport 调用 Transport->Tick()(NetworkSubsystem.cpp:265-272)并裁剪过期块。 快照下发:服务器 DispatchSnapshotViaTcp_Async(GameModeBase)后台线程序列化压缩 → 游戏线程 SendSnapshot → ClientBeginTcpSnapshotResync 控制平面带端口/令牌 → 客户端 ConnectClient → 认证 → FlushPendingSnapshotForPlayer 补发 → OnSnapshotBytesReceived → 应用。 槽位生命周期:AMassBattleNetworkGameModeBase::AllocatePlayerSlot 调 ReusePlayerSlot(GameModeBase.cpp:631),FreePlayerSlot 调 ReleasePlayerSlot(:651)——防止死 socket 跨登出残留污染重连;PostLogin 的 TCP 预连接握手经 ExpectPlayerSlot 白名单槽位并立即让客户端 ClientConnectTcpTransport(:629 附近)。 lockstep 自动分流:服务器 SendLockstepCommandToClients(≥16KB 负载)BroadcastLockstepDataBlock + 引用命令;客户端 SendLockstepCommandToServer SendLockstepDataBlockToServer 上行,服务器 ConvergeUplinkBlock 汇聚后回广播。 网络模拟:UMassBattleFuncLib::SetNetworkEmulation / ClearNetworkEmulation / GetNetworkEmulation(BP)调用本类 SetEmulationSettings / GetEmulationSettings,并同时设置 UE 的 net.PktLag 系列,使 TCP 与 UDP RPC 两条传输一起受影响(FuncLib.cpp:980-1009)。

Creation and binding: UMassBattleNetworkSubsystem::InitializeSnapshotTcpTransport creates per net mode (server: game port +100~+109 attempts with a Port=0 fallback; client: lazy) and binds the three delegates — server-side OnLockstepDataBlockUploaded stages the uplink block and converges any waiting reference command (NetworkSubsystem.cpp:177-191); client-side OnSnapshotBytesReceived deserializes + QueueSnapshotApplys (:207-223), and OnLockstepDataBlockReceived caches downlink blocks (:226-230). Per-frame driving: UMassBattleNetworkSubsystem::TickSnapshotTcpTransport calls Transport->Tick() (NetworkSubsystem.cpp:265-272) and prunes stale blocks. Snapshot delivery: server DispatchSnapshotViaTcp_Async (GameModeBase) serializes/compresses on a background thread → game-thread SendSnapshot → the ClientBeginTcpSnapshotResync control plane carries port/token → client ConnectClient → auth → FlushPendingSnapshotForPlayer delivery → OnSnapshotBytesReceived → apply. Slot lifecycle: AMassBattleNetworkGameModeBase::AllocatePlayerSlot calls ReusePlayerSlot (GameModeBase.cpp:631); FreePlayerSlot calls ReleasePlayerSlot (:651) — so a dead socket cannot survive a logout and poison the next reconnect; the PostLogin TCP pre-connect handshake whitelists the slot via ExpectPlayerSlot and immediately has the client ClientConnectTcpTransport (around :629). Lockstep auto-flood: the server's SendLockstepCommandToClients (≥16KB payload) uses BroadcastLockstepDataBlock + a reference command; the client's SendLockstepCommandToServer uploads via SendLockstepDataBlockToServer, and the server's ConvergeUplinkBlock converges and rebroadcasts. Network emulation: UMassBattleFuncLib::SetNetworkEmulation / ClearNetworkEmulation / GetNetworkEmulation (BP) call this class's SetEmulationSettings / GetEmulationSettings and also set UE's net.PktLag family, impairing the TCP and UDP RPC transports together (FuncLib.cpp:980-1009).