the symptom, in your words

"Owner-simulated networking"

✓ verified on 26.07.08e
lane Writing gameplayposted updated

▸ SYMPTOM

  • Proxies simulate input and fight the owner (jitter, double-apply).
  • Host-authoritative meters get written on clients then snapped back every snapshot.
  • A scene-wide clock/singleton that was never NetworkSpawned reads the same value on every peer at first, then silently diverges once the host changes it.

▸ CAUSE

Two different recipes get mixed:

  1. Per-owner objects (player pawns): owner simulates, [Sync] replicates to proxies.
  2. Shared host truth (day/night, run stats): the host owns the object, mutates it, and [Sync(SyncFlags.FromHost)] replicates.

Both recipes need the same prerequisite. GameObject.NetworkMode defaults to Snapshot, not Never (per Sandbox.Engine.xml), and corrected 2026-07-31 (doc and source verified, engine build 26.07.22): [Sync] only works when the GameObject has the NetworkMode.Object mode. Properties on NetworkMode.Snapshot objects are never synced after the initial snapshot to anyone, even if marked with [Sync]. NetworkMode.Object is entered via NetworkSpawn(), so NetworkSpawn is mandatory before [Sync] does anything, for any object carrying a [Sync] field, not only per-owner objects.

The engine does not stop a non-host from writing a FromHost field locally (the next snapshot steamrolls it), so a manual host guard is still load-bearing.

▸ FIX

Lobby + pawn spawn (host)

snippet
if (!Networking.IsActive)
    Networking.CreateLobby(new LobbyConfig());

// on host, INetworkListener.OnActive(Connection):
var go = prefab.Clone();
go.NetworkSpawn(channel); // owner channel

Owner-simulated pawn

snippet
protected override void OnFixedUpdate()
{
    if (IsProxy) return; // proxies do not simulate

    // read input, integrate, write [Sync] fields
}

[Sync] public Vector3 NetPosition { get; set; }
[Sync(SyncFlags.FromHost)] public bool RoundActive { get; set; } // example host flag

[Rpc.Broadcast] for one-shot events. Host migration and interp come free with the stock path.

Networked props are runtime-only: never rely on them being saved into scene files.

Scene-wide singleton (shared truth)

  • The host creates it and NetworkSpawns it host-owned, exactly like any other host-authored session object. A runtime Scene.CreateObject() singleton that is never spawned has no cross-peer identity: Network.Active stays false and its FromHost fields never send.
  • The joining client must not create a competing local copy of the same singleton. It receives and reads the host's networked proxy.
  • Tag authoritative fields [Sync(SyncFlags.FromHost)].
  • Gate mutations:
snippet
if (Networking.IsActive && !Networking.IsHost) return;

Guard the NetworkSpawn on Networking.IsHost so single-player (never networked) stays unchanged. See FromHost singleton needs NetworkSpawn for the full case.

▸ WHY IT WORKS

IsProxy partitions simulation so only the owning connection integrates. [Sync] streams owner state to everyone else. FromHost is the parallel channel for global state. Both ride the same plumbing: the NetworkObject that carries the sync table is only constructed inside NetworkSpawn(...) or on receipt of a create message, so an object left in the default Snapshot mode never gets one and its [Sync] fields are inert. Peers that bootstrap such an object deterministically agree on its starting value because each computed the same number, not because anything replicated.

Verified on engine 26.07.08e: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?
changelog
  • Corrected (26.07.22, doc and source verified): the 'don't NetworkSpawn scene-wide singletons' recipe is retracted. [Sync] only works on NetworkMode.Object, entered via NetworkSpawn(), for any object carrying a [Sync] field.

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord