## Declaring a message

A message is a class with fields and a pair of methods that say how they travel:

```csharp
public class OpenDoor : NetworkEvent
{
    public int doorId;

    public override void Serialize(ByteWriter writer)
    {
        writer.WriteInt32(doorId);
    }

    public override void Deserialize(ByteReader reader)
    {
        doorId = reader.ReadInt32();
    }
}
```

Packing is yours because guessing it would cost more than writing it: Udon has no reflection, so
anything that serialized for you would have to be handed a description of every field anyway. A
signal with no fields overrides neither method.

**Separate the wish from the fact.** `OpenDoorRequest` and `DoorOpened` as two types is almost
always better than one type sent twice. A handler then knows which it has from the type, and cannot
act on somebody's request as though it were settled.

## Two places a message can go

A message goes either to **the whole room** or to **one object's own channel**. That is the entire
choice, and what you write says which:

```csharp
Room.Send(new OpenDoor { doorId = 3 });   // the room: every client
this.Send(new Touched());                 // this object's own channel
channel.Send(new Touched());              // another object's channel, by reference
```

**`this.Send` is not `Room.Send`.** It sends on the channel belonging to the object the behaviour is
on, so nothing has to carry an id saying which object was meant. `Room.Send` is the room-wide one,
and being static it also works from a plain or abstract class, which has no `this` to hang a call
off.

Subscribing mirrors it exactly:

```csharp
Room.Subscribe<OpenDoor>(OnOpenDoor);     // hear that type from the room
this.Subscribe<Touched>(OnTouched);       // hear that type on this object's channel
channel.Subscribe<Touched>(OnTouched);    // hear that type on another object's channel
```

| what you write | where it goes | who hears it |
|---|---|---|
| `Room.Send(evt)` | the room | everything subscribed to that type, on every client |
| `this.Send(evt)` | this object | everything subscribed to that type on this object |
| `channel.Send(evt)` | that one object | everything subscribed to that type on that channel |

The room is the one to reach for when anybody may send. An object's own channel has a single owner,
so **`this.Send` requires owning the object** — see [below](#one-objects-own-channel).

## Who acts on it

Addressing decides who **acts**, never who may **send**:

```csharp
Room.Send(message, NetworkTarget.All);         // everybody, the default
Room.Send(message, NetworkTarget.Master);      // only the client arbitrating
Room.Send(message, NetworkTarget.Others);      // everybody but the sender
Room.Send(message, player);                    // one player
```

Any client can put any message on the wire addressed however they like, and every client receives
the bytes either way. `NetworkTarget.Master` is a filter, not a permission, and a world that needs
real authority checks who sent it:

```csharp
private void OnOpenDoorRequest(VRCPlayerApi sender, OpenDoorRequest message)
{
    if (!Room.IsMaster || !Allowed(from))
        return;

    Room.Send(new DoorOpened { doorId = message.doorId });
}
```

That is the master-as-server shape: a client asks, the arbitrating client decides, and its own
message to everybody is what actually changes anything.

## What you get

**In order, once each, sender included.** A client acts on its own message immediately, because a
client never receives its own writes and would otherwise be the only one that never heard.

**A batch that arrives twice acts once.** Every message carries a sequence number and each reader
remembers where it got to, so a resend after VRChat refused the first attempt changes nothing.

**A late joiner hears nothing they missed.** Each batch carries when it was made, and a client that
joined later drops one stamped before it arrived rather than replaying a fight it missed. That is
the opposite of what a synced value should do, and exactly what an event should: use
[a synced value](/docs/net/synced-values) when a late joiner *should* learn the current state.

**Nothing is lost to a refused send.** VRChat caps how much one behaviour may send at once, and a
batch over that cap is refused rather than truncated. A refused batch is kept and retried. A world
that keeps `Room.Bus.Pending` above zero is sending faster than the wire will take it.

## One object's own channel

A message about a particular object can travel on that object instead of on the room, so nothing
has to carry an id saying which one it meant. Write the same two verbs on the behaviour itself,
with no component to add and nothing to reference:

```csharp
public override void Start()
{
    this.Subscribe<Touched>(OnTouched);
}

public override void Interact()
{
    this.Send(new Touched());
}
```

A handler can be told who sent it by taking the player as well:

```csharp
this.Subscribe<Touched>((sender, message) => Log.Info(sender.displayName + " touched it"));
```

**Sending requires owning the object.** Only the owner's write replicates, so a non-owner is told
now rather than finding out its message never left. Claim it first
(`Ownership.Claim(gameObject)`) if any client should be able to send; a pickup or an interact
usually already has.

To read another object's channel from somewhere else, that object needs a `NetworkBus` on it, and
the same verbs work against the reference:

```csharp
public NetworkBus channel;

channel.Subscribe<Touched>(OnTouched);
channel.Send(new Touched());
```

Either way an object channel has a single owner, so two players sending on the same one contend for
it. Use an object channel when the object's owner is the one sending, and the room when anybody may
send.

## What it costs

Three bytes of overhead per message: one saying who should act on it, one length, one saying which
type it is. A message addressed to a particular player carries that player's id as well.

A batch pays four bytes once, for the stamp saying when it was made. Once per batch and never per
message, so batching several messages spreads it.

The budget that actually bites is VRChat's cap on one behaviour's synced state, which applies to a
whole batch rather than to one message. `this.Pending()` is how a world sees it coming: zero almost
always, and staying above zero means the batches are larger than one sync will take.

## Custom events, without this package

VRChat's own event call is there whether or not you use any of the above, because the compiler
passes it through:

```csharp
this.SendCustomNetworkEvent(NetworkEventTarget.All, nameof(Ring));

public void Ring()
{
    bell.Play();
}
```

It calls a **public** method by name on every client, or on the owner with
`NetworkEventTarget.Owner`. It carries no data, which is the whole reason typed messages exist.
`SendCustomEventDelayedSeconds` and `SendCustomEventDelayedFrames` schedule a local call later and
send nothing.

`OnOwnershipRequest` can be overridden to return `false` and refuse a transfer, and
`OnOwnershipTransferred` runs on every client when an owner changes. ClientSim does not raise
ownership requests, so that pair has to be tested in an uploaded world.

## When nothing arrives

The package says so once per behaviour, rather than leaving you to guess:

- Sending with no bus in the scene, so the message went nowhere.
- Subscribing, then finding no bus anywhere to read for ten seconds.

Both mean the same thing in practice: the object that carries room messages is missing or disabled.
It is added for you when a world first sends, so the usual cause is that it was switched off along
with something else.
