# `Cauldron2D.World`
[🔗](https://github.com/jaman/cauldron/blob/v0.2.2/cauldron_2d/lib/cauldron_2d/world.ex#L1)

Runs a `Cauldron2D.Game` at a fixed tick and keeps its players supplied with views.

    {:ok, world} = Cauldron2D.World.start_link(game: MyGame, game_opts: [seed: 1], hz: 50)

Players join from their own processes with `Cauldron2D.Player.join/3`; each tick the
world applies the input they sent, steps the game once per due step, and sends every
player whose frame is due `{:cauldron_frame, %{tick: n, view: view, events: events}}`
— every tick, or as often as the player asked (`join/4`'s `:frames`, `frames/3`),
the view built only for those frames. A player whose
process exits is removed. Every join and leave is told to the subscribers of
`Cauldron2D.World.Presence`, and each tick's events to those of
`Cauldron2D.World.Events`. `stats/1` says how the world is keeping up.

The next tick is timed only once the last has run, for when the next step falls due,
so the world's mailbox never holds more than one tick: input sent while a slow tick
runs is applied before the next step, however far the world falls behind. Steps
more than `Cauldron2D.Loop`'s `:max_steps` behind are dropped, not owed.

A world started with `tick: :on_input` has no clock: it runs one step, with `dt` of
`1 / hz`, each time a player's input arrives, and sends views after it. That is the
world for a turn-based game. `pause/1` stops a clocked world stepping until
`resume/1`; time paused is not owed afterwards, and input sent meanwhile is applied
on resume.

## Options

  * `:game` — the module implementing `Cauldron2D.Game`. Required
  * `:game_opts` — passed to its `init/1`. Default `[]`
  * `:state` — a game state to start from instead of calling `init/1`, as
    `snapshot/1` returned it. Default none
  * `:hz` — ticks per second, and the `dt` of a step. Default `50`
  * `:tick` — `:fixed`, stepping on the clock, or `:on_input`. Default `:fixed`
  * `:record` — keep a `Cauldron2D.Replay` of joins, leaves and input. Default `false`
  * `:name` — a GenServer name. Default none

# `player_id`

```elixir
@type player_id() :: term()
```

# `stats`

```elixir
@type stats() :: %{
  players: non_neg_integer(),
  hz: number(),
  steps: non_neg_integer(),
  ticks: non_neg_integer(),
  tick_us: %{
    mean: non_neg_integer(),
    p95: non_neg_integer(),
    max: non_neg_integer()
  },
  behind: non_neg_integer()
}
```

How the world is keeping up: the players present, the ticks a second it is set to,
the steps run so far, and over the last 256 ticks that ran a step — how many they
were, the microseconds a tick took (stepping and sending every view) as a mean, a
95th percentile and the longest, and `behind`, how many of them had more than one
step due.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `frames`

```elixir
@spec frames(GenServer.server(), player_id(), boolean() | number()) :: :ok
```

Change how often player `id` is sent frames, as `join/4`'s `:frames` takes it.

# `game`

```elixir
@spec game(GenServer.server()) :: module()
```

The game module the world runs.

# `input`

```elixir
@spec input(GenServer.server(), player_id(), Cauldron2D.Input.state()) :: :ok
```

Send player `id`'s current input, applied before the next tick.

# `join`

```elixir
@spec join(GenServer.server(), player_id(), map(), keyword()) ::
  :ok | {:error, term()}
```

Add the calling process as player `id`. `{:error, :taken}` when another process plays
as `id`; any other `{:error, reason}` is the game's refusal.

`:frames` is how often the player is sent a frame: `true` (default) after every tick
that steps, a number for that many frames a second at most, or `false` for none — a
second seat played from a process that already receives frames as another player. A
frame carries the events of every step since the player's last frame. `:timeout` is how
many milliseconds the world is given to take the player in, default `10_000`.

# `leave`

```elixir
@spec leave(GenServer.server(), player_id()) :: :ok
```

Remove player `id`.

# `pause`

```elixir
@spec pause(GenServer.server()) :: :ok
```

Stop stepping until `resume/1`. Nothing for a world stepping on input.

# `paused?`

```elixir
@spec paused?(GenServer.server()) :: boolean()
```

Whether the world is paused.

# `players`

```elixir
@spec players(GenServer.server()) :: [player_id()]
```

The ids of the players present.

# `replay`

```elixir
@spec replay(GenServer.server()) :: Cauldron2D.Replay.t() | nil
```

The replay recorded so far, or `nil` when the world was not started with `record: true`.

# `resume`

```elixir
@spec resume(GenServer.server()) :: :ok
```

Step again, from now.

# `snapshot`

```elixir
@spec snapshot(GenServer.server()) :: term()
```

The game's current state.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

Start a world; see the module documentation for the options.

# `stats`

```elixir
@spec stats(GenServer.server()) :: stats()
```

How the world is keeping up; see `t:stats/0`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
