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

FTE's console variables: one table every process of a game reads and writes, the
server, its client program and its menu alike, as FTE's single process has one set.

    cvars = QuakeServer.Cvars.new()
    QuakeServer.Cvars.register(cvars, "sv_gravity", "800")
    {:ok, _prints} = QuakeServer.Cvars.set(cvars, "sv_gravity", "600")
    QuakeServer.Cvars.value(cvars, "sv_gravity")
    #=> 600.0

The table is an ETS table belonging to the process that called `new/0`; it lives as
long as that process and `delete/1` frees it sooner. Reads never wait on another
process. Names are matched without regard to ASCII case, and a variable registered
with an `:alias` answers to both names.

A variable holds its value as text (`string`), with `value` the text read as FTE's
`Q_atof` reads it (a 32-bit float) and `ival` as `Q_atoi` reads it. Each has a default
(the value `cvarreset` gives back) and, for one the engine registers, the engine's own
default.

Flags are FTE's (`flags/0`): `:archive` (written by `cfg_save`, set by `seta`),
`:userinfo` and `:serverinfo` (the value is the player's or the server's info key of
the same name), `:noset` (read only), `:maplatch` (a change while a server runs waits
for the next map), `:rendererlatch` and `:videolatch` (a change while the renderer is
up waits for `vid_reload` or `vid_restart`), `:notfromserver` (no program or server
may set it), `:nounsafeexpand` (no program may read it), `:usercreated` (made by a
config or a program rather than the engine) and the rest of FTE's.

Every change is stamped and counts up the table's generation (`generation/1`), so a
program's `autocvar_` globals and a server's info keys follow it
(`QuakeServer.Autocvars`).

The table also holds what FTE keeps beside its cvars: whether a server or a renderer
is running (`put_running/3`), whether an archived variable changed since the last save
(`unsaved?/1`), the directory configs are written to (`home/1`), the console aliases
(`command_alias/2`) and the key bindings with their bind maps (`binding/3`,
`bindmaps/1`).

# `flag`

```elixir
@type flag() ::
  :archive
  | :userinfo
  | :serverinfo
  | :noset
  | :maplatch
  | :notfromserver
  | :usercreated
  | :cheat
  | :semicheat
  | :rendererlatch
  | :serveroverride
  | :renderercallback
  | :nounsafeexpand
  | :rulesetlatch
  | :shadersystem
  | :tellgamecode
  | :configdefault
  | :nosave
  | :noreset
  | :teamplaytaint
  | :watched
  | :videolatch
  | :warnonchange
  | :rendereroverride
```

# `t`

```elixir
@type t() :: %QuakeServer.Cvars{
  generation: :atomics.atomics_ref(),
  release: boolean(),
  table: :ets.tid()
}
```

# `add_flags`

```elixir
@spec add_flags(t(), String.t(), [flag()]) :: :ok
```

Give `name` the flags `flags` besides its own; nothing when there is no such variable.

# `apply_latches`

```elixir
@spec apply_latches(t(), flag() | [flag()], keyword()) :: non_neg_integer()
```

Give every variable with any of the flags `flags` (one flag or a list) its latched
value, as FTE's `Cvar_ApplyLatches`, and with `clear: true` take those flags off them
all. Returns how many had a latched value.

# `archive_text`

```elixir
@spec archive_text(t(), boolean()) :: String.t()
```

The variables as FTE's `cfg_save` writes them (`Cvar_WriteVariables`): the archived
ones, or with `all?` every one but `cl_warncmd`; never read only or `:nosave` ones.
Each group is headed by a `// group` line; an engine variable is written as `name
"value"`, one a config or program made as `seta` or `set`, with its description after
`//` at the next multiple of eight columns (at least 24), each of its lines so.

# `atof`

```elixir
@spec atof(String.t()) :: float()
```

Text read as a number, as FTE's `Q_atof`: a 32-bit float.

# `atoi`

```elixir
@spec atoi(String.t()) :: integer()
```

Text read as a whole number, as FTE's `Q_atoi`.

# `binding`

```elixir
@spec binding(t(), integer(), non_neg_integer()) :: {String.t(), integer()} | nil
```

The key binding of key number `keynum` in modifier state `modifier` (0 to 15, as
`QuakeServer.Keys` numbers them) as `{command, level}`, or `nil` when it is unbound.

