QuakeServer.Server (quake_server v0.1.0)

Copy Markdown View Source

A running level of a QuakeC game: the program and its entities, the map, the models and sounds it has asked for, and what happened this frame for a client to show.

server = QuakeServer.Server.start("e1m1", source: MyGame.Source, skill: 1)
server = QuakeServer.Server.connect(server)
server = QuakeServer.Server.frame(server, 1 / 60, command)
{events, server} = QuakeServer.Server.drain(server)

A command is %{forward:, side:, up:, angles: {pitch, yaw, roll}, attack:, jump:, impulse:}, the moves in units a second and the angles in degrees. For a program that runs its players' commands itself (QuakeServer.Physics.Command) it is %{forward:, side:, up:, angles:, buttons:, impulse:}.

Entity 0 is the world, entity 1 the player (slots is how many entities after the world are player slots: 1 for a level, 0 for a client program's world). The player slots' entities are in use from the level's start, as FTE allocates them; the player is in the game from connect/3 until disconnect/1 (parms is nil while it is not).

Summary

Types

What a player carries from level to level: the 64 spawn parameters, parm1 first, and the program's parm_string.

t()

Functions

Move the host clock (clock) on by seconds; physics frames catch up to it.

Run the program's function name as it stands.

The spawn parameters the player carries to the next level (save_spawn_parms/1's parms).

Run a player's command as it arrives, for a program that runs its players' commands itself (QuakeServer.Physics.Command), at the host clock (clock); a program that does not has command kept for its frames instead.

Bring the player in, as FTE does when its client begins (SV_SetUpClientEdict, then SV_Begin_Core), carrying parms from the last level (save_spawn_parms/1, or the bare list of 64), or with the program's SetNewParms run for fresh ones when nil. The player's entity is cleared (not for a NetQuake program, CRC 5927), named by the player's name info key (Player without one), and given its entity number as colormap, 16 × topcolor + bottomcolor as clientcolors (each 0 outside 0 to 13), gravity 1, maxspeed the cvar sv_maxspeed, movetype noclip and no frags. The spawn parameters go into the parm* globals and parm_string; ClientConnect runs with its argument 1 when option :csqc is true (the client runs a client program), 0 otherwise, then PutClientInServer, each with self the player and time the server's; oldorigin becomes origin; and a program that runs its players' commands runs one empty command (no time, no moves, the angles the player's v_angle).

The value of console variable name as a number (FTE's value), 0.0 when there is no such variable.

Whether there is a console variable name.

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

Take the player out of the level, as FTE's SV_DespawnClient does to a level it keeps: ClientDisconnect runs with self the player, a NetQuake program's player entity is cleared, the player's frags become 0 and the player is no longer in the game.

The events of the frames since the last drain, oldest first, and the server without them.

Add an event for the client.

The options every call into the program takes: the server's budget and trace, and QuakeServer.Autocvars.sync/1 run before the call.

Run one physics frame of seconds (QuakeServer.Physics.frame/1), command being the player's latest for a program that does not run its players' commands itself: the routes asked for before are handed to their callbacks (QuakeServer.Extensions.Routes.run/1), the replies to uri_get that have come to URI_Get_Callback (QuakeServer.Extensions.Web.run/1), the console's queued commands run, then every entity, and the physics clock (time) moves on by seconds. How long a frame should be is the caller's choice; FTE runs them as QuakeServer.Runner does.

Run call (a function of the server) and put self and other back as they were, as a builtin that calls back into the program must.

Restore onto a freshly started level the save text of save_level/1, as FTE's SV_LoadLevelCache: the program's SV_PerformLoad(file, entities, slots) reads the text after its moddata line, every entity is linked into the world again, and the physics time, the host clock and time become the saved time.

Load map name with its world entity and the source's program, spawning nothing else and calling no hooks: the world a client program (FTE's CSQC) runs in. Takes start/2's options; its console variable table is not marked as having a server running and its latched variables stay latched.

The name of player entity: its name info key, Player without one (FTE's name default).

Print lines to the console, each as a {:print, :notify, line} event.

Set console variable name to value, text or a number (written as FTE's Cvar_SetValue writes one), as FTE's Cvar_Set (QuakeServer.Cvars.set/4); a name there is no variable for is made, as a config or program makes one. What FTE prints about it is printed, and the server's own autocvar_ globals and info keys follow at once (QuakeServer.Autocvars.sync/1).

Free the level's program memory (QuakeC.release/1) and a console variable table the server made itself; the server may not be used after.

Bring back a level left by a changelevel with a landmark, as it was left, as FTE's SV_LoadLevelCache brings one back: the change it asked for is forgotten; the info keys of option :serverinfo (the server's as they stand) are taken under the level's own and the star keys of start/2 set again for option :startspot; the program's serverflags global becomes option :serverflags, time the level's physics time and startspot the landmark; its written files become option :written and their entity tables option :entity_tables. Connect the player to it next.

Run QuakeC function value function with self and other set, at the server's time.

Save the level for a return through a landmark, as FTE's SV_SaveLevelCache saves one for a program that saves itself: the player is taken out (disconnect/1), and the text is FTE's: the version (515), a comment (the level's name and kills, 39 characters, spaces as _), vmmode, the cvars skill, deathmatch, coop and teamplay, time and map, then moddata and what the program's SV_PerformSave(file, entities, slots) writes to the file it is given, saves/<map>.lvc. Returns the text and the server.

What the level change carries, as FTE's SV_SaveSpawnparms takes it: the player's spawn parameters go back into the parm* globals and parm_string, the program's SetChangeParms runs with self the player, and the globals are read into parms and string; with no player in the game both are nil. serverflags is the program's global.

The preamble of a save from save_level/1: the console variables it sets, its time, its map, and its program data with the place its moddata line ends.

Whether the program saves and restores a level itself (SV_PerformSave and SV_PerformLoad).

Set self, other and time for a call into the program.

Load map name and spawn its entities, as a fresh level, in the order of FTE's SV_SpawnServer: the info keys mapname, *cheats (ON while the cvar sv_cheats is set), *bspversion (30 for a Half-Life map) and *startspot are set (an empty one removed); the program is loaded, its dimension_send defaulted, and its init called with -1 at time 0; the model list is the world and its submodels; the world entity is set up (its model, solid bsp, movetype push, its box) and the player slots' entities allocated; the globals mapname, serverflags, time (1), and coop (when the cvar coop is set) or else deathmatch are set; initents runs; a QuakeWorld program (CRC 54730) has StartFrame run at time 0; the map's entities spawn; the world's angles are cleared and the level's name taken from its message (ext.levelname); two physics frames of a tenth of a second run from time 1, leaving the host clock at 1.3; then the info key map is set and the startspot global.

Types

command()

@type command() :: %{
  forward: number(),
  side: number(),
  up: number(),
  angles: {number(), number(), number()},
  attack: boolean(),
  jump: boolean(),
  impulse: non_neg_integer()
}

spawn_parms()

@type spawn_parms() :: %{parms: [number()], string: String.t() | nil}

What a player carries from level to level: the 64 spawn parameters, parm1 first, and the program's parm_string.

t()

@type t() :: %QuakeServer.Server{
  ambients: term(),
  area: term(),
  bsp: term(),
  budget: term(),
  builtins: term(),
  changelevel: term(),
  changelevel_startspot: term(),
  check: term(),
  clock: term(),
  command: term(),
  console: term(),
  coords: term(),
  count: term(),
  cvars: term(),
  data_dir: term(),
  edicts: term(),
  events: term(),
  ext: term(),
  frametime: term(),
  hull: term(),
  inhibit: term(),
  leaves: term(),
  lightstyles: term(),
  links: term(),
  map: term(),
  messages: term(),
  model_names: term(),
  models: term(),
  parms: term(),
  qc: term(),
  replacements: term(),
  serverflags: term(),
  skill: term(),
  slots: term(),
  sound_names: term(),
  source: term(),
  statics: term(),
  time: term(),
  trace: term(),
  versions: term()
}

Functions

advance(server, seconds)

@spec advance(t(), number()) :: t()

Move the host clock (clock) on by seconds; physics frames catch up to it.

call_named(server, name)

@spec call_named(t(), String.t()) :: t()

Run the program's function name as it stands.

change_parms(server)

@spec change_parms(t()) :: [float()] | nil

The spawn parameters the player carries to the next level (save_spawn_parms/1's parms).

client_command(server, entity, command)

@spec client_command(t(), non_neg_integer(), map()) :: t()

Run a player's command as it arrives, for a program that runs its players' commands itself (QuakeServer.Physics.Command), at the host clock (clock); a program that does not has command kept for its frames instead.

connect(server, parms \\ nil, opts \\ [])

@spec connect(t(), spawn_parms() | [number()] | nil, keyword()) :: t()

Bring the player in, as FTE does when its client begins (SV_SetUpClientEdict, then SV_Begin_Core), carrying parms from the last level (save_spawn_parms/1, or the bare list of 64), or with the program's SetNewParms run for fresh ones when nil. The player's entity is cleared (not for a NetQuake program, CRC 5927), named by the player's name info key (Player without one), and given its entity number as colormap, 16 × topcolor + bottomcolor as clientcolors (each 0 outside 0 to 13), gravity 1, maxspeed the cvar sv_maxspeed, movetype noclip and no frags. The spawn parameters go into the parm* globals and parm_string; ClientConnect runs with its argument 1 when option :csqc is true (the client runs a client program), 0 otherwise, then PutClientInServer, each with self the player and time the server's; oldorigin becomes origin; and a program that runs its players' commands runs one empty command (no time, no moves, the angles the player's v_angle).

cvar(server, name)

@spec cvar(t(), String.t()) :: float()

The value of console variable name as a number (FTE's value), 0.0 when there is no such variable.

cvar?(server, name)

@spec cvar?(t(), String.t()) :: boolean()

Whether there is a console variable name.

cvar_string(server, name)

@spec cvar_string(t(), String.t()) :: String.t()

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

disconnect(server)

@spec disconnect(t()) :: t()

Take the player out of the level, as FTE's SV_DespawnClient does to a level it keeps: ClientDisconnect runs with self the player, a NetQuake program's player entity is cleared, the player's frags become 0 and the player is no longer in the game.

drain(server)

@spec drain(t()) :: {[term()], t()}

The events of the frames since the last drain, oldest first, and the server without them.

emit(server, event)

@spec emit(t(), term()) :: t()

Add an event for the client.

exec_opts(server)

@spec exec_opts(t()) :: keyword()

The options every call into the program takes: the server's budget and trace, and QuakeServer.Autocvars.sync/1 run before the call.

frame(server, seconds, command)

@spec frame(t(), number(), command()) :: t()

Run one physics frame of seconds (QuakeServer.Physics.frame/1), command being the player's latest for a program that does not run its players' commands itself: the routes asked for before are handed to their callbacks (QuakeServer.Extensions.Routes.run/1), the replies to uri_get that have come to URI_Get_Callback (QuakeServer.Extensions.Web.run/1), the console's queued commands run, then every entity, and the physics clock (time) moves on by seconds. How long a frame should be is the caller's choice; FTE runs them as QuakeServer.Runner does.

keeping_self(server, call)

@spec keeping_self(t(), (t() -> t())) :: t()

Run call (a function of the server) and put self and other back as they were, as a builtin that calls back into the program must.

load_level(server, text)

@spec load_level(t(), String.t()) :: t()

Restore onto a freshly started level the save text of save_level/1, as FTE's SV_LoadLevelCache: the program's SV_PerformLoad(file, entities, slots) reads the text after its moddata line, every entity is linked into the world again, and the physics time, the host clock and time become the saved time.

open(name, opts)

@spec open(String.t(), keyword()) :: t()

Load map name with its world entity and the source's program, spawning nothing else and calling no hooks: the world a client program (FTE's CSQC) runs in. Takes start/2's options; its console variable table is not marked as having a server running and its latched variables stay latched.

player_name(server, entity)

@spec player_name(t(), pos_integer()) :: String.t()

The name of player entity: its name info key, Player without one (FTE's name default).

put_cvar(server, name, value)

@spec put_cvar(t(), String.t(), String.t() | number()) :: t()

Set console variable name to value, text or a number (written as FTE's Cvar_SetValue writes one), as FTE's Cvar_Set (QuakeServer.Cvars.set/4); a name there is no variable for is made, as a config or program makes one. What FTE prints about it is printed, and the server's own autocvar_ globals and info keys follow at once (QuakeServer.Autocvars.sync/1).

release(server)

@spec release(t()) :: :ok

Free the level's program memory (QuakeC.release/1) and a console variable table the server made itself; the server may not be used after.

resume(server, opts)

@spec resume(t(), keyword()) :: t()

Bring back a level left by a changelevel with a landmark, as it was left, as FTE's SV_LoadLevelCache brings one back: the change it asked for is forgotten; the info keys of option :serverinfo (the server's as they stand) are taken under the level's own and the star keys of start/2 set again for option :startspot; the program's serverflags global becomes option :serverflags, time the level's physics time and startspot the landmark; its written files become option :written and their entity tables option :entity_tables. Connect the player to it next.

run(server, function, self, other)

@spec run(t(), integer(), non_neg_integer(), non_neg_integer()) :: t()

Run QuakeC function value function with self and other set, at the server's time.

save_level(server)

@spec save_level(t()) :: {String.t(), t()}

Save the level for a return through a landmark, as FTE's SV_SaveLevelCache saves one for a program that saves itself: the player is taken out (disconnect/1), and the text is FTE's: the version (515), a comment (the level's name and kills, 39 characters, spaces as _), vmmode, the cvars skill, deathmatch, coop and teamplay, time and map, then moddata and what the program's SV_PerformSave(file, entities, slots) writes to the file it is given, saves/<map>.lvc. Returns the text and the server.

save_spawn_parms(server)

@spec save_spawn_parms(t()) :: %{
  parms: [float()] | nil,
  string: String.t() | nil,
  serverflags: float()
}

What the level change carries, as FTE's SV_SaveSpawnparms takes it: the player's spawn parameters go back into the parm* globals and parm_string, the program's SetChangeParms runs with self the player, and the globals are read into parms and string; with no player in the game both are nil. serverflags is the program's global.

saved_level(text)

@spec saved_level(String.t()) :: %{
  cvars: %{required(String.t()) => String.t()},
  time: float(),
  map: String.t(),
  moddata: binary(),
  at: non_neg_integer()
}

The preamble of a save from save_level/1: the console variables it sets, its time, its map, and its program data with the place its moddata line ends.

saves_levels?(server)

@spec saves_levels?(t()) :: boolean()

Whether the program saves and restores a level itself (SV_PerformSave and SV_PerformLoad).

set_globals(server, self, other)

@spec set_globals(t(), non_neg_integer(), non_neg_integer()) :: t()

Set self, other and time for a call into the program.

start(name, opts)

@spec start(String.t(), keyword()) :: t()

Load map name and spawn its entities, as a fresh level, in the order of FTE's SV_SpawnServer: the info keys mapname, *cheats (ON while the cvar sv_cheats is set), *bspversion (30 for a Half-Life map) and *startspot are set (an empty one removed); the program is loaded, its dimension_send defaulted, and its init called with -1 at time 0; the model list is the world and its submodels; the world entity is set up (its model, solid bsp, movetype push, its box) and the player slots' entities allocated; the globals mapname, serverflags, time (1), and coop (when the cvar coop is set) or else deathmatch are set; initents runs; a QuakeWorld program (CRC 54730) has StartFrame run at time 0; the map's entities spawn; the world's angles are cleared and the level's name taken from its message (ext.levelname); two physics frames of a tenth of a second run from time 1, leaving the host clock at 1.3; then the info key map is set and the startspot global.

Options

  • :source — the game's QuakeServer.Source module (required); the program functions its replacements/0 names run as the game's functions (QuakeServer.Builtins.call/3)
  • :builtins — the module whose call/3 runs the program's builtins, default QuakeServer.Builtins
  • :budget — how many statements one call into the program may run, default 100 000
  • :trace — a function given the name of every QuakeC function the program enters, default none
  • :hull — fun(hulls, size) choosing the hull a box of size moves through in a brush model, default QuakeServer.World.choose_hull/2
  • :inhibit — fun(spawnflags, skill), true for a map entity to leave out, default QuakeServer.Q.skill_inhibited?/2; a program with CheckSpawn filters its own

A program defining init has it called with -1 once its memory is fresh, and one defining initents has that called before the map's entities spawn.

  • :skill — 0 (easy) to 3 (nightmare), default 1
  • :serverflags — carried from level to level (the episode runes), default 0
  • :cvar_table — the QuakeServer.Cvars table of console variables to use, shared with whatever else holds it; default a table of the server's own, freed by release/1. FTE's engine variables are registered in it (QuakeServer.Cvars.Engine), its :maplatch variables take their latched values, and it is marked as having a server running. The skill option sets skill; without one a blank skill becomes 1.
  • :cvars — console variables to set in the table, name to text or number, as set sets them
  • :data_dir — the directory the program's written files (data/…) are kept in; without one they last as long as the server
  • :coords — :short (Quake's) or :float, how messages carry coordinates and angles; see QuakeServer.Builtins.Write
  • :startspot — the landmark name the last level's changelevel gave, put in the program's startspot global once the entities have spawned and settled
  • :written — the files the program wrote on earlier levels, name to bytes (the host's ext.written of the last level)
  • :entity_tables — the entity tables of those files (the host's ext.entity_tables of the last level), by which the program's edict_num translates the entity numbers it reads from them; see QuakeServer.EntityTable
  • :serverinfo — the server's info keys as the last level left them, kept under the keys this level sets, as FTE keeps its server info from level to level

A changelevel asked for during play leaves the map in changelevel and the landmark, when one was given, in changelevel_startspot.