Skip to main content

Relay

Netcode for GameObjects (Netcode) allows you to connect to a host by its IP and port. However, if the host is not on the same network as you (i.e. somewhere over the Internet), you will need some extra services to achieve a successful connection and thus a successful game.

Many factors impact how you connect to the remote host, and it can be tricky to implement. There are two ways of achieving such a connection:

  • NAT punching: an advanced technique that allows you to directly connect to the host computer even if it is on another network.
  • Usage of a Relay server: the server is on the Internet with a public-facing IP that you and the host can reach. After the connection to the Relay, it will pass along the messages to all the other party members connected.

Netcode does not offer tools to help you do a successful punch through a NAT. But, Unity Services provides a Relay Service that can relay all Unity Transport based technology, like Netcode.

How does Relay work?#

You need to install the Unity Relay SDK if you want to interact with the service. To install it, add com.unity.services.relay package to your project.

The host of your game needs to allocate a Relay first. This process will create the Relay server and allow the clients to connect to it. Once done, you will be able to get a join code from your server. It is a random string that your clients will provide to the SDK to join the correct server.

How do I enable Relay for my project?#

The first thing that needs to be understood is that Relay is a Unity service. Thus, you need to add it to your organization in the Unity Dashboard (under the multiplayer section). After that, link your project to your organization (Project Settings -> Services).

How do I allocate a server?#

To allocate a Relay server, you need to make an authenticated call to Unity backend using their SDK. Call the CreateAllocationAsync method with the maximum number of users your Relay will handle. This function can throw exceptions, and catching them can give you hints about the underlying error.

//Ask Unity Services to allocate a Relay server that will handle eight players
Allocation allocation = await Unity.Services.Relay.Relay.Instance.CreateAllocationAsync(8);

The Allocation class represents all the necessary data for a Host player to start hosting using the specific Relay server allocated. You don't need to understand each part of this allocation. You will feed them to your chosen transport that will handle the relay communication on its own. For the more curious and for reference, here is a simple overview of those parameters :

  • A RelayServer class containing the IP and port of your allocated server
  • The allocation ID in a Base64 form and GUID form referred to as AllocationIDBytes and AllocationID A blob of encrypted bytes representing the connection data (known as ConnectionData) allows users to connect to this host.
  • A Base64 encoded Key for message signature.

Each allocation created a unique join code which is a simple string. This join code will allow your clients to join your game. You can retrieve it by calling the Relay SDK like so :

string joinCode = await Unity.Services.Relay.Relay.Instance.GetJoinCodeAsync(allocation.AllocationID);

With those two calls, you now have your Relay server ready and the associated join code. Pass the allocation parameters to your host transport and send the join code (a simple string) over the Internet by the mean of your choice to your clients. Finally, remember always to authenticate your users before using SDK methods. The easiest way is the anonymous one (shown in the following code snippet), but you can use more advanced techniques.

//Initialize the Unity Services engine
await UnityServices.InitializeAsync();
if (!AuthenticationService.Instance.IsSignedIn)
{
//If not already logged, log the user in
await AuthenticationService.Instance.SignInAnonymouslyAsync();
}

To sum up, here comes the whole code that allows working with the Relay server.

/// <summary>
/// RelayHostData represents the necessary informations
/// for a Host to host a game on a Relay
/// </summary>
public struct RelayHostData
{
public string JoinCode;
public string IPv4Address;
public ushort Port;
public Guid AllocationID;
public byte[] AllocationIDBytes;
public byte[] ConnectionData;
public byte[] Key;
}
/// <summary>
/// HostGame allocate a Relay server and returns needed data to host the game
/// </summary>
/// <param name="maxConn">The maximum number the Relay can have</param>
/// <returns>A Task returning the needed hosting data</returns>
public static async Task<RelayHostData> HostGame(int maxConn)
{
//Initialize the Unity Services engine
await UnityServices.InitializeAsync();
//Always autheticate your users beforehand
if (!AuthenticationService.Instance.IsSignedIn)
{
//If not already logged, log the user in
await AuthenticationService.Instance.SignInAnonymouslyAsync();
}
//Ask Unity Services to allocate a Relay server
Allocation allocation = await Unity.Services.Relay.Relay.Instance.CreateAllocationAsync(maxConn);
//Populate the hosting data
RelayHostData data = new RelayHostData
{
Key = allocation.Key,
Port = (ushort) allocation.RelayServer.Port,
AllocationID = allocation.AllocationId,
AllocationIDBytes = allocation.AllocationIdBytes,
ConnectionData = allocation.ConnectionData,
IPv4Address = allocation.RelayServer.IpV4
};
//Retrieve the Relay join code for our clients to join our party
data.JoinCode = await Unity.Services.Relay.Relay.Instance.GetJoinCodeAsync(data.AllocationID);
return data;
}

