# `QuakeServer.Runner`
[🔗](https://github.com/jaman/quake_server/blob/v0.1.0/lib/quake_server/runner.ex#L1)

A level running in a process of its own, on its own clock, as FTE's listen server runs
one for its own client (`SV_Frame`, `SV_Physics`): the host clock follows real time,
and physics frames catch up to it whenever it is at least the cvar `sv_mintic` ahead,
each frame as long as it is ahead up to `sv_maxtic`; after `sv_limittics` frames of
`sv_maxtic` the rest is dropped. A player's command runs as it arrives
(`QuakeServer.Server.client_command/3`), covering its `msec` milliseconds when given,
else its `timelength` in whole milliseconds, up to 250. A command's `sequence` (the
client's number for it) is the one the server acknowledges in every packet after it
runs, as FTE's server sends a client's `last_sequence` (`PEXT2_PREDINFO`): 0 when the
program does not run its players' commands itself, telling the client it cannot
predict.

    {:ok, runner} = QuakeServer.Runner.start_link(map: "c0a0", server: [source: MyGame.Source])

    QuakeServer.Runner.subscribe(runner, self())
    receive do
      {QuakeServer.Runner, {:serverdata, %{level: level}}} ->
        QuakeServer.Runner.begin(runner, level, csqc: true)
    end
    QuakeServer.Runner.command(runner, command)
    receive do
      {QuakeServer.Runner, %{level: level, packets: packets}} -> ...
    end

    table = QuakeServer.Runner.handoff(runner)
    %{events: events, listener: at, setangle: angles} = QuakeServer.Runner.take(table)

A level starts without the player, as FTE's `SV_SpawnServer` does. The subscribed
client is sent `{QuakeServer.Runner, {:serverdata, serverdata}}` (`t:serverdata/0`),
what FTE sends a client from `svc_serverdata` to the `spawn` command: the level, a
number counting up from 1 with each level started, its map and name, the model and
sound lists, the server's info keys but `mapname` (FTE's `fullserverinfo`), and the
signon messages (`QuakeServer.Signon`); then a message with no packets carrying every
light style. A client subscribing later is sent the same for the level as it stands.
The client loaded, `begin/3` brings the player in (`QuakeServer.Server.connect/3`),
as FTE's `begin` does, and sets the view (FTE's `svc_setangle`, `FIXANGLE_FIXED`): the
player's `angles` while its `fixangle` is set, else its `v_angle`, roll included;
`fixangle` is then cleared. A `begin` for a level other than the current one is
ignored, and one for a player already in the game runs the program's `RestoreGame` and
sets the view again.

Until the player is in the game the level is paused, as FTE pauses a single player
server with nobody spawned: no physics frames run, the host clock moves on only while
it stays at or below 1.5, and commands and requests are ignored.

Each server frame ends with `QuakeServer.Network.frame/2` and
`QuakeServer.Animation.sample/1`; when a command has arrived since the last packet,
the player is sent one, as FTE sends a client a packet for each it receives. The
subscribed client gets each as the message `{QuakeServer.Runner, snapshot}`, a
`t:snapshot/0`: the packet in `packets`, as `%{reliable: [binary], entities: [update],
animation: %{entity => state}, datagram: [binary], sequence: integer}` (the reliable
messages queued for the player, `QuakeServer.Outbox`, the entity updates, the
animation states the player lacks, `QuakeServer.Animation.take/2`, the datagram, and
the sequence of the last command the server ran), the
light styles set or changed since the last message (`%{style => pattern}`, as FTE
sends `svc_lightstyle`) and their colours (`lightstyle_colours`, `%{style => {r, g,
b}}`), the server's info keys (`serverinfo`), the server's time, its model list, and
the level with its map.

A level change the program asks for is followed between frames, the clock waiting, as
FTE's `changelevel` (`SV_Map_f`): the player's spawn parameters and `parm_string`
(`QuakeServer.Server.save_spawn_parms/1`), the program's `serverflags`, its written
files with their entity tables (`QuakeServer.EntityTable`), the server's info keys and
the player's info keys are carried. With a landmark
the level left is kept for a return, as FTE's level cache keeps it: a program that
saves levels itself has it saved (`QuakeServer.Server.save_level/1`), and on a return
through a landmark the level starts afresh with the console variables of the save and
is restored from it (`QuakeServer.Server.load_level/2`); for another program the
player is taken out (`QuakeServer.Server.disconnect/1`), the level is kept as it
stands and brought back as it was left (`QuakeServer.Server.resume/2`). Without a
landmark the level left and the kept levels are released
(`QuakeServer.Server.release/1`) and the saves forgotten. The reliable messages still queued for the
player are sent as a last packet, then `{QuakeServer.Runner, {:changing, map}}`, as
FTE stuffs `changing`; the next level starts without the player.

