Unity Workflow LibraryUWLAbout

Topic 6 · Networking

Pitfalls: netcode & persistence

Intent-only clients, relevancy, late join, persistence.

Generalized pitfalls from shipped and active projects. Record your own project's traps the same way: Keeping a project pitfall file. All entries LOCKED evidence. Status: locked (empirical, seen in shipped/active projects).

  • CLIENTS-SEND-INTENT-ONLY — clients send input or intent, never durable state or outcomes; the server owns movement truth, combat, inventory, rewards and persistence. Check: every client->server message is an input or a request the server validates. See also OWNERSHIP-KEYED-ON-DURABLE-ID (the Netcode overview page). Status: locked (empirical, seen in shipped/active projects).
  • VISIBLE-IS-NOT-RELEVANT — a client-visible entity is not automatically client-relevant; interest management limits bandwidth and cheat exposure. Check: relevance is defined per entity type before high-volume entities ship. Status: locked (empirical, seen in shipped/active projects).
  • RELEVANCY-RADIUS-FITS-THE-FASTEST-MOVER — a relevancy radius sized for weapon range lets fast vehicles pop in late and close. Why: radius ignored closing speed. Check: radius >= (max closing speed x entry latency) plus margin, or pace region entry for fast movers. Status: locked (empirical, seen in shipped/active projects).
  • SCALE-SENDS-BY-IMPORTANCE — with many relevant players at high tick rate, send by importance/distance, not round-robin. Check: ghost/object importance scales with distance; a crowd test reports snapshot size. Status: locked (empirical, seen in shipped/active projects).
  • REWIND-EVERYTHING-YOU-CAN-HIT — lag compensation must rewind every hittable body (NPCs, animals, dummies) and its depth must cover the supported RTT. Check: a lag-comp test at the max supported RTT hits every target class. Status: locked (empirical, seen in shipped/active projects).
  • ONE-SHARED-FRAME-NOT-PER-PLAYER — do not mint coordinate frames/physics worlds per player position. Why: frame creation keyed on the requester. Check: frames are keyed by fixed spatial cells shared by all players. Status: locked (empirical, seen in shipped/active projects).
  • LATE-JOIN-REPLAY-IS-SCOPED-AND-PACED — a late joiner's world replay must be scoped by relevance and streamed, not dumped and throttled. Check: replay only relevant content; queue, never drop, initial-sync requests. See also LATE-JOIN-SNAPSHOT-STREAMS-PER-PIECE. Status: locked (empirical, seen in shipped/active projects).
  • NO-RELIABLE-RPC-FOR-HIGH-FREQUENCY-STATE — high-frequency state goes in replicated snapshots, not reliable RPCs. Check: RPCs are events; per-tick values are ghost fields/NetworkVariables. Status: locked (empirical, seen in shipped/active projects).
  • PREDICTION-STAYS-PENDING — client predictions stay explicitly pending until server confirmation. Check: predicted state is visually and logically distinct until confirmed. Status: locked (empirical, seen in shipped/active projects).
  • RECOMPILE-COPIED-NETCODE — never copy a system from an older netcode package version without recompiling its lifecycle and snapshot APIs against the current package. Check: copied netcode is diffed against the current package API, not just compiled. Status: locked (empirical, seen in shipped/active projects).
  • NETWORKED-OBJECTS-OUTLIVE-MENU-SCENES-ONLY-IF-PLACED-RIGHT — an object spawned while a menu scene is active dies when the client unloads that scene. Why: it was parented to/registered with the menu scene. Check: spawn session objects into a persistent scene or after the gameplay scene is active (DestroyWithScene false where appropriate). Status: locked (empirical, seen in shipped/active projects).
  • ONE-PREFAB-ONE-REGISTRATION-LIST — the same prefab in two network prefab lists unregisters it. Check: one source list per prefab; a validator flags duplicates. Status: locked (empirical, seen in shipped/active projects).
  • MULTIPLAYER-PLAYMODE-PACKAGE-CAN-BE-A-FACADE — verify the multiplayer play-mode package actually contains its runtime on your editor version. Check: confirm the package's assemblies/types exist before relying on it. Status: locked (empirical, seen in shipped/active projects).
  • STORE-ISSUES-REVISIONS — let the persistence store issue revisions; never keep a counter beside it. Check: revision comes from the store's transaction. Status: locked (empirical, seen in shipped/active projects).
  • SMOKE-TEST-ACROSS-A-RESTART — a persistence smoke test must close and reopen the same store file. Check: test writes, closes, reopens and reads. Status: locked (empirical, seen in shipped/active projects).
  • ONE-WRITER-PROCESS-PER-STORE — two processes over one store file lose updates. Why: coalescing caches and revision counters are per process. Check: one writer per store; others go through it. Status: locked (empirical, seen in shipped/active projects).
  • COMMIT-THEN-MARK-CLEAN — advance dirty hashes/flags only after the database transaction commits. Check: clean-marking happens after commit success. Status: locked (empirical, seen in shipped/active projects).
  • NO-WHOLE-TABLE-REPLACE-FROM-PARTIAL-DATA — never replace a full table with a partial or client-derived payload. Check: writes are row-level upserts/deletes with explicit scope. Status: locked (empirical, seen in shipped/active projects).
  • DB-OPEN-FAILURE-IS-FATAL — a database open failure is a server startup failure, not an empty-world fallback. Check: open failure aborts startup with an error. Status: locked (empirical, seen in shipped/active projects).
  • RESTORE-BEFORE-CAPTURE — never capture/save a world before its domains restored successfully; clear restore gates when the world is recreated in-process. Check: save is gated on restore success; world teardown resets the gate. Status: locked (empirical, seen in shipped/active projects).
  • SNAPSHOT-BEFORE-DESPAWN — snapshot disconnecting players before destroying or releasing their networked objects. Check: disconnect handler saves, then despawns. Status: locked (empirical, seen in shipped/active projects).
  • ECONOMY-NEEDS-ROWS-NOT-BLOBS — snapshot blobs are for regenerable state only; anything an economy depends on needs row-level records and real transactions. Check: currency, inventory and ownership are rows with transactions. Status: locked (empirical, seen in shipped/active projects).
  • ONE-COMMAND-ID-PER-DECISION — retries of the same logical action reuse one command id and replay the original result; deterministic rejections are recorded, infrastructure faults throw. Check: idempotency store keyed by command id; only deterministic outcomes are persisted. See also IDEMPOTENCY-RECORD-ORDER-BY-RETRY-COST (the ugs page). Status: locked (empirical, seen in shipped/active projects).
  • LOCAL-SAVES-AUTHORIZE-NOTHING-ONLINE — local saves are not proof of integrity and may not authorize online economy, ownership or participation; client anti-cheat is friction, not authority; admin bypasses are isolated from production builds. Check: every online action resolves against server records. Status: locked (empirical, seen in shipped/active projects).