2026-05-29 17:57:24 +08:00

7.6 KiB

TH1 Network Contract

This reference summarizes the multiplayer contract after the May 2026 pre-release network audit.

SimpleP2P

  • All normal game messages use per-peer outgoing queues.
  • Per-peer FIFO is required.
  • Ordered envelopes are used for game payloads; do not bypass them for gameplay sync.
  • Outgoing sequence is committed only after enqueue succeeds.
  • Large payloads are chunked after ordered wrapping.
  • Outgoing queue processing may send multiple messages per update within a per-frame message/byte budget, but must preserve FIFO per peer.
  • Incoming large chunks must validate magic, version, message id, chunk index, chunk count, total length, and payload length.
  • Large incoming messages and outgoing queues have per-peer and global byte budgets.
  • Ordered gaps and large-message receives must timeout and disconnect/clean state rather than wait forever.
  • Steam message pointers must be released in finally.

SteamLobbyManager

  • SendMessageToPeer returns bool; precheck failures must call the same failure path used by P2P send failures.
  • BroadcastMessage returns bool.
  • Critical broadcast must gather current lobby members, skip self, preflight all targets through SimpleP2P.CanQueueMessages, and only then enqueue.
  • Missing connection, non-lobby target, invalid data, or queue budget failure must surface through OnLobbyErrorEvent / send-failure logging.

GameNetSender

  • Sender methods that gate local state must return bool.
  • GameStart must validate MapData and return broadcast success.
  • ChangeCiv carries full MemberCiv room state such as civ, force, and ready state. Client optimistic local application is allowed only after send success and must still be reconciled by host UpdateLobbyData.
  • RequestLobbyData should be retried by clients until host config matches current lobby members. Avoid treating a request as synced before the host P2P connection is active and accepted.
  • ActionConfirm must return send success.
  • ActionExecute must return broadcast success.
  • ForceUpdate and full MapData sends must validate multiplayer map data before sending.
  • MapDataDebugMessage sends current MapData for diagnostics only. Member-to-host and host-broadcast sends should use GameNetSender and the normal lobby queue path, but callers must not treat it as authoritative sync.
  • Heartbeat send timestamps should only update after send acceptance.

GameNetReceiver

  • Wrap deserialization and dispatch in try/catch.
  • Ignore P2PMsgType.NetworkStress; the editor stress tool consumes diagnostics packets through SimpleP2P.OnMessageReceivedEvent before gameplay dispatch.
  • Validate incoming GameStart and ForceUpdate maps before applying.
  • Host handles ChangeCiv as a full member room-config mutation, including civ, force, and ready state, then broadcasts changed lobby config.
  • Client UpdateLobbyData is authoritative for room config. Mark lobby data synced only when the received MapConfig.MultiCivs matches the current lobby member set.
  • NetStartGame and NetResumeMatch return bool; UI should only close/hide after success.
  • ForceUpdate should restore previous game state if resume fails.
  • MapConfirm must guard null maps, missing actions, and null action payloads.
  • MapDataDebugMessage should compare incoming MapData with local Main.MapData and log [MapDataDebug] hash/action/diff details only. It must not call NetResumeMatch, change GameState, request ForceUpdate, or replace local map data.

Network Stress Tool

  • Tool path: Unity/Assets/Scripts/TH1_Logic/Editor/NetworkStressEditorWindow.cs.
  • Diagnostics message: NetworkStressMessage / P2PMsgType.NetworkStress.
  • The tool must send probes through Lobby.BroadcastMessage and Lobby.SendMessageToPeer, not through a raw side channel.
  • The host starts one-click tests; clients auto-start on ControlStart.
  • Reports are keyed by RunId; stale packets, ACKs, and reports from previous runs must be ignored.
  • Host export should contain one report per lobby member when clients are reachable. If a report is late, host may update the same export file after receipt.
  • Current default test is a one-minute flow: 50 seconds of traffic and up to 10 seconds of report collection.

MapData Debug Tool

  • Tool path: Unity/Assets/Scripts/TH1_Logic/Editor/NetworkStressEditorWindow.cs.
  • Menu: Tools/Steam MapData一致性诊断.
  • Diagnostics message: MapDataDebugMessage / P2PMsgType.MapDataDebug.
  • Use when MapConfirm/ForceUpdate logs are too late to reveal the original divergence. The tool captures the sender's current MapData at button click time.
  • Buttons:
    • Members send current MapData to host.
    • Host broadcasts current MapData to all members.
  • Receiver logs whether local and remote maps match, map hashes, action counts, first action mismatch, and diff details.
  • The tool is read-only from a gameplay perspective. It must not mutate Main.MapData, request reconnect, enter ForceUpdating, or hide/show gameplay UI.
  • It should continue to use GameNetSender / lobby P2P queues so full-map diagnostics exercise ordered delivery and large-message chunking.

Lobby MapConfig / Ready State

  • Pre-game room settings and member state live in Main.Instance.MapConfig.
  • MemberCiv contains MemberId, PlayerId, CivId, ForceId, and IsReady.
  • Host is the authority. Clients can optimistically apply their own MemberCiv only after ChangeCiv send success, but must keep requesting host data until HasSameLobbyMembers is true.
  • Host UpdateLobbyMember removes stale members, adds new members, and keeps the owner ready. New guests default to not ready.
  • UI should call SetSelfReady or ToggleSelfReady; read readiness through IsMemberReady or MemberCiv.IsReady.
  • Changing civ resets that member's IsReady; host room setting changes reset guest readiness through ResetGuestReadyStates.
  • StartGame and resume must require AreAllLobbyMembersReady; owner is treated ready and guests must be ready.

MapData And NetData

  • MapData.DeserializedMissingCriticalData means the save/network map must not be used.
  • OnAfterMemoryPackDeserialize may coalesce non-critical containers to avoid callback NREs but must not silently accept missing core data.
  • NetData.RefreshPlayerNet(MapData) returns bool.
  • In multiplayer mode, every current lobby member must have a valid, non-duplicate PlayerId.
  • Host resume/start must fail before assigning global MapData if player network mapping is invalid.

Main And UI

  • Host start/resume snapshots MapData, InputLogic, MapInteractionLogic, and MapGeneratorLogic.
  • Host GameStart failure must roll back and must not save, refresh turn, or report success.
  • Custom map load must check mapRecord == null before RegenerateMap.
  • UIOutsideMultiplayView.ShowLoadingAndStartGame must only invoke OnStartGame after the host start/resume method returns true.
  • UIOutsideMultiplayView.StartGame must gate host start/resume on lobby readiness.
  • Lobby UI should mutate member config through MapConfig APIs rather than directly editing shared MemberCiv instances.
  • Timer callbacks for start announcements should be cancelled during abort paths.

ActionLogic

  • Client ActionConfirm failure aborts local action execution.
  • Owner ActionExecute broadcast failure aborts owner local action execution.
  • TurnEnd can send confirmation and stop local execution as designed.

Timer/Event Notes

  • Timer callbacks should guard destroyed Unity targets.
  • Timer mutation during callback must not remove the wrong task.
  • Event publish should isolate listener exceptions so one bad listener does not block others.