Skip to main content

RPC (Remote Procedure Calls)

info

Please note that this feature is still under development and breaking changes may occur in the future.

RPC serves as a mean to perform a directed reliable one-time action. What does it mean?

General usage

RPC-enabled methods have to be marked with the [ElympicsRpc] attribute. Any method is suitable as long as it meets the following criteria:

  • it belongs to a class inheriting from ElympicsMonoBehaviour,
  • it has void return type,
  • its parameter list consists only of primitive types and strings, without in, out or ref modifiers,
  • it is an instance (non-static) method,
  • it is a concrete method without virtual, abstract or override modifiers,
  • the class it belongs to is not a part of a custom assembly; only the default scripting assembly (Assembly-CSharp.dll) is scanned by Elympics.

Such a method can be called only from a synchronized object (i.e. implementing IObservable directly or indirectly and being part of a game object with the ElympicsBehaviour component attached). As RPCs are tick-aligned, a call must be performed inside:

  • ElympicsUpdate method after implementing IUpdatable,
  • Initialize method after implementing IInitializable, or
  • physics callbacks.
RPC API

An RPC method call looks like any other plain method call, but Elympics modifies the compiled code so the called method is not run immediately. Instead, a message containing all passed arguments is created and then sent to the target instance. Only there the method body is finally executed.

The direction of RPC is decided by passing ElympicsRpcDirection.ServerToPlayers or ElympicsRpcDirection.PlayerToServer to the [ElympicsRpc] attribute marking the method. Additionally, RPC methods should be scheduled only after performing if (Elympics.IsServer) or if (Elympics.IsClient || Elympics.IsBot) check, respectively.

Negative checks

It may be tempting to use if (!Elympics.IsServer) instead of if (Elympics.IsClient || Elympics.IsBot), however you should not do that because for example both Elympics.IsBot and Elympics.IsServer are set to true for bot instances.
If you don't split the checks, you can use the Elympics.IsClientOrBot convenience property.

RPC actions are sent to the target at the end of a tick. Received RPCs are executed at the beginning of a tick, before running ElympicsUpdate (or after applying the authoritative state on clients with prediction disabled). You can find more details in our Game loop article. Keep in mind that several ticks may pass between scheduling and executing an action as the information has to be transmitted over the network. However, we make sure no RPC executes before its associated tick.

Server-to-players calls

The purpose of server-to-players RPCs is to run audiovisual effects not affecting the game state. Such an RPC serves as an alternative to the ElympicsVar.ValueChanged callback which is unreliable and may be run multiple times during reconciliation.

Do not change state in server-to-players RPCs

Remember that client instances cannot modify the game state by themselves as all the changes would be overwritten by a server-authoritative snapshot.
That's why server-to-players RPCs (which are executed on clients) are not meant to modify the game state and only run some special effects.
If you want to modify the state, just do it directly on the server instance, without using RPCs.

The following code example demonstrates how to use a server-to-players RPC.

public class PlayerHealthController : ElympicsMonoBehaviour
{
[SerializeField] private PlayerData playerData;
private readonly ElympicsInt _health = new(100);
private readonly ElympicsBool _dead = new();

public void OnCollisionEnter(Collision other)
{
var bullet = other.transform.GetComponentInParent<Bullet>();
if (bullet == null)
return;

_health.Value -= 20;
if (_health.Value < 0)
{
_dead.Value = true;
if (Elympics.IsServer) // ensuring the RPC is scheduled by the server instance to execute on all client instances
AddKillLogEntry(bullet.Owner.Nickname, playerData.Nickname); // scheduling the RPC
}
}

[ElympicsRpc(ElympicsRpcDirection.ServerToPlayers)] // specifying the direction
private void AddKillLogEntry(string killer, string victim)
{
// the contents of this method will be run on client instances
killLog.AddEntry(killer, victim);
}

{...}
}
Using server-to-players RPCs with bots

Bots are run as a part of the server so they don't execute server-to-players RPCs which are meant to trigger audiovisual effects. Such effects would not be visible/audible on the headless server instance.

Player-to-server calls

Player-to-server RPC is a user-friendly way of sending one-time inputs (e.g. sending your nickname, choosing a team or marking yourself as ready). Using the traditional input mechanism would require additional boilerplate logic to ensure the data is reliably received only once.

Using player-to-server RPCs instead of all inputs

Note that RPCs should not be used to replace all the inputs as they are costly to send over the network and may introduce unwanted lag when used every tick.

RPCs are not scheduled during reconciliation. Keep this in mind – with prediction enabled, RPC calling logic may be based on non-confirmed state and cannot be "fixed" (i.e. RPCs cannot be unscheduled or have their source tick number changed).

Player-to-server RPCs are executed on the server so it is safe to modify the game state with them.

Security of RPC-based inputs

Currently, there is no mechanism preventing one of the players from sending player-to-server RPCs on behalf of another player. That means that for the time being RPCs are insecure and therefore should not be used for handling game logic e.g. in paid competitions.
Necessary security measures are to be introduced in the near future.

These RPCs may only be called after performing if (Elympics.IsClient || Elympics.IsBot) check. The check may be split into separate if (Elympics.IsClient) and if (Elympics.IsBot) checks, as presented in the example below.

public class PlayerData : ElympicsMonoBehaviour, IInitializable
{
private readonly ElympicsString _nickname = new();
public string Nickname => _nickname.Value;

public void Initialize()
{
if (Elympics.IsClient) // ensuring the RPC is scheduled by a client instance to execute on the server instance
SetNickname(PlayerPrefs.GetString("Nickname")); // scheduling the RPC
if (Elympics.IsBot) // ensuring the RPC is scheduled by a bot to execute inside the server instance
SetNickname("Bot"); // scheduling the RPC
}

[ElympicsRpc(ElympicsRpcDirection.PlayerToServer)]
private void SetNickname(string nickname)
{
// the contents of this method will be run on the server
_nickname.Value = nickname; // the variable will be changed on the server and then propagated to players in a snapshot
}

{...}
}
Using player-to-server RPCs with bots

Player-to-server RPCs scheduled by bots are executed directly without a delay as bots are a part of the server.