LogoLogo
HomeOnline DashboardAPIDiscordForums
SDK 1.6
SDK 1.6
  • Welcome
  • Overview
    • Features
    • Roadmap
  • Getting started
    • Get the Unity SDK
    • Setup a project
      • 1. Scene setup
      • 2. Prefab setup
      • 3. Test your game locally
        • Local testing using builds
        • Local testing via Unity's Multiplayer Play Mode
        • Local testing via ParrelSync
      • 4. Test in the cloud
        • Deploy a Replication Server
        • Share builds
    • How to... ?
    • Single-player to multiplayer
    • Video tutorials
    • Samples and tutorials
      • Package samples
      • Sample Connection UIs
      • First Steps tutorial
        • 1. Basic syncing
          • 1.1 Animation parameters
          • 1.2 Sending commands
        • 2. Physics / Authority transfer
        • 3. Areas of interest
        • 4. Parenting entities
        • 5. Complex hierarchies
        • 6. Persistence
      • Campfire project
        • Game mechanics
        • Leveraging object pooling
        • Remote interactions: Chairs
        • Remote interactions: Trees
        • A unique object with complex state
        • Custom instantiation and destruction
        • Running a server-side NPC
        • Playing audio and particles
        • A simple text chat
      • Beginner's guide to networking
    • Troubleshooting
  • Manual
    • Unity Components
      • CoherenceSync
      • CoherenceBridge
      • CoherenceLiveQuery
      • CoherenceTagQuery
      • CoherenceGlobalQuery
      • CoherenceInput
      • CoherenceNode
      • PrefabSyncGroup
      • Order of execution
    • Networking state changes
      • Instantiate and Destroy Objects
      • Supported types
      • Messaging with Commands
      • Syncing child GameObjects
      • Animation
      • CoherenceSync references
      • [Sync] and [Command] Attributes
      • [OnValueSynced] Attribute
      • Creating your own syncable member
      • Custom Component Actions
      • Rigid Bodies
      • Interpolation
    • Authority
      • Authority transfer
      • Server-authoritative setup
    • Lifetime
      • Persistence
      • Uniqueness
      • Example: A global counter
    • Parenting network entities
      • Direct children CoherenceSyncs
      • Deeply-nested CoherenceSyncs
      • Nesting Prefabs at Edit time
    • Asset management
      • Instantiating from CoherenceSyncConfig
      • Instantiate via
      • Load via
    • Scene management
    • Multiple Connections within a Game Instance
    • Baking (code generation)
      • Conditional compilation
    • Replication Server
      • Rooms and Worlds
      • Replication Server API
    • Simulators (Servers)
      • Scripting: Client vs Simulator
      • Run local Simulators
      • World Simulators
      • Room Simulators
      • Simulator slugs
      • Build and Deploy
      • Command-line arguments
    • Client Connections
    • Optimization
      • Areas of Interest
      • Level of Detail (LOD)
      • Profiling
      • Simulation Frequency
    • Project Settings
    • Advanced topics
      • Big worlds
        • World Origin Shifting
        • Load balancing
      • Competitive games
        • Simulation Frame
        • Determinism, Prediction and Rollback
      • Team workflows
        • Version Control integration
        • Continuous Integration
      • Schema explained
        • Specification
        • Field settings
        • Archetypes
      • Code stripping
      • Replication Server CLI
      • Single-player gameplay
    • Scripting API
  • Hosting
    • Choosing where to host
    • coherence Cloud
      • Online Dashboard
      • Manage Worlds
      • Configure Rooms
      • Player Accounts
      • Game Services
        • Lobbies
        • Cloud Storage
        • Key-Value Store (Legacy)
      • APIs
        • Worlds
        • Rooms
        • Lobbies
        • Cloud Storage
        • Key-Value Store (Legacy)
    • Peer-to-peer
      • Implementing Client hosting
  • Support
    • Release notes
    • Glossary
    • Unreal Engine support
    • WebGL support
    • ECS / DOTS support
    • Known issues
    • Upgrade guide
      • Upgrade 1.5 -> 1.6
      • Upgrade 1.4 -> 1.5
      • Upgrade 1.3 -> 1.4
      • Upgrade 1.2 -> 1.3
      • Upgrade 1.1 -> 1.2
      • Upgrade 1.0 -> 1.1
      • Upgrade 0.10 -> 1.0
      • Upgrade 0.9 -> 0.10
    • Credit cost & pricing
    • Report a bug
Powered by GitBook
On this page
  • Overview
  • Design phase
  • Sending a command on a networked object
  • Sending a command to a specific MonoBehaviour instance
  • Ordering of commands
  • Receiving a command
  • Identifying which Client sent a command
  • Sending a command to multiple CoherenceSyncs
  • Sending null values in command arguments
  • Limitations

Was this helpful?

Export as PDF
  1. Manual
  2. Networking state changes

Messaging with Commands

Last updated 16 days ago

Was this helpful?

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 Authority Only mode can be sent only to the 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>(...)

If there are multiple commands bound to different components of the same type (for example, your CoherenceSync hierarchy has five Transforms, and you create a command for Transform.SetParent on all of them), the command is only sent to the first one found in the hierarchy which matches the type.

  • The first argument is the name of the method on the component that we want to call.

  • 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.AuthorityOnly – send the command only to the Client that has 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 authority will allow only for the MessageTarget.AuthorityOnly 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 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.

Note that ordered commands are not ordered relative to entity creation/destruction or binding updates. They are ordered only relative to other ordered commands.

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.

Identifying which Client sent a command

While commands by default carry no information on who sent them in order to optimize traffic, you can create commands that include a ClientID as one of the parameters. Then, on the receiving end, compare that value with a list of connected Clients.

Another useful way to access ClientID is via CoherenceBridge, like this:

[Sync] private uint StateAuthority;
(...)
sync.OnStateAuthority.AddListener(() => StateAuthority = (uint)sync.CoherenceBridge.ClientID);

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.AuthorityOnly);
        }
    }

    // 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.

When a null argument is deserialized on a client receiving the command, it is possible that the null value is converted into a non-null default value. For example, sending a null string in a command could result in clients receiving an empty string. As another example, a null Byte[] argument could be deserialized into an empty Byte[0] array. So, receiving code should be ready for either a null value or an equivalent default.

Limitations

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 simply won't work. 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 AuthorityOnly but it has already transferred an authority of that entity, the command is simply discarded.

It is good practice to use the C# expression when referring to the method name, since it prevents accidentally misspelling it, or forgetting to update the string if the method changes name.

You can create your own implementation for these IDs or, more simply, use coherence's built-in feature.

Refer to the page.

nameof
Client Connections
supported types
CoherenceSync
Configure window: Accessible from CoherenceSync Inspector
Routing mode of a command