Messaging with Commands
Overview
Commands are network messages sent from one CoherenceSync to another CoherenceSync. Functionally equivalent to RPCs, commands bind to public methods accessible on the GameObject hierarchy that CoherenceSync sits on.

We have a video that can clarify how Network Commands work when invoked on network entities of varying authority state (from 5:00):
Design phase
In the design phase, you can expose public methods the same way you select fields for synchronization: through the Configure window on your CoherenceSync component.


By clicking on the method, you bind to it, defining a command. The grid icon on its right lets you configure the routing mode. Commands with a Send to State Authority Only
mode can be sent only to the state authority of the target CoherenceSync, while ones with the Send to All Instances
can be broadcasted to all Clients that see it. The routing is enforced by the Replication Server as a security measure, so that outdated or malicious Clients don't break the game.

Sending a command on a networked object
To send a command, we call the SendCommand
method on the target CoherenceSync
object. It takes a number of arguments:
The generic type parameter must be the type of the receiving Component. This ensures that the correct method gets called if the receiving GameObject has components that implement methods that share the same name.
Example:
sync.SendCommand<Transform>(...)
The first argument is the name of the method on the component that we want to call.
It is good practice to use the C# nameof
expression when referring to the method name, since it prevents accidentally misspelling it, or forgetting to update the string if the method changes name.
Alternatively, if you want to know which Client sent the command, you can add
CoherenceSync sender
as the first argument of the command, and the correct value will be automatically filled in by the SDK.The second argument is an enum that specifies the
MessageTarget
of the command. The possible values are:MessageTarget.All
– sends the command to each Client that has an instance of this Entity.MessageTarget.StateAuthorityOnly
– send the command only to the Client that has state authority over the Entity.MessageTarget.InputAuthorityOnly
– send the command only to the Client that has input authority over the Entity.MessageTarget.Other
- sends the command to every Entity other than the one SendCommand is called on.
Mind that the target must be compatible with the routing mode set in the bindings, i.e. Send to state authority
will allow only for the MessageTarget.StateAuthorityOnly
while Send to all instances
allows for both values.
Also, it is possible that the message never sends as in the case of a command with MessageTarget.Other
sent from the authority with routing of State Authority Only.
The rest of the arguments (if any) vary depending on the command itself. We must supply as many parameters as are defined in the target method and the schema.
Here's an example of how to send a command:
var target = enemy.GetComponent<CoherenceSync>();
target.SendCommand<Character>(nameof(Character.SendDamage),
MessageTarget.All,
damageValue, staminaBlockCost,
staminaRecoveryDelay, ignoreDefense,
hitReaction, hitPosition,
ConnectionId, isPlayer);
Sending a command to a specific MonoBehaviour instance
If you have the same command bound more than once in the same Prefab hierarchy, you can target a specific MonoBehaviour when sending a message, via the SendCommand(Action action) method in CoherenceSync.
Additionally, if you want to target every bound MonoBehaviour, you can do so via the SendCommandToChildren method in CoherenceSync.
Ordering of commands
By default commands don't have any order. In other words, commands might be received by other Clients in a completely different order than they were sent.
If the order of commands is crucial, use SendOrderedCommand
instead of SendCommand
. This guarantees that any given ordered command will be received by other Clients in the same order in which it was sent from the source Client, relative to other sent ordered commands by that Client.
Sending commands as ordered should be used only where necessary, since each ordered command slightly increases bandwidth and latency in case of bad network conditions.
Receiving a command
We don't have to do anything special to receive the command. The system will simply call the corresponding method on the target network entity.
If the target is a locally simulated entity, SendCommand
will recognize that and not send a network command, but instead simply call the method directly.
Command Meta
Commands can carry metadata that provides additional context about when and by whom the command was sent. This is controlled by using [Command(UseMeta = true)]
.
Command metadata includes:
Frame: The frame number when the command was sent
Sender: The ID of the client that sent the command
Enabling Command Meta
To enable metadata for a command, add the UseMeta = true
parameter to the [Command]
attribute:
[Command(UseMeta = true)]
public void CommandWithMeta()
{
// Access metadata here
}
Accessing Command Meta
There are two ways to access command metadata:
Using CurrentCommandMeta (direct access):
[Command(UseMeta = true)]
public void CommandWithMeta()
{
var meta = CoherenceSync.CurrentCommandMeta;
Debug.Log($"Command sent at frame: {meta.Frame}");
Debug.Log($"Sent by client: {meta.Sender}");
}
Important: Accessing CurrentCommandMeta
can throw an exception if accessed outside of a command's context or if meta was not sent with the command.
Using TryGetCurrentCommandMeta (safe access):
[Command(UseMeta = true)]
public void CommandWithMetaAndData(string data, int value)
{
if (CoherenceSync.TryGetCurrentCommandMeta(out var meta))
{
Debug.Log($"Command sent at frame: {meta.Frame}");
Debug.Log($"Sent by client: {meta.Sender}");
Debug.Log($"Data: {data}, Value: {value}");
}
}
Both methods can be used for commands with or without parameters. The key difference is that CurrentCommandMeta
provides direct access but can throw exceptions, while TryGetCurrentCommandMeta
is the safer option that returns a boolean indicating success.
When to Use Command Meta
Command meta is useful when you need to:
Track the timing of command execution for gameplay synchronization
Identify which client sent a command for security or gameplay purposes
Implement frame-based game logic that depends on command timing
Debug network command flow
Sending a command to multiple CoherenceSyncs
Sometimes you want to inform a bunch of different CoherenceSyncs about a change. For example, an explosion impact on a few players. To do so, we have to go through the instances we want to notify and send commands to each of them.
using Coherence;
using Coherence.Toolkit;
using UnityEngine;
public class MyCommands : MonoBehaviour
{
public void MulticastCommand()
{
var self = GetComponent<CoherenceSync>();
var listeners = FindObjectsByType<CoherenceSync>();
foreach (var listener in listeners)
{
if (!listener || listener == self)
{
continue;
}
listener.SendCommand<MyCommands>(nameof(ReceiveCommand),
MessageTarget.StateAuthorityOnly);
}
}
// bind to this method via the Bindings window
public void ReceiveCommand()
{
Debug.Log("Received!");
}
}
In this example, a command will get sent to each CoherenceSync under the state authority of this Client. To make it only affect CoherenceSyncs within certain criteria, you need to filter to which CoherenceSync you send the command to, on your own.
Sending null values in command arguments
Some of the primitive types supported are nullable values, this includes:
Byte[]
string
Entity references: CoherenceSync, Transform, and GameObject
In order to send one of these values as a null (or default) we need to use special syntax to ensure the right method signature is resolved.
using Coherence;
using Coherence.Toolkit;
using UnityEngine;
using System;
public class MyCommand : MonoBehaviour
{
private CoherenceSync sync;
public void MyNullableCommand(string someString, Byte[] someArray)
{
if (!string.IsNullOrEmpty(someString)) //Could be null or string.Empty
{
// Do something with the string
}
if (someArray != null && someArray.Length > 0) //Could be null or Byte[0]
{
// Do something with the array
}
}
public void SendNullableCommand()
{
sync.SendCommand<MyCommand>(nameof(MyNullableCommand),
MessageTarget.All,
(typeof(string), (string)null),
(typeof(Byte[]), (Byte[])null));
}
}
Null-value arguments need to be passed as a ValueTuple<Type, object> so that their type can be correctly resolved. In the example above sending a null value for a string is written as:
(typeof(string), (string)null)
and the null Byte[] argument is written as:
(typeof(Byte[]), (Byte[])null)
Mis-ordered arguments, type mis-match, or unresolvable types will result in errors logged and the command not being sent.
Limitations
When a Prefab is not using a baked script there are some restrictions for what types can be sent in a single command:
4 entity references
maximum of 511 bytes total of data in other arguments
a single Byte[] argument can be no longer than 509 bytes because of overhead
Some network primitive types send extra data when serialized (like Byte arrays and string types) so gauging how many bits a command will use is difficult. If a single command is bigger than the supported packet size, it won't work even with baked code. For a good and performant game experience, always try to keep the total command argument sizes low.
When a Client receives a command targeted at StateAuthorityOnly
but it has already transferred an authority of that entity, the command is simply discarded.
Last updated
Was this helpful?