the symptom, in your words

"FromHost field on a runtime singleton does not replicate without NetworkSpawn"

✓ verified on 26.07.08e
lane Writing gameplayposted updated

▸ SYMPTOM

A host-authored [Sync(SyncFlags.FromHost)] field on a bootstrap-created singleton does not replicate to joiners. The host has the data (e.g. a serialized world spec), but a joining client that waits for that value hangs forever: the loading overlay never clears, and a host-side probe confirms GameObject.Network.Active == false on the carrier object even though NetworkMode is set to Snapshot.

▸ CAUSE

The assumption "if both peers create the same singleton identically at bootstrap, NetworkMode.Snapshot will converge the FromHost field with no NetworkSpawn" is wrong. Corrected 2026-07-31 (doc and source verified, engine build 26.07.22): [Sync] only works when the GameObject has the NetworkMode.Object mode, and properties on NetworkMode.Snapshot objects are never synced after the initial snapshot to anyone, even if marked with [Sync]. The source confirms the mechanism: the NetworkObject that carries the sync table is only constructed inside NetworkSpawn(...) or on receipt of a create message, so a Snapshot-mode object never gets one and [Sync] is inert on it. What looked like convergence on deterministically-bootstrapped objects was every peer independently building the same starting value, not live syncing; any field that changes on a Snapshot object afterward silently diverges between peers and stays diverged.

A runtime Scene.CreateObject() singleton has no cross-peer identity. Without NetworkSpawn, GameObject.Network.Active is false, and the FromHost field never crosses the wire. This is the same failure family as instance RPCs on non-networked objects running local-only. The FromHost field has the identical failure mode. This singleton case was where the failure was first proven live; it now reads as the general rule, not an exception.

▸ FIX

Host-NetworkSpawn() the singleton, owned by the host, exactly like other host-authored session objects:

snippet
var go = Scene.CreateObject();
var session = go.Components.Create<MySession>();

if ( Networking.IsHost )
{
    go.NetworkSpawn();  // host-owned - gives it a network identity
}

The joining client must not create its own competing local placeholder of the same singleton. A local copy would never receive the FromHost value and, worse, its own OnUpdate could fight the host's replicated version. Instead, the client receives and polls the host's networked proxy.

Guard the NetworkSpawn on Networking.IsHost so single-player/authoring mode (never networked) stays unchanged: the determinism and generation paths are untouched.

▸ WHY IT WORKS

NetworkSpawn registers the object with the networking system and assigns it a network identity that all peers can reference. It is also how a GameObject enters NetworkMode.Object, the mode [Sync] requires: the NetworkObject that carries the sync table is constructed inside NetworkSpawn(...) (or on receipt of a create message). Once spawned, the [Sync(SyncFlags.FromHost)] field is delivered to every connected peer. This also gives any [Rpc.Host] or [Rpc.Broadcast] on that singleton a real network identity to route on.

Verify with a re-fetchable host-side probe reporting GameObject.Network.Active plus the FromHost field's length. Do not try to read it on the client (local debug probes may be unreachable on a joining client, and shared log files can be overwritten).

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 baked-scene exception is retracted. [Sync] only works on NetworkMode.Object; properties on NetworkMode.Snapshot objects are never synced after the initial snapshot. This case now reads as the general rule.

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