Skip to content

Actions

Andrea Catania edited this page Oct 11, 2022 · 2 revisions

The Action is a Network Synchronizer feature that allow to execute some code in sync, on all the clients.

Usually, this feature is used to perform an action on some in common nodes. Take as an example the Doors in Apex Legends, usually the character can interact with it to open/close the door: this action is performed in sync on all the clients so that many can interact with the door in real time.

However, this functionality is really useful to send information between client and server too: for example to signal health loss, some gameplay event, etc..

How to use it

To use this feature, the first thing you have to do is register the Action, using NetworkSync.register_action:

extends KinematicBody
class_name Door

func _ready():
	var can_be_triggered_on_client_side = true
	NetworkSync.register_action(self, "door_interact", "door_interact_encoder", can_be_triggered_on_client_side)

func door_interact(force: float):
	# Open / Close the door using `force` amount

func door_interact_encoder(encoder: InputNetworkEncoder):
	encoder.register_input("force", 0.0, DataBuffer.DATA_TYPE_POSITIVE_UNIT_REAL, DataBuffer.COMPRESSION_LEVEL_3)

The code is simple, let's see what each parameter does:

  • The first parameter is the object that owns the action.
  • The second parameter is the function executed when the action is triggered.
  • The third parameter is the function used to instruct the NetSync about how to encode the parameters in a really compact buffer (to optimize the bandwidth usage).

On client, to trigger this action is enough calling the function NetworkSync.trigger_action_by_name; which we can put in a function to expose the functionality as an API:

[...]

func interact(force):
	NetworkSync.trigger_action_by_name(self, "door_interact", [force])

On client side, it's possible to just call the exposed API the_door.interact(), to open the door in sync across all the clients.

Extra functionalities

Server validation

Sometimes it's desirable to validate an action done by the client to make cheating much complicated: For example, we may want to give some health to the door, so the character can break it. This new damage Action should be validated before the server send it to all the other clients.

During the Action registration it's possible to specify the validation function:

func _ready():
	var can_be_triggered_on_client_side = true
	NetworkSync.register_action(self, "damage", "damage_encoder", can_be_triggered_on_client_side, false, "damage_validate")

func damage(amount):
	[...]

func damage_encoder(encoder: InputNetworkEncoder):
	[...]

func damage_validate(amount):
	var is_valid = is_destructible == false
	return is_valid

Wait server validation

On the above example, the action is executed right away on the client who triggered the action, even before the server has validated it. That's totally fine, but sometimes it's desirable that even the client who triggered the action waits the server validation.

To do that it's enough pass true on the 5th parameter of register_action:

func _ready():
	var can_be_triggered_on_client_side = true
	var wait_validation = true
	NetworkSync.register_action(self, "damage", "damage_encoder", can_be_triggered_on_client_side, wait_validation, "damage_validate")

Note: In case the client that triggered the action, took for granted the server validation, and the server discard the action, that's not a big issue: The server will instruct the client to rewind back and re-sync.

Specific peer action

This is less common but nevertheless useful; sometimes you will need that the server triggers an action only on a specific peer. The last parameter of trigger_action_by_name is an array of recipients peers:

NetworkSync.trigger_action_by_name(self, "notify_health_loss", [new_health], [get_network_master()])

This feature can be used only by the server.

Server only trigger

Some specific actions will be triggered by the server; in that case it's really nice to instruct the NetSync about this particular behavior, so it can refuse those actions when triggered by the clients (This is good to make cheating difficult).

func _ready():
	var can_be_triggered_on_client_side = false
	NetworkSync.register_action(self, "start_match", "start_match_encoder", can_be_triggered_on_client_side)