Commands
Introduction
Minimum SDK Version: 1.2.3
Quantum Commands are an input data paths to Quantum standard APIs. They are similar to Quantum Inputs but don't required to be send every tick.
They are reliable and the server will never ignore them based on time but with the trade-off that local clients (even the one that generates the Command) cannot predict the tick they will be part of the simulation. Developers can show visual prediction if needed, but the simulation will only receive the Command after the server confirms it as part of a tick.
Commands are implemented as regular C# classes that inherit from Photon.Deterministic.DeterministicCommand
. They can contain any data that can be serialized:

Create Commands as part of the quantum.systems project when they contain data that affects or implements gameplay logic, or implement them inside the quantum.state project.
Similar to Quantum Systems, Commands need to be included in CommandSetup.cs
to be available in runtime:

Sending Commands To The Server
Commands can be send from anywhere inside Unity:
C#
RemoveUnitCommand command = new RemoveUnitCommand();
command.CellIndex = 42;
QuantumRunner.Default.Game.SendCommand(command);
SendCommand()
has two overloads.
C#
void SendCommand(DeterministicCommand command);
void SendCommand(Int32 player, DeterministicCommand command);
Specify the player index (PlayerRef) when you have multiple players controlled from the same machine. Games that only have one local player can ignore the player index field.
Polling Commands From The Simulation
To receive and handle Commands inside the simulation poll the frame for a specific player:
C#
RemoveUnitCommand command = f.GetPlayerCommand(playerRef) as RemoveUnitCommand;
if (command != null) {
// execute command
}
The API does not enforce (or implement) any callback mechanism or particular design pattern. It's up to the developer to chose how to interpret them (for exmaple encoding them into signals; use a Chain of Responsibility; implement the command execution as a method in them, etc).
Quantum Compound Commands
Usually, if you send multiple Deterministic Commands per tick, they will not arrive the simulation all at the same tick. They will arrive separately on consecutive ticks.
The Compound Commands class is useful for sending and receiving many commands within a single simulation tick. Even if the commands have different types.
The first step on using the Compound Commands is to declare this class into your quantum_code
, wherever you prefer (either on quantum_system
or quantum_state
).
C#
namespace Quantum {
public class CompoundCommands : DeterministicCommand {
public List<DeterministicCommand> Commands = new List<DeterministicCommand>();
private int listCount = 0;
public override void Serialize(BitStream stream) {
listCount = Commands.Count;
stream.Serialize(ref listCount);
if (stream.Reading) {
Commands.Clear();
}
for (var i = 0; i < listCount; i++) {
if (stream.Reading) {
DeterministicCommand cmd;
QuantumGame.Instance.Session.CommandSerializer.ReadNext(stream, out cmd);
Commands.Add(cmd);
} else {
if (Commands[i] != null)
QuantumGame.Instance.Session.CommandSerializer.PackNext(stream, Commands[i]);
}
}
}
}
}
This class is responsible for managing a single command which contains a set of other commands. That’s why we have a list of commands in which we will further insert new commands, and (de)serialize them.
Besides of declaring the class, you need to add the CompoundCommands
class to the CommandSetup
class, just like any regular command.
On Unity, whenever you want to send multiple commands per tick (or even just a single command), you need to create a new CompoundComponent
, create your own custom commands and fulfill their custom information, and add them to the Commands list, before sending the command.
C#
public class SampleCompoundCommands : MonoBehaviour {
void Update () {
if (UnityEngine.Input.anyKeyDown) {
CompoundCommands compoundCommands = new CompoundCommands();
BuyUnitCommand buyUnitCommand = new BuyUnitCommand();
buyUnitCommand.UnitID = 0;
compoundCommands.Commands.Add(buyUnitCommand);
AttackCommand attackCommand = new AttackCommand();
attackCommand.SelectedPosition = Vector.One;
compoundCommands.Commands.Add(attackCommand);
QuantumRunner.Default.Game.SendCommand(compoundCommands);
}
}
}
You can send the commands when you prefer. For example, you can have a separate manager class which looks if there if any command added to the Compound Commands, send the command and clear it:
C#
if (compound.Commands.Count > 0) {
game.SendCommand(player, compound);
compound.Commands.Clear();
}
Now you just have to receive the CompoundCommands
on the simulation and decide which pattern you will use to interpret them. So for example, in a system:
C#
var compound = f.GetPlayerCommand(player) as CompoundCommand;
if (compound != null) {
for (int i = 0; i < compound.listCount; i++) {
var command = compound.Commands[i];
// do a switch case over command types
// or activate signals so other systems know that a specific command arrived
// or better: do a polymorphic Execute method inside the command itself
// or any other pattern to externalize the execution taking into consideration the concrete class of the actual internal instances;
}
}
Back to top