> ## Documentation Index
> Fetch the complete documentation index at: https://world.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Commands

Commands are how players interact with your game world. They represent actions that players want to perform, such as moving, attacking, or chatting.

To handle commands in your game, you must first define them and create systems to handle them. Commands can come from either game clients or other shards.

## Defining Commands

Commands are plain Go structs that embed `BaseCommand` and implement the `Command` interface. This interface requires a single `Name()` method that returns a **unique** string identifier.

```go theme={null}
import "github.com/argus-labs/world-engine/pkg/cardinal"

type AttackCommand struct {
    cardinal.BaseCommand // Required
    TargetID string `json:"target_id"`
    Damage   int    `json:"damage"`
}

func (AttackCommand) Name() string {
    return "attack"
}
```

## Handling Commands

Just like with components, you must declare the commands a system can access in its state struct. Add a `WithCommand[T]` field to your system state type, where `T` is your command type:

```go theme={null}
import "github.com/argus-labs/world-engine/pkg/cardinal"

type AttackSystemState struct {
    cardinal.BaseSystemState
    AttackCommands cardinal.WithCommand[AttackCommand]
}
```

<Note>
  A system cannot have multiple `WithCommand[T]` fields with the same command type. However,
  different systems can handle the same command type. This is useful for when you want a single
  command to trigger multiple game logic and/or side effects.
</Note>

### Iterating Over Commands

Use `Iter` to loop through all commands received this tick. Each iteration yields a `CommandContext` that provides access to the command data and metadata.

To access the command's payload, use `Payload`:

```go theme={null}
func AttackSystem(state *AttackSystemState) error {
    for cmd := range state.AttackCommands.Iter() {
        attack := cmd.Payload()
        // Use attack.TargetID, attack.Damage, etc.
    }
    return nil
}
```

### Personas

A persona is the unique identity associated with a player’s account. All commands include metadata containing the sender’s persona, which you can use for authorization checks like verifying entity ownership.

If your client is authenticated, commands sent from it will automatically include your account's persona. In multi-shard setups, personas remain consistent across shards, allowing any shard to verify and act on the same player identity.

You can get a command's persona using `Persona`:

```go theme={null}
func AttackSystem(state *AttackSystemState) error {
    for cmd := range state.AttackCommands.Iter() {
        attack := cmd.Payload()
        sender := cmd.Persona()

        // Get the player's entity and verify ownership.
        player, ok := state.Players.GetByID(attack.PlayerID)
        if !ok {
            continue
        }
        if player.Owner.Get() != sender {
            continue // Skip unauthorized commands.
        }

        // Process the command...
    }
    return nil
}
```

## Sending Commands

Use the client SDK to send commands to the server:

<Tabs>
  <Tab title="Unity C#">
    ```csharp theme={null}
    await shard.SendCommandAsync("attack", new Dictionary<string, object>
    {
        ["target_id"] = "enemy-123",
        ["damage"] = 50
    });
    ```
  </Tab>

  <Tab title="TypeScript">
    ```ts theme={null}
    TODO: update
    ```
  </Tab>
</Tabs>

### Command-Reply Pattern

Commands are asynchronous by design. When you send a command, you receive an acknowledgment, but not the result of the command's execution. If you need a result, you typically emit an event from the system that processes the command and subscribe to it from the client.

Some command types are more synchronous in nature, like updating game config, where every command has a corresponding result event. For these cases, use the synchronous command method, which handles the temporary event subscription for you and returns the result event directly:

<Tabs>
  <Tab title="Unity C#">
    ```csharp theme={null}
    var result = await shard.SendCommandWithReplyAsync<PurchaseResult>("buy-item", new Dictionary<string, object>
    {
        ["item_id"] = "sword-01",
        ["quantity"] = 1
    });
    ```
  </Tab>

  <Tab title="TypeScript">
    ```ts theme={null}
    TODO: update
    ```
  </Tab>
</Tabs>

## Inter-Shard Commands

In a multi-shard setup, you can send commands from within a system to another shard. This allows you to trigger systems in other shards or coordinate game state.

### Defining Shard Targets

To send an inter-shard command, you must first define an `OtherWorld` variable for each shard you want to communicate with:

<Note>The convention is to place this in `pkg/other_worlds/other_world.go`.</Note>

```go theme={null}
import "github.com/argus-labs/world-engine/pkg/cardinal"

var MatchmakingShard = cardinal.OtherWorld{
    Region:       "us-west-2",
    Organization: "my-org",
    Project:      "my-game",
    ShardID:      "matchmaking",
}
```

An `OtherWorld` represents a specific remote shard and is used to route commands to the correct destination.

### Sending Inter-Shard Commands

Use `SendCommand` to dispatch a command to another shard. Pass the system's `BaseSystemState` and the command you want to send:

```go theme={null}
import matchcmd "my-game/shards/matchmaking/command"

func GameEndSystem(state *GameEndSystemState) error {
    for cmd := range state.EndGameCommands.Iter() {
        game := cmd.Payload()

        // Queue the winner for another match on the matchmaking shard.
        MatchmakingShard.SendCommand(&state.BaseSystemState, matchcmd.QueuePlayer{
            PlayerID: game.WinnerID,
            Rating:   game.NewRating,
        })
    }
    return nil
}
```

The receiving shard processes inter-shard commands like any other command, using `WithCommand` in its system state.

<Note>
  The target shard must have a system that handles the command type you're sending. If no system
  handles the command, it will be discarded.
</Note>
