This document is about: FUSION 2
SWITCH TO

6 - Remote Procedure Calls

Overview

Remote Procedure Calls, or RPCs, are one of most common features of any network library and its intuitive mapping to regular methods makes it an easy first choice when trying to bring multiple clients together in a shared world. Unfortunately it is often not the best choice.

RPCs can be problematic in a tick based state synchronization library like Fusion, because they are not tied to a specific tick and will execute at different times on different clients. But maybe more importantly, they are not part of the network state, so any player that connects or re-connects after an RPC was sent, or just didn't receive it because it was sent unreliably, will never see the consequences of it.

In most cases state synchronization itself is sufficient to keep players aligned, and adding an OnChange listener to a networked property can handle most transitional cases where the application cares about a change in state and not just the actual state itself.

Still, there are use-cases where RPCs are a good choice, here are a few examples:

  1. Sending a taunt message or other volatile non gameplay interaction between players.
  2. Purchasing gear from an in-game shop where exact timing is not important, and the direct result of the RPC call (deduction of funds and adding to inventory) are only important to the player that made the call. (I.e. don't use an RPC to equip said purchase)
  3. Setting initial player properties like name, color, skin. (I.e. anything that is a direct result of "rare" input from the player. Basically any player input that you would not want to have in the per-tick Input struct)
  4. Launching a game (voting on gamemode, map, or just indicating to the host that a player is ready).

Consult the Manual for an in-depth description of this topic

Fusion RPCs

In the few cases where RPCs are the right option, Fusion makes it very simple. Just tag a conventional method on any NetworkBehaviour with the RPC attribute and indicate who may call the method as well as who shall receive the call. Make sure the method has "RPC" as prefix or postfix (no particular capitalization) and you are ready to call it.

The goal in this small example is to send a "Hello Mate!" message to all other players when pressing the R key.

Calling the RPC

First add a text field to your scene:

GameObject > UI > Text - TextMeshPro (import TextMeshPro Essentials if necessary). Change the size of the text field to fill the whole screen so it will be easier to read the text.

Before adding the RPC itself, the input handling needs to be extended. As the RPC call is the actual networked message, there is no need to extend the input struct. Also, as RPCs are not tick aligned anyways, there is no need to use Fusions input handling, so open Player.cs and add:

C#

private void Update()
{
  if (Object.HasInputAuthority && Input.GetKeyDown(KeyCode.R))
  {
    RPC_SendMessage("Hey Mate!");
  }
}

Note the check for Object.HasInputAuthority - this is because this code runs on all clients, but only the client that controls this player should call the RPC.

RPC Implementation

While still in Player.cs, also add the RPC body itself. It gets tagged with the [Rpc] attribute and the method name begins with "RPC". Like this:

C#

private TMP_Text _messages;

[Rpc(RpcSources.InputAuthority, RpcTargets.StateAuthority, HostMode = RpcHostMode.SourceIsHostPlayer)]
public void RPC_SendMessage(string message, RpcInfo info = default)
{
    RPC_RelayMessage(message, info.Source);
}

[Rpc(RpcSources.StateAuthority, RpcTargets.All, HostMode = RpcHostMode.SourceIsServer)]
public void RPC_RelayMessage(string message, PlayerRef messageSource)
{
    if (_messages == null)
        _messages = FindObjectOfType<TMP_Text>();

    if (messageSource == Runner.LocalPlayer)
    {
        message = $"You said: {message}\n";
    }
    else
    {
        message = $"Some other player said: {message}\n";
    }
    
    _messages.text += message;
}

Why are there two RPCs here instead of just a single one? This is because Fusion Host Mode is a star topology. Clients have no way to send data to other clients directly using an RPC. Clients can only send RPCs to the Host which then must relay the message to all clients.

Let's have a look at the attribute of the SendMessage RPC:

  • RpcSources.InputAuthority => Only the client with input authority over an object can trigger the RPC to send the message.
  • RpcTargets.StateAuthority => The SendMessage RPC is sent to the Host (StateAuthority).
  • RpcHostMode.SourceIsHostPlayer => Since a Host is both a server or a client it needs to be specified which one is calling the RPC.

And for the RelayMessage RPC:

  • RpcSources.StateAuthority => Server/Host is sending this RPC.
  • RpcTargets.All => All clients should receive this RPC.
  • HostMode = RpcHostMode.SourceIsServer => The server part of the host application is sending this RPC.

With that in place whenever a player presses the R key a message is transmitted to each client.

Playing the game

With that the Host Mode Basics tutorial is complete. Create a build and enter play mode on two clients. One as the host, the other as the client. The player can move around, spawn spheres with their mouse buttons and send RPCs by pressing the R key.

Next Host Mode Basics 7 - Where to go Next

Back to top