# `bindings`

```elixir
@spec bindings(t()) :: [{integer(), non_neg_integer(), String.t(), integer()}]
```

Every key binding as `{keynum, modifier, command, level}`, by key number then modifier.

# `bindmaps`

```elixir
@spec bindmaps(t()) :: {non_neg_integer(), non_neg_integer()}
```

The two modifier states key presses read their bindings from (FTE's `key_bindmaps`), `{0, 0}` at first.

# `bits`

```elixir
@spec bits([flag()]) :: non_neg_integer()
```

The bitmask of flag names `names`.

# `c_atof`

```elixir
@spec c_atof(String.t()) :: float()
```

Text read as a number, as C's `atof`: leading spaces, a sign, digits, a point and an exponent; a 32-bit float.

# `command_alias`

```elixir
@spec command_alias(t(), String.t()) :: String.t() | nil
```

The console alias `name`'s commands, or `nil` when there is none.

# `command_aliases`

```elixir
@spec command_aliases(t()) :: [{String.t(), String.t()}]
```

Every console alias as `{name, commands}`, sorted by name.

# `delete`

```elixir
@spec delete(t()) :: :ok
```

Free the table; it may not be used after.

# `exists?`

```elixir
@spec exists?(t(), String.t()) :: boolean()
```

Whether there is a variable `name`.

# `fetch`

```elixir
@spec fetch(t(), String.t()) :: QuakeServer.Cvars.Cvar.t() | nil
```

The variable `name`, or `nil` when there is none.

# `flag?`

```elixir
@spec flag?(QuakeServer.Cvars.Cvar.t(), flag()) :: boolean()
```

Whether `cvar` has flag `name`.

# `flags`

```elixir
@spec flags() :: %{required(flag()) =&gt; pos_integer()}
```

FTE's flag bits by name.

# `format_number`

```elixir
@spec format_number(number()) :: String.t()
```

A number as FTE's `Cvar_SetValue` writes it: six decimals, trailing zeros and point dropped.

# `generation`

```elixir
@spec generation(t()) :: non_neg_integer()
```

How many changes the table has seen; it counts up with every change.

# `get`

```elixir
@spec get(t(), String.t(), String.t(), [flag()], keyword()) ::
  QuakeServer.Cvars.Cvar.t() | nil
```

The variable `name`, made when there is none, as FTE's `Cvar_Get2`: one that is there
takes on `:userinfo` and `:serverinfo` from `flags`; one made is created by a config or
a program (`:usercreated`) with `default` as its value and default and the other
`flags`. A name with a quote, `^`, `$`, `;` or whitespace in it, or an empty one, makes
nothing: `nil`. Options: `:description`, `:group` (default `"Custom variables"`).

# `home`

```elixir
@spec home(t()) :: Path.t() | nil
```

The directory `cfg_save` writes configs to and `exec` reads them from first, or `nil`.

# `info_stamps`

```elixir
@spec info_stamps(t()) :: [{String.t(), integer()}]
```

The variables flagged `:serverinfo` or `:userinfo`, as `{name, stamp}`.

# `list`

```elixir
@spec list(t()) :: [QuakeServer.Cvars.Cvar.t()]
```

Every variable, sorted by name.

# `lock_defaults`

```elixir
@spec lock_defaults(t()) :: :ok
```

Every variable's default becomes its value, unless read only or a cheat (FTE's `cvar_lockdefaults`).

# `lock_from_server`

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

The server takes the variable over, as FTE's `Cvar_LockFromServer`: it is set to
`value` past its latches and flagged `:serveroverride`, its own value kept as the
latched one to come back to (`apply_latches/3` with `:serveroverride`).

# `new`

```elixir
@spec new() :: t()
```

A new, empty table belonging to the calling process.

# `purge_defaults`

```elixir
@spec purge_defaults(t()) :: :ok
```

