Network messages
Note
This is part of the Low Level Transport for advanced users only.
After you have succesfully established a connection, it is time to send and receive messages. Every message that is sent must implement the IMessage interface to define send mechanics (reliablity, order, etc) and serialization (how the message is converted into a sequence of bytes).
Message properies
There are five properies you need to define for every message:
- Timestamp
- Timed
- Reliable
- Ordered
- Unique
- Channel
Timed messages
A message is timed if IMessage.Timed
is true. A timed message includes accurate timings for the HostTimestamp field in MessageReceived when the message is received. This timestamp tells the receiver when the message was created. If the message is not timed, the timestamp is approximated using round trip time.
Message timestamp
If the message is timed, you can provide a timestamp that tells the netcode when the message was created. This is useful when you relay messages across several peers but want to keep the original timestamp.
Reliable messages
A message is reliable if IMessage.Reliable
is true. Whenever you send a reliable message, it keeps being sent until it is acknowledged. If an acknowledgment never comes, the connection is timed out and a disconnect event is triggered.
Ordered messages
A message is ordered if IMessage.Ordered
is true. In transit, messages can get lost or their order can get mixed up. If you set a message to be ordered, the remote peer makes sure that the message is received in order within its channel. If an unreliable message comes out of order, it is ignored. Reliable ordered mesages however always get delivered in order and are never dropped. Make sure you do not send any unoredered messages over the same channel that you send ordered messages.
Unique messages
A message is unique if IMessage.Unique
is true. In some cases, messages can get duplicated, such as when a reliable message is resent. Marking a message as unique ensures that any duplicated message is ignored and the listener will not get notified of it. It is recommended that you set this to true for all reliable messages.
Message channel
Different types of messages can be sent over different channels. There are 255 channels available, you can use any of them.
Serializing messages
Every message needs to implement a public void Write(Writer writer)
method so the netcode can convert the message to a sequence of bytes and send it. This method is called asynchronously in the background, so make sure you do not use any Unity API's in it. The method can also be called multiple times (if the message needs to be sent again). It is recommended that you keep your messages immutable (after creation, don't change their contents).
Recommended pattern
The recommended way of sending and receiving messages is to create an enum backed by a byte that contains all message types, and a separate class for each message. The channel of each message should come from the enum. This allows you to then convert the channel in the receiving method back into the message type and then read the appropriate message.
Defining messages
The following example defines two reliable unique messages for sending and receiving:
// Message channels
public enum MessageType : byte {
MessageChat = 3,
MessageSpawn = 4,
}
// A message with a string
public class MessageChat : IMessage {
public HostTimestamp Timestamp => Host.Now;
public bool Timed => false;
public bool Reliable => true;
public bool Ordered => false;
public bool Unique => true;
public byte Channel => MessageType.MessageChat;
public string Message;
public void Read(Reader reader) {
Message = reader.ReadString();
}
public void Write(Writer writer) {
writer.Write(Message);
}
}
// A message with player name and spawn information
public class MessageSpawn : IMessage {
public HostTimestamp Timestamp => Host.Now;
public bool Timed => false;
public bool Reliable => true;
public bool Ordered => false;
public bool Unique => true;
public byte Channel => MessageType.MessageSpawn;
public string Name;
public Vector3 Position;
public Quaternion Rotation;
public void Read(Reader reader) {
Name = reader.ReadString();
Position = reader.ReadVector3();
Rotation = reader.ReadQuaternion();
}
public void Write(Writer writer) {
writer.Write(Name);
writer.Write(Position);
writer.Write(Rotation);
}
}
Sending messages
To send messages, simply use the Peer.Send
method on a connected peer:
Peer peer;
// ... connect to peer ...
// Create message
MessageChat message = new MessageChat() {
Message = "Hello world!"
};
// Send message
peer.Send(message);
Receiving messages
To receive messages, listen for a IPeerListener.OnPeerReceive
event, check the channel and read the message.
private void OnPeerReceive(Peer peer, Reader reader, MessageReceived info) {
// Convert channel to message type
MessageType type = (MessageType) info.Channel;
// Switch based on received message type
switch (type) {
case MessageType.MessageChat:
// Read chat message
MessageChat msgChat = new MessageChat();
msgChat.Read(reader);
Console.WriteLine("Peer " + peer.Remote + " says: " + msgChat.Message);
break;
case MessageType.MessageSpawn:
// Read spawn message
MessageSpawn msgSpawn = new MessageSpawn();
msgSpawn.Read(reader);
Console.WriteLine("Peer " + peer.Remote + " spawns at " + msgSpawn.Position);
break;
}
}
Message events
It is possible to listen for message events by implementing IMessageListener in the message. The following example creates an empty reliable message, send its, and listens for message events.
public class MessageDebug : IMessage, IMessageListener {
public HostTimestamp Timestamp => Host.Now;
public bool Timed => false;
public bool Reliable => true;
public bool Ordered => false;
public bool Unique => true;
public byte Channel => 0;
public void Read(Reader reader) {}
public void Write(Writer writer) {}
public void OnMessageSend(Peer peer, MessageSent message) {
Debug.Log("Mesage sent to " + peer);
}
public void OnMessageAcknowledge(Peer peer, MessageSent message) {
Debug.Log("Mesage acknowledged by " + peer);
}
}
Peer peer;
// ... connect to peer ...
// Create and send message
MessageChat message = new MessageDebug();
peer.Send(message, message);