Knowledge of the network format is not necessary to work with Dissonance in most cases. This documentation is only required if you want to interact with Dissonance over the network from your own non-Unity code. For example writing a Dissonance server in another language.
The Dissonance network system manages three main bits of data:
- Who is in the session
- Who is listening to which rooms
- Voice packets
This document will give you an overview of how the Dissonance network system manages this data. To see the exact packet format look at
PacketReader.cs in the Dissonance package, these structs have a method for writing/reading each different packet type.
Every different machine in the session is a peer. This include both the server and the client.
A peer which is recording and playing voice.
A which manages the organisation of the session and relays voice to clients.
A peer which is both a server and a client.
A server which is not a client (i.e. no auto recording or playback).
Some packets are sent reliably. This means that the packets will arrive at their destination in the order they were sent, there are no lost packets. This is used for all non-voice packets.
Some packets are sent unreliably. This means that the packets may be lost in transport or arrive in a different order. This is always used for voice packets.
Audio is recorded, processed and played back in frames, this is a buffer of 10-40ms of audio. Every frame is packed into a single network packet.
A room is a type of channel which requires the listener to explicitly subscribe to the room to hear any audio sent to that room. Rooms have a name (a string) but on the network rooms are generally referred to by a 16 bit ID which is calculated by the
ToRoomId(string name) method.
All packets contain a header which is used to check that the packet is valid.
The first 16 bits of every Dissonance packet are a 16 bit magic number
0x8BC7. This is read from the start of the packet and if it's incorrect the packet is immediately discarded. If something goes wrong and non-Dissonance packets are sent to Dissonance this prevents them from being decoded.
The next 8 bytes are the packet type, this tells Dissonance how the contents of the packet should be decoded. The values used for this are defined in
After that all packets (except
HandshakeRequest) have a 32 bit session number, this is a unique number randomly generated by the server when it starts a new session. If the session number does not match the packet is immediately discarded. If something goes wrong and packets from one Dissonance session are sent to another Dissonance session this prevents them from being decoded.
Kicking From A Session⚓︎
If a packet with an incorrect session number is received by the server it will send back an
ErrorWrongSession packet to the client which contains the session number being used by the server. If the client is not using this session number it will disconnect and reconnect to the server.
Joining A Session⚓︎
- A new client sends a
HandshakeRequestmessage to the server. This tells the server the codec settings in use by this client as well as it's name.
The server replies with a
HandshakeResponsemessage. This sends the complete state of the server to the client:
the session ID. A unique value prepended to all packets.
- The client ID. A unique 16 bit ID for this client.
- Client list. A list of all other clients in the session (name, codec setting, unique ID).
- Room list. A list of the room names which at least one client is currently listening to.
Listeners list. A list of clients and the rooms which they are currently listening to.
The client replies with a
ClientStatemessage. This tells the server the complete state of the client:
- Client ID
- Codec Settings
- Rooms list. A list of the rooms this client is currently listening to.
HandshakeResponse message contains data about all clients currently in the session. In a very large session this can cause a problem with oversize packets. It is valid for the server to send some/none of the client data in the initial
HandshakeResponse packet and instead to send it in individual
ClientState messages immediately after the
Joining Or Leaving A Room⚓︎
The server maintains a list of which rooms every client is currently listening to. Sending a complete
ClientState message every time a client join or leaves a room would be wasteful, instead a
DeltaClientState message is sent. This contains:
- Flag indicating
- Client ID
- Room name
The update messages
DeltaClientState are sent from clients to the server, which updates it's internal state. The server also broadcasts these messages out to all clients which update their own state. This means that every client has exactly the same list of who is listening to which rooms.
Peer To Peer⚓︎
It's possible for peers to communicate directly. When this is setup the metadata messages are still sent to the server but voice packets are sent directly from one client to another.
To set this up a client sends a
HandshakeP2P message to every peer which it knows how to directly contact. The
HandshakeP2P message contains the ID of the sending client. When a client receives a
HandshakeP2P message from another client it can take note of the connection which that message came through, send back a
HandshakeP2P message in response over that connection, and now the two peers can communicate directly.
Each client records audio, preprocesses it to improve audio quality, encodes it (using opus) and then sends the packet. The client decides who to send the packet to based on it's knowledge of who is listening to what. The client sends the voice packet via P2P to as many client as possible. The remaining packets are relayed via the server.
VoiceData packet contains:
- Sender Client ID
- 8 bit bitfield of packet flags
- Sequence number. A number which can be used to put packets into the correct order
- A list of channels which this voice is addressed to. For each channel:
- 16 bit channel bitfield
- 16 Bit channel ID
- A frame of encoded audio
The 8 bit bitfield contains:
- The MSB is always set to
- The remaining 7 bits contain a wrapping counter which increments every time the "channel session" changes. The "channel session" changes whenever all sending channels are closed (i.e. there is an interruption in the voice stream).
When packets cannot be sent directly with P2P they can be relayed via the server. The
ServerRelayUnreliable packets are used for this purpose. These packets contain a list of destination client IDs and then an array of bytes.
When the server receives one of these packets it sends the array of bytes out to all of the listed clients. The server will discard attempts to relay
Text packets can be sent through the Dissonance session, unlike voice they are always relayed via the server. The
TextData packet contains:
- Recipient type. This indicates if the packet is targeted at a player or a room.
- Sender ID. Client ID of the sender.
- Recipient ID. This is either a room ID or a client ID, depending upon the recipient type.
- A string of UTF8 encoded text.
Leaving A Session⚓︎
When a client leaves the session the server sends a
RemoveClient message out to all clients. This simply contains the ID of the client which is leaving the session.