Networking · guide
Networked prefabs
What may be spawned, on both ends of the wire — and filling that list from the content build rather than from a start-up path.
Edit this page on GitHubDocuments
What it is
A NetworkPrefabRegistry is what may be spawned, on both ends of the wire: an address, the id
the wire uses for it, the template, and which of its nodes get a NetworkId. A NetworkPrefab is
one entry.
A NetworkObject is the tag a designer puts on the nodes that should get one.
NetworkPrefabContent fills a registry out of a build's content catalog, by label, and gives
back a NetworkPrefabLoad — what went in, and anything that was labelled a networked prefab and
turned out not to be one.
What it is for
A spawn on the wire is twelve bytes: a prefab id, a scene id and an owner. The prefab id is the hash
of the addressable's address (docs/plan/08-asset-pipeline-and-addressables.md), so a
server that registers prefabs/crate and a client that registers the same address agree on the
number without a handshake — nothing about the registry is ever sent.
What still had to agree was the list, and until this existed it agreed by being typed twice.
Register(address, prefab) is a call in a start-up path, so a prefab added on the server and
forgotten on the client is a spawn the client receives, cannot resolve, and drops — with the bug
sitting in two files that look nothing like each other. A label is a property of the content, so both
ends read the same list out of the same build and neither maintains it.
Using it
Put the label on the group your networked prefabs live in — networked-prefabs, which is
NetworkPrefabContent.Label:
# Assets/Prefabs/Networked.vxgroupname: NetworkedPrefabsloadPath: Localpacking: PackTogetherlabels: - networked-prefabsThen fill the registry once, at boot, on every peer:
using System;using System.Threading.Tasks;using Vixen.Assets;using Vixen.Net.Engine;using Vixen.Net.Engine.Content;using Vixen.Net.Replication;public static class Boot { public static async Task<NetworkSpawner> SpawnerAsync(AssetManager assets, NetworkIdAllocator ids) { var prefabs = new NetworkPrefabRegistry(); var load = await NetworkPrefabContent.LoadAsync(prefabs, assets); if (load.Problems.Length > 0) { // A .vxgroup broad enough to sweep up a texture. Refuse rather than log: a peer that // starts with half a prefab table hands out spawns nobody can build. throw new InvalidOperationException(string.Join(Environment.NewLine, load.Problems)); } return new(prefabs, ids); }}Several groups fill one registry — LoadAsync(prefabs, assets, ["creatures", "vehicles"]) — and a
game whose networked prefabs are a list in code rather than a group uses
LoadFromAsync(prefabs, assets, addresses) instead.
Whatever is already in the registry stays, so a game may mix a build's prefabs with templates it built itself. Loading the same build twice registers once.
A bad label is a problem; a bad address is an exception
The distinction is deliberate, and it is the same one definitions draws. A
group broad enough to sweep up a texture is a content mistake: the rest registers and the problem
is named, in address order. An address that is not in the build's catalog at all is the caller
being wrong — a typo in a hand-written list — and it throws AddressNotFoundException, because
swallowing it would turn that typo into a prefab that can silently never be spawned.
A missing bundle, a corrupt chunk, or content from a newer build than the binary still throws. That is not content being wrong; it is the build being broken.
The template is held and the asset is not
A Prefab is a template world, captured once and stamped out for the life of the build, so the
AssetHandle<PrefabAsset> is released as soon as the template exists. What a prefab's components
point at — a mesh, a material, a sound — is an AssetReference inside the component and is loaded on
the ordinary ref-counted handle path by whoever draws it.
⚠ A registered template is a world held for the process. That is why the label says *"this may arrive over the wire"* rather than "this is a prefab": a game has far more prefabs than it replicates, and there is no reason to pay for the rest.
Examples
Only the nodes that asked for an id get one — a hundred-entity set piece where one turret rotates
costs one id and one record. Asking is a NetworkObject on the node, authored on the prefab:
# Assets/Prefabs/Turret.vxprefab — the barrel replicates, the sight is scenery.version: 1name: Turretroots: - name: Turret children: - name: Barrel components: - !NetworkObject {} - name: SightThe {} is not decoration: a tag component has no members, and a node that is only a type tag binds
as a scalar rather than as a mapping.
var entry = prefabs.Require("prefabs/turret");// The root, plus every template node carrying a NetworkObject, in capture order.Console.WriteLine($"{entry.Prefab.EntityCount} entities, {entry.IdCount} ids");Why the marker is not NetworkId
A NetworkId is a number the server allocated, and a NetworkObject is a designer's claim
about content. Only the second is something an asset can hold: what a compiled scene may name is a
component that is [Component] and [DataContract], and a handle that only exists once a session
does has no business being in a file — a play-mode save would write live ids into content, and a
designer could type in one no server ever handed out.
⚠ Until NetworkObject existed, the marker was the handle, and through a content build every
prefab had exactly one networked node whatever its author said — SceneContent.Capture drops a
component the scene registry does not know, silently, so the prefab loaded, spawned, and the barrel
simply never replicated. ANetworkedMarkerSurvivesTheContentBuild in Vixen.Net.Engine.Content.Tests
is the A/B that holds it shut: the same three entities registered twice, once out of a live world and
once through a chunk, a bundle, a catalog and an AssetManager, agreeing about which two want ids.
NetworkPrefabRegistry still counts a node carrying a NetworkId as marked, and that is the
Prefab.CaptureFrom path rather than the authoring one: a live subtree that has been in a session
carries allocated ids, and capturing it as a template should not quietly drop every node in it.
Two addresses that hash alike are refused where both names are still in hand:
// Throws, naming both: two prefabs the wire could not tell apart.prefabs.Register("prefabs/crate", crate);prefabs.Register("prefabs/kratee", other);Through NetworkPrefabContent that refusal comes back as a problem rather than an exception, because
it is a property of the content rather than of the call.
See also
- networked players — the other half of spawning: a connection getting a body.
- definitions — the same label-driven load, one layer down.
- content in a game — catalogs, labels and addresses.
Core/Vixen.Net.Engine/README.md— spawning as a replicated component, and the rest of the wire.Core/Vixen.Net.Engine.Content/README.md— why the loader is an assembly of its own, and what it still owes.