# PlayersHandle

> Talos next · Since 0.7

The local players on the couch (`meta.players`) and the entity each drives.

Seating and unseating raise `player-joined` (`{ player, device, pad? }`), `player-left`, `player-disconnected` and `player-reconnected` (`{ player }`) on the event bus. A behaviour on a player's entity reads its own `ctx.input` exactly as a single player's does.

## Signature

```ts
export interface PlayersHandle {
  /** Every seated player, in number order. */
  list(): LocalPlayer[]
  /** How many players are seated. */
  readonly count: number
  /** The scene's player limit, 1 to 4; 1 for a scene without a players block. */
  readonly max: number
  /** Whether free devices may join now. Open in a lobby, closed during play. */
  readonly joining: boolean
  /** Open or close joining: a lobby opens it, a race closes it. Seated players stay seated. */
  setJoining(open: boolean): void
  /** Seat a device from a script (`join: "script"` scenes). The new number, or null when no seat was free. */
  join(device: "keyboard" | "keyboard-left" | "keyboard-right"): number | null
  join(device: "pad", pad: number): number | null
  /** Seat a player with no device on this machine: a peer's, or one a test or tool drives by input frames. */
  join(device: "remote"): number | null
  /** Unseat a player; their devices are free to join again. */
  leave(number: number): boolean
  /** Drive `entity` with player `number`'s devices; null goes back to the scene's `spawn { for: "player", player }`. */
  bind(number: number, entity: EntityHandle | string | null): boolean
  /** The player whose devices drive `entity`, or 0. */
  of(entity: EntityHandle | string): number
  /** Player `number` (2 and up) reads its own input context; null follows player 1's again. */
  setContext(number: number, name: string | null): boolean
  /** The input context player `number` reads now, or null. */
  context(number: number): string | null
}
```

Import: `import type { PlayersHandle } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `list` | `list(): LocalPlayer[]` | Every seated player, in number order. |
| `count` | `readonly count: number` | How many players are seated. |
| `max` | `readonly max: number` | The scene's player limit, 1 to 4; 1 for a scene without a players block. |
| `joining` | `readonly joining: boolean` | Whether free devices may join now. Open in a lobby, closed during play. |
| `setJoining` | `setJoining(open: boolean): void` | Open or close joining: a lobby opens it, a race closes it. Seated players stay seated. |
| `join` | `join(device: "keyboard" \| "keyboard-left" \| "keyboard-right"): number \| null` | Seat a device from a script (`join: "script"` scenes). The new number, or null when no seat was free. |
| `leave` | `leave(number: number): boolean` | Unseat a player; their devices are free to join again. |
| `bind` | `bind(number: number, entity: EntityHandle \| string \| null): boolean` | Drive `entity` with player `number`'s devices; null goes back to the scene's `spawn { for: "player", player }`. |
| `of` | `of(entity: EntityHandle \| string): number` | The player whose devices drive `entity`, or 0. |
| `setContext` | `setContext(number: number, name: string \| null): boolean` | Player `number` (2 and up) reads its own input context; null follows player 1's again. |
| `context` | `context(number: number): string \| null` | The input context player `number` reads now, or null. |

## Example

The tested fixture `fixtures/recipes/many-entities/scenes/behaviors/beacon.ts`.

```ts
import type { Behavior } from "@argus/api"

/** A beacon: counts the drones that touch it. Only this beacon hears its touches (`emitTo`). */
interface State {
  touches: number
}

export default {
  init(ctx) {
    ctx.state.touches ??= 0
  },
  onEvent(ctx, ev) {
    if (ev.name === "touched") ctx.state.touches += 1
  },
} satisfies Behavior<State>
```
