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

How a `Cauldron2D.World` on one machine keeps a world on another in step with it.

The host keeps each join, leave and input it applies, in order (`logged/2`), and after
each tick sends every follower `{:cauldron_steps, host, base, actions, due, check}`
(`flushed/3`): the actions it applied since the last, the steps it then ran (`due`)
from step `base`, and every 60 steps `{step, checksum}` of the state the steps left
(`Cauldron2D.Game.checksum/1`). A follower applies the same actions to its own state
and runs the same steps (`followed/2`), draining the game's events after each and
keeping them in `drained` for its next frames. A player of the follower's own is
`ready` once its join comes back from the host, and is sent frames from then on. One that finds itself at another step than
`base`, or whose checksum differs, takes the host's state again (`resynced/1`).

A follower starts from `start/1` of the host: the game's `Cauldron2D.Game.export/1` of
the state, the step and the tick rate. Its state is `Cauldron2D.Game.restore/1` of it.

# `action`

```elixir
@type action() :: {:join, term(), map()} | {:leave, term()} | {:input, term(), term()}
```

# `start`

```elixir
@type start() :: %{state: term(), steps: non_neg_integer(), hz: number()}
```

# `check`

```elixir
@spec check(map(), non_neg_integer(), non_neg_integer()) ::
  {non_neg_integer(), term()} | nil
```

`{step, checksum}` of the state when `due` steps from `base` passed a checking step, else `nil`; `nil` while `world` has no followers.

# `flushed`

```elixir
@spec flushed(map(), non_neg_integer(), non_neg_integer(), tuple() | nil) :: map()
```

`world` after sending its followers what it applied and the `due` steps it ran from
step `base`, with `check/3` as the steps left the state.

# `followed`

```elixir
@spec followed(map(), tuple()) :: {map(), boolean()}
```

`world`, a follower, after the host's `{base, actions, due, check}` and every further
one already waiting in its mailbox. Returns `{world, stepped?}`.

# `logged`

```elixir
@spec logged(map(), action()) :: map()
```

`world` keeping `action` for its followers; nothing kept while it has none.

# `resynced`

```elixir
@spec resynced(map()) :: map()
```

`world`, a follower, at the host's state and step as they are now.

# `start`

```elixir
@spec start(map()) :: {map(), start()}
```

What a follower of `world` starts from, once what `world` applied is sent to those following it already.

# `started`

```elixir
@spec started(map(), start()) :: map()
```

`world`, a follower, at the step and state of `start`.

---

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