Remote Events

Sometimes you need to communicate with specific clients directly or trigger some behavior on other clients. For this case, you can use Remote Events.

Think of it like sending a text message. You can send a packet of data directly to a single friend, or send it in a group chat and everyone will receive the message.

For example, remote events can be used to:

  • Send a message to a specific client.
  • Play a VFX on all or specific clients.

Remote Events will only be processed by clients that are currently connected to the server instance. Clients that join later will not process the event that was sent before they joined.

Note

A common mistake is to use remote events for state synchronization. This will result in late-joining clients not receiving the state that was sent before they joined. For state synchronization, use Network Variables.

C# Example

Sending an event is as simple as calling a method, which can take many arguments as custom data.

// Send a network event to all actors
const byte EVENT_ID = 0;
SpatialBridge.networkingService.remoteEvents.RaiseEventAll(EVENT_ID, "Here is the magic number!", 42);
// Send a network event to all actors
const byte EVENT_ID = 0;
SpatialBridge.networkingService.remoteEvents.RaiseEventAll(EVENT_ID, "Here is the magic number!", 42);

We currently support these types: bool, byte, short, int, long, float, double, string, Vector2, Vector3, Vector4, Quaternion, Color, Color32, DateTime and arrays of these types.

It is good practice to limit the amount of data you send with an event to the minimum required for the event to be processed. You want to keep the network traffic as low as possible.

On the receiving side, you will register a callback to handle the event.

void Start()
{
// Register for callback when a network event is received
SpatialBridge.networkingService.remoteEvents.onEvent += HandleEventReceived;
}

void OnDestroy()
{
// Don't forget to unregister the callback
SpatialBridge.networkingService.remoteEvents.onEvent -= HandleEventReceived;

}

void HandleEventReceived(NetworkingRemoteEventArgs args)
{
// Check if the event ID matches the one we are interested in
if (args.eventID == EVENT_ID)
{
// Extract the data from the event
string message = (string)args.arguments[0];
int magicNumber = (int)args.arguments[1];

Debug.Log($"Received event with message: {message} and magic number: {magicNumber}");
}
}
void Start()
{
// Register for callback when a network event is received
SpatialBridge.networkingService.remoteEvents.onEvent += HandleEventReceived;
}

void OnDestroy()
{
// Don't forget to unregister the callback
SpatialBridge.networkingService.remoteEvents.onEvent -= HandleEventReceived;

}

void HandleEventReceived(NetworkingRemoteEventArgs args)
{
// Check if the event ID matches the one we are interested in
if (args.eventID == EVENT_ID)
{
// Extract the data from the event
string message = (string)args.arguments[0];
int magicNumber = (int)args.arguments[1];

Debug.Log($"Received event with message: {message} and magic number: {magicNumber}");
}
}

To keep things organized, typically you will want to define your event IDs as an enum.

// It is good practice to always assign a value to the enum elements to avoid backwards compatibility issues when
// adding new elements to the enum as it will change the values of the existing elements.
public enum RemoteEventIDs : byte
{
SendMagicNumber = 0,
PrivateMessage = 1,
// Add more events here
}

// ..

SpatialBridge.networkingService.remoteEvents.RaiseEventAll((byte)RemoteEventIDs.SendMagicNumber, "Here is the magic number!", 42);

// ..

void HandleEventReceived(NetworkingRemoteEventArgs args)
{
switch (args.eventID)
{
case (byte)RemoteEventIDs.SendMagicNumber:
string message = (string)args.arguments[0];
int magicNumber = (int)args.arguments[1];
Debug.Log($"Received event with message: {message} and magic number: {magicNumber}");
break;
case (byte)RemoteEventIDs.PrivateMessage:
// Handle private message event
break;
// Add more cases here
}
}
// It is good practice to always assign a value to the enum elements to avoid backwards compatibility issues when
// adding new elements to the enum as it will change the values of the existing elements.
public enum RemoteEventIDs : byte
{
SendMagicNumber = 0,
PrivateMessage = 1,
// Add more events here
}

// ..

SpatialBridge.networkingService.remoteEvents.RaiseEventAll((byte)RemoteEventIDs.SendMagicNumber, "Here is the magic number!", 42);

// ..

void HandleEventReceived(NetworkingRemoteEventArgs args)
{
switch (args.eventID)
{
case (byte)RemoteEventIDs.SendMagicNumber:
string message = (string)args.arguments[0];
int magicNumber = (int)args.arguments[1];
Debug.Log($"Received event with message: {message} and magic number: {magicNumber}");
break;
case (byte)RemoteEventIDs.PrivateMessage:
// Handle private message event
break;
// Add more cases here
}
}

Visual Scripting Example

Remote events can be sent and processed in Visual Scripting too. Sending is done with the Send Event node, and receiving is done with the Receive Event node.

Imagine an invisible thread between the SendEvent and ReceiveEvent nodes.

Network events breakdown

Similar to C# code, events have an ID of type byte which allows values between 0-255. To keep your project organized, it's recommended to define your event IDs as named variables somewhere in your project, like this:

RPC ID variables

Along with the event you can send up to 5 arguments. This should be a minimal amount of data that is required for the event. For example, for an event that plays an explosion VFX you might want to send a Vector3 for the position of the explosion. You don't need to send any data that is already known to the receiving client (such as what the explosion effect name is for example).