How do I join a pre-allocated server?#

The host of your game allocated the Relay server, and your client has received its join code. You now need to request all the allocation parameters from the join code to join the game. To do that, call the JoinAllocationAsync method with your join code like so :

//Ask Unity Services to join a Relay server based on our join code
JoinAllocation allocation = await Unity.Services.Relay.Relay.Instance.JoinAllocationAsync(joinCode);

The JoinAllocation type is similar to the Allocation one used before with the host. This type adds a blob of bytes which is the HostConnectionData that you will need to give to the transport of your choice so you can connect. Finally, like for the hosting part, always remember to authenticate your user beforehand. Here is the code often used to join a Relay :

/// <summary>
/// RelayHostData represents the necessary informations
/// for a Host to host a game on a Relay
/// </summary>
public struct RelayJoinData
{
public string JoinCode;
public string IPv4Address;
public ushort Port;
public Guid AllocationID;
public byte[] AllocationIDBytes;
public byte[] ConnectionData;
public byte[] HostConnectionData;
public byte[] Key;
}
/// <summary>
/// Join a Relay server based on the JoinCode received from the Host or Server
/// </summary>
/// <param name="joinCode">The join code generated on the host or server</param>
/// <returns>All the necessary data to connect</returns>
public static async Task<RelayData> JoinGame(string joinCode)
{
//Initialize the Unity Services engine
await UnityServices.InitializeAsync();
//Always autheticate your users beforehand
if (!AuthenticationService.Instance.IsSignedIn)
{
//If not already logged, log the user in
await AuthenticationService.Instance.SignInAnonymouslyAsync();
}
//Ask Unity Services for allocation data based on a join code
JoinAllocation allocation = await Unity.Services.Relay.Relay.Instance.JoinAllocationAsync(joinCode);
//Populate the joining data
RelayJoinData data = new RelayJoinData
{
Key = allocation.Key,
Port = (ushort) allocation.RelayServer.Port,
AllocationID = allocation.AllocationId,
AllocationIDBytes = allocation.AllocationIdBytes,
ConnectionData = allocation.ConnectionData,
HostConnectionData = allocation.HostConnectionData,
IPv4Address = allocation.RelayServer.IpV4
};
return data;
}

How do I pass allocation data to my transport?#

Now that you have an allocation (either by joining or hosting), you need to make all traffic coming from Netcode go through the Relay. And to achieve that, you need to pass allocation parameters to your transport. For now, only the UnityTransport supports the Relay protocol. To pass allocation parameters to it, you first need to retrieve it from your NetworkManager like so :

//Retrieve the Unity transport used by the NetworkManager
UnityTransport transport = NetworkManager.Singleton.gameObject.GetComponent<UnityTransport>();

You can now call the method SetRelayServerData on the retrieved transport with all the allocation parameters in this order :

  1. The relay server IP address, found in the RelayServer property of the allocation
  2. The relay server port, found in the RelayServer property of the allocation
  3. The allocation ID in Base64 encoded bytes.
  4. The key in Base64 encoded bytes.
  5. The connection data in your allocation
  6. If you are joining a game: the host connection data retrieved during your call to join. Make special attention since the host connection data is an optional parameter but must be pass in case of join.

Your transport is now correctly configured. You can call StartClient or StartHost and use the Netcode library as usual.

For more information see Unity Relay