"Owner-simulated networking"
▸ 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:
- Per-owner objects (player pawns): owner simulates,
[Sync]replicates to proxies. - 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)
if (!Networking.IsActive)
Networking.CreateLobby(new LobbyConfig());
// on host, INetworkListener.OnActive(Connection):
var go = prefab.Clone();
go.NetworkSpawn(channel); // owner channelOwner-simulated pawn
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 runtimeScene.CreateObject()singleton that is never spawned has no cross-peer identity:Network.Activestays 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:
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.
- 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.