After every frame and command the runner puts in its handoff table (`handoff/1`) the
events since, where the player's eye is, and the view angles the server set
(`fixangle`): `{pitch, yaw, roll}`, `{:turn, yaw}` for a turn to add (`fixangle` 2).
`take/1` empties it of events and angles without waiting on the runner. The handoff
also holds whether the player stands on the ground and how fast they move.

`console/2` runs console commands in the level's server, as FTE's host console
does. Every level the runner starts uses one console variable table, as FTE's one
process has one: the server option `:cvar_table`, or a table of the runner's own.

A program error (`QuakeC.Exec.Error`, raised by the program's `error` builtin with
`developer` 0 or by the machine) ends the server, as FTE's `SV_Error` and
`Host_EndGame` do: the level and the kept levels are released, the reason
`"SV_Error: " <> message <> "\n"` is put in the handoff table (`ended` in `take/1`) and
the subscribed client is sent `{QuakeServer.Runner, {:ended, reason}}`, as is a client
that subscribes after, and the table no longer counts a server as running
(`QuakeServer.Cvars.running?/2`). The runner then stays up with no server: commands, requests and
console commands are ignored and the clock stops.

# `serverdata`

```elixir
@type serverdata() :: %{
  level: pos_integer(),
  map: String.t(),
  levelname: String.t(),
  model_names: [String.t()],
  sound_names: [String.t()],
  serverinfo: %{required(String.t()) =&gt; String.t()},
  signon: [binary()]
}
```

What a client is sent when a level starts, before it begins.

# `snapshot`

```elixir
@type snapshot() :: %{
  level: pos_integer(),
  map: String.t(),
  model_names: [String.t()],
  packets: [
    %{
      reliable: [binary()],
      entities: list(),
      animation: %{required(pos_integer()) =&gt; QuakeServer.Animation.state()},
      datagram: [binary()],
      sequence: non_neg_integer()
    }
  ],
  lightstyles: %{required(non_neg_integer()) =&gt; term()},
  lightstyle_colours: %{required(non_neg_integer()) =&gt; tuple()},
  serverinfo: %{required(String.t()) =&gt; String.t()},
  time: float()
}
```

What a client takes of the world (`snapshot/1`).

# `begin`

```elixir
@spec begin(pid(), pos_integer(), keyword()) :: :ok
```

Bring the player into level `level`, its client having loaded it, as FTE's `begin`;
option `csqc: true` when the client runs a client program (FTE's `enablecsqc`).

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `command`

```elixir
@spec command(pid(), QuakeServer.Server.command() | map()) :: :ok
```

Use `command` for the player from the next frame on.

# `console`

```elixir
@spec console(pid(), String.t()) :: :ok
```

Run console commands `text` in the level (`QuakeServer.Console.run/2`).

# `handoff`

```elixir
@spec handoff(pid()) :: :ets.tid()
```

The table the runner hands its events, the player's eye and view angles over in.

# `load`

```elixir
@spec load(pid()) :: :ok
```

Load the level a runner started with `held: true` was given.

# `request`

```elixir
@spec request(pid(), String.t(), [{String.t(), term()}]) :: :ok
```

Run the player's client program request `name` with `values` (`QuakeServer.Requests`).

# `start_link`

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

Start level `:map` with `Server.start/2` options `:server`; with `held: true` the level
is not loaded until `load/1`.

# `subscribe`

```elixir
@spec subscribe(pid(), pid()) :: :ok
```

Send the player's packets to `client` from now on, starting with the level as it stands.

# `take`

```elixir
@spec take(:ets.tid()) :: %{
  events: [term()],
  listener: Cauldron3D.Vec.t(),
  setangle: term(),
  onground: boolean(),
  velocity: Cauldron3D.Vec.t(),
  ended: String.t() | nil,
  map: String.t() | nil
}
```

The events put in `table` since the last take, oldest first, the player's eye, the
last view angles the server set since, or `nil`, whether the player stands on the
ground (`onground`) and their velocity, the reason the server ended (`ended`), or
`nil` while it runs, and the level's map (`map`), or `nil` before the first.

---

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