SNetworkActions

Network Actions allow you to sync variables or to call functions over the network. To use network actions, you must have a script that inherits from SNetworkBehaviour. Then, you need to declare:

private SNetworkActions actions;

You need to link the behavior and Register() your actions inside OnReady or OnSpawn

If you register inside OnSpawn, this means the actions will only work when the object is spawned. You should Clear() them inside OnDespawn() to avoid dupplicates if the object is spawned again.

If you register inside OnReady, the actions will work locally even if the object is not spawned. And will work remotely when the object is spawned. Clear() does not need to be called since everything will be cleared when you disconnect or the object is destroyed.

Registering Actions

Actions that work whether the object is spawned or not

protected override void OnReady()
{
    actions = new SNetworkActions(this);
    actions.Register("damage", DoDamage);
    actions.RegisterSerializable("refresh", DoRefresh);
}

Actions that work only when the object is spawned

protected override void OnSpawn()
{
    actions = new SNetworkActions(this);
    actions.RegisterInt("damage", DoDamage);
    actions.RegisterSerializable("refresh", DoRefresh);
}
protected override void OnDespawn()
{
    actions.Clear();
}

Triggering Actions

The Register function that you call will determine which parameter should be added to the function called on Trigger.

public void Damage(int damage)
{
    actions?.Trigger("damage", 1); //Will call DoDamage();
}

private void DoDamage(int damage)
{
    hp -= damage;
    if (hp <= 0)
        Kill();
}
public void Refresh()
{
    TowerRefreshData refresh = new TowerRefreshData();
    refresh.player_id = player_id;
    refresh.hp = hp;

    actions?.Trigger("refresh", refresh); //Will call DoRefresh
}

private void DoRefresh(SerializedData sdata)
{
    TowerRefreshData refresh = sdata.Get<TowerRefreshData>();
    player_id = refresh.player_id;
    hp = refresh.hp;
}

SerializedData

When you register your action, the function you call will determine the parameter the callback should have. For example, RegisterInt, RegisterFloat, RegisterString will callback a function with a int/float/string parameter respectively. If you want to pass more than one parameter. You must RegisterSerializable() and create a INetworkSerializable class to contain all the variables. The callback function will receive a SerializedData and you simply need to call Get<>() to get your class.

protected override void OnSpawn()
{
    actions = new SNetworkActions(this);
    actions.RegisterSerializable("refresh", DoRefresh);
}
private void DoRefresh(SerializedData sdata)
{
    TowerRefreshData refresh = sdata.Get<TowerRefreshData>();
    ...
}

And the data class

public class TowerRefreshData : INetworkSerializable
{
    public int player_id;
    public int hp;

    public void NetworkSerialize<T>(BufferSerializer<T> serializer) where T : IReaderWriter
    {
        serializer.SerializeValue(ref player_id);
        serializer.SerializeValue(ref hp);
    }
 }

NetworkDelivery

When registering an action, you can select a different NetworkDelivery, this will determine if the message is Reliable or Unreliable (faster). You can also sequence messages or fragment them.

Check Netcode documentation for the different type of delivery: NetworkDelivery

For really big data, ReliableFragmentedSequenced must be used otherwise it will exceed the maximum size of a single network message. Keep in mind that ReliableFragmentedSequenced Cannot be forwarded.

NetworkActionTarget

You can also select a target, which determines who should execute the actions. For example the target All will execute the code on both the server and clients. While the target Server will execute it only on the server

protected override void OnReady()
{
    actions.Register("damage", DoDamage, NetworkDelivery.Reliable, NetworkActionTarget.ServerAndSelf);
    actions.RegisterSerializable("refresh", DoRefresh, NetworkDelivery.Unreliable, NetworkActionTarget.Clients);
}

If a client (or server) triggers an action that targets itself, it will run it locally, if it targets something else, it will send the action through the network.

NetworkActionTarget

  • All: Action will be executed on all clients and server.

  • Clients: Action will be executed on all clients only (not the host).

  • Server: Action will be executed on server only.

  • ServerAndSelf: Action will be executed both locally and on the server (but will not be forwarded to other clients).

  • Single: Single Target, use SetTarget(client_id) to choose the target before triggering, must be triggered from server if targeting a client other than self.

Authority

Trigger requests coming from clients that do not have authority will be ignored. A device has authority if it is the owner of the NetworkObject or if it is the server. Actions can still be triggered locally when the client does not have authority. The server has authority on everything.

If a client with authority triggers an action that target other Clients, the server will receive it and then forward the action to all clients. (Messages cant be sent directly from client to client, and must be forwarded by the server).

Example

protected override void OnReady()
{
    //Define all the actions here
    actions = new SNetworkActions(this);
    actions.RegisterVector(ActionType.OrderMove, DoMoveTo, NetworkDelivery.Reliable, NetworkActionTarget.All);
}

public void MoveTo(Vector3 pos)
{
    //This function can be called from either the client or the server
    actions.Trigger(ActionType.OrderMove, pos); 
}

private void DoMoveTo(Vector3 pos)
{
    //Code here will be executed on both the client and server, since the target is All
}

Since the target is set to ALL and the server is the owner of this NetworkObject (this is decided when Spawn() is called, not from Register), the following will happen:

If a client calls MoveTo(), the function DoMoveTo() will only be executed locally (since it target clients and server will ignore triggers from non-owner).

If a server calls MoveTo(), the function DoMoveTo() will be executed locally on the server, AND then sent to all clients to be executed there too. Because the server has authority to send the action, and because the target is set to All.

Last updated