Networking

Synced values that tell you when they change, and typed messages anyone can send, with nothing to wire up.

Updated 2026-09-10

Two clients in a world have to agree on something. VRChat gives you two ways to do that, and they solve different problems badly if you pick the wrong one.

A synced variable is a fact that everybody holds. A score, a door's open or shut, whose turn it is. It survives: somebody walking in ten minutes late gets the current value without anybody resending it.

A message is something that happened once. A hit landed, a round started, a button was pressed. Sending it as a variable goes wrong in ways that look like nothing at all: assigning the same value twice is one change, so two identical hits become one; a late joiner receives the last hit of a fight they missed as though it just happened; and only the owner may write, so two players hitting the same target fight over the variable and one hit disappears with nothing said.

This package is both, with the parts that usually go wrong handled.

public class Door : UdoniteBehaviour
{
    private Synced<bool> open = new Synced<bool>();

    public override void Start()
    {
        open.Subscribe(OnOpenChanged);
        Room.Subscribe<Knocked>(OnKnocked);
    }

    public override void Interact()
    {
        open.Value = !open.Value;      // replicated, and every client's handler runs
        Room.Send(new Knocked());      // heard once by everybody, sender included
    }

    private void OnOpenChanged(bool value) => panel.SetActive(value);

    private void OnKnocked(Knocked message) => sound.Play();
}

Room.Send goes to the whole room, and anybody may send. A message about one particular object travels on that object instead, written as this.Send on the behaviour that is the object, and that one requires owning it. Messages has both.

open.Value = ... writes the variable and asks VRChat to replicate it; there is no RequestSerialization to forget. Subscribe runs your handler on every client when the value arrives, once the whole update has landed rather than while half of it is still the old value.

Who needs to own what

Ownership is VRChat's, not this package's: only the owner's write to an object replicates. What differs is what each thing does when you are not the owner.

what you do who has to own it if you do not
Write a Synced<T> value the object holding the field the write stays on your client, and the next value to arrive overwrites it
this.Send on this object's channel this object nothing is staged and nothing is delivered, said once in the console
channel.Send on a NetworkBus that bus's object the same
Room.Send nobody every client writes to its own player object's bus, so there is nothing to contend for
Move a NetworkTransform the object the owner sends and everybody else interpolates towards it

A room message is the one that needs no owner, which is the main reason to prefer it when any client may act. An object's channel and a synced field both come down to one writer, so a world where anybody may act on the object claims it first:

if (!Ownership.IsMine(gameObject))
    Ownership.Claim(gameObject);

A pickup or an Interact usually already claims it. MasterOwned is the opposite case: derive from it and only the arbitrating client can own the object, for state nobody else may write.

What is here

Synced<T> A variable every client holds, that tells you when it changes
Room Send and subscribe to typed messages; who is here, who is arbitrating
NetworkEvent A message with fields, rather than a bag of bytes
NetworkBus One object's own message channel, when a message is about that object
NetworkTransform Position, rotation and scale, interpolated, with teleport detection
Ownership Who owns an object, and taking or giving it
MasterOwned A base class for an object only the arbitrating client may own

What you do not have to set up

Room messages need one object in the scene to carry them, because a message has to be written to something a client owns and nothing can create that while a world runs. The package adds it for you the first time a behaviour in your world sends or subscribes, and says so once in the console. A world that never sends gets nothing added to it.

If you would rather place it yourself, put a NetworkBus on an object carrying VRCPlayerObject and yours is used instead. It is found by its components, never by its name.

Udonite → Open Window → Settings → Net has a toggle for this: turn it off and the package never touches the scene, whether or not your world sends anything — placing the bus is then entirely yours to do. On is the default. This one setting lives in ProjectSettings/, committed like the rest of what the project excludes, because it is a fact about who owns the scene rather than one developer's own preference.

Something went wrong Reload