Every engine variable's default goes back to the engine's (FTE's `cvar_purgedefaults`); values stay.

# `put_binding`

```elixir
@spec put_binding(t(), integer(), non_neg_integer(), String.t() | nil, integer()) ::
  :ok
```

Bind key number `keynum` in modifier state `modifier` to `command` at restriction
`level`, or unbind it with `nil`; either way the configuration counts as changed
(`unsaved?/1`).

# `put_bindmaps`

```elixir
@spec put_bindmaps(t(), {non_neg_integer(), non_neg_integer()}) :: :ok
```

Set the two modifier states key presses read their bindings from.

# `put_command_alias`

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

Make console alias `name` run `commands`, as FTE's `alias` command.

# `put_engine_default`

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

Make `value` variable `name`'s engine default, as FTE's `Cvar_SetEngineDefault`: its
default follows when it was the engine's; its value is left as it is. Nothing when
there is no such variable.

# `put_home`

```elixir
@spec put_home(t(), Path.t()) :: :ok
```

Use `directory` as the place configs are written to and read from first.

# `put_running`

```elixir
@spec put_running(t(), :server | :renderer, boolean()) :: :ok
```

Mark whether a server (`:server`) or the renderer (`:renderer`) is running, for the latching flags.

# `register`

```elixir
@spec register(t(), String.t(), String.t(), keyword()) :: QuakeServer.Cvars.Cvar.t()
```

Register an engine variable, as FTE's `Cvar_Register`: one not there is made with
`default` as its value, its default and its engine default; one a config or a program
made first becomes the engine's, keeping its value (its latched one, if any) and its
`:archive` flag; one the engine registered already is left as it is.

Options: `:flags` (a list of `t:flag/0`), `:alias`, `:description`, `:group`.

# `remove_flags`

```elixir
@spec remove_flags(t(), String.t(), [flag()]) :: :ok
```

Take the flags `flags` off `name`; nothing when there is no such variable.

# `reset`

```elixir
@spec reset(t(), String.t() | nil, keyword()) :: [String.t()]
```

Set back to their defaults the variables whose names match `pattern` (`*` and `?`
wildcards; `*` or `nil` is every one), as FTE's `cvarreset`. Options: `:group` (only
variables of a group matching it), `alt: true` (second names match too), `:exclude`
(flag names; variables with any are left). `:noreset` variables are left, as are read
only ones when every variable is reset. Returns the lines printed.

# `reset_defaults`

```elixir
@spec reset_defaults(t(), :all | :saved | :unsaved) :: :ok
```

The defaults of engine variables go back to the engine's, as FTE's
`cvar_resettodefaults_*`: `:all`, `:saved` (archived only) or `:unsaved`; read only and
`:noreset` variables are left.

# `running?`

```elixir
@spec running?(t(), :server | :renderer) :: boolean()
```

Whether a server (`:server`) or the renderer (`:renderer`) is running.

# `saved`

```elixir
@spec saved(t()) :: :ok
```

Mark the archived variables saved.

# `serverinfo`

```elixir
@spec serverinfo(t()) :: %{required(String.t()) =&gt; String.t()}
```

The server's info keys from its `:serverinfo` variables: every one with a value.

# `set`

```elixir
@spec set(t(), String.t(), String.t() | nil, keyword()) ::
  {:ok, [String.t()]} | {:error, :missing}
```

Set `name` to `value` (`nil` for its default), as FTE's `Cvar_Set`: a read-only
variable is refused; a variable latched by its flags keeps `value` for later (a
`:maplatch` one while a server runs, a `:rendererlatch` or `:videolatch` one while the
renderer is up); otherwise the value changes, any latched value is dropped, and an
engine variable's own check runs (`r_meshpitch` and `r_meshroll` become 1 or -1, `fov`
at least 10, `viewsize` 30 to 120).

`{:ok, lines}` gives the lines FTE prints about it (latching while `cl_warncmd` is set,
read only); `{:error, :missing}` when there is no such variable. Option `force: true`
sets past the latches and `:noset`.

# `stamp`

```elixir
@spec stamp(t(), String.t()) :: integer() | nil
```

The change stamp of `name`, or `nil` when there is no such variable.

# `string`

```elixir
@spec string(t(), String.t()) :: String.t()
```

The value of `name` as text, `""` when there is no such variable.

# `unsaved?`

```elixir
@spec unsaved?(t()) :: boolean()
```

Whether an archived variable changed since the last save (FTE's `cvars_haveunsaved`).

# `userinfo`

```elixir
@spec userinfo(t()) :: %{required(String.t()) =&gt; String.t()}
```

The player's info keys from the `:userinfo` variables: every one with a value.

# `value`

```elixir
@spec value(t(), String.t()) :: float()
```

The value of `name` as a number, 0.0 when there is no such variable.

---

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