Unreleased

Added

  • Runner.start_link(held: true) keeps the level unloaded until Runner.load/1; a client that subscribes before then is sent the serverdata when the level loads.

  • QuakeServer.Source.replacements/0 (optional): program functions the game runs itself, by name. The server's program has them replaced (QuakeC.Progs.replace/2) and QuakeServer.Builtins.call/3 runs the game's function when the program calls one.

  • QuakeServer.World.bounds/2: the box linking an entity would give it now.

  • FTE's setpos and noclip console commands for the player (QuakeServer.Cheats).

  • Runner.take/1 gives the level's map.

  • QuakeServer.Animation: the animation of the entities a program sends itself (frame as the sequence, frame1time, the server's time as animtime, and the framerate frame1time moves at), sampled each server frame and sent to the player in each packet's animation when the player lacks it, as GoldSrc's engine carries sequence, frame, animtime and framerate. QuakeServer.Network.held/2 gives the entities a player holds.

  • FTE's key bindings: QuakeServer.Keys (FTE's key numbers and names, modifiers, and the QuakeC key codes), the console's bind, in_bind, bindlevel, unbind and unbindall (QuakeServer.Console.Bindings), the bindings kept in the console variable table (QuakeServer.Cvars.binding/3, put_binding/5, bindings/1, bindmaps/1, put_bindmaps/2) and written by cfg_save while cfg_save_binds is set.

  • The console's setinfoblob, and console commands a menu program takes in its m_consolecommand.

  • QuakeServer.Cvars.put_engine_default/3, as FTE's Cvar_SetEngineDefault.

  • QuakeServer.Extensions.Settings.extension?/1.

  • The engine variables cfg_save_binds, con_maxlines, com_protocolname, fs_basepath, fs_gamepath and fs_homepath.

  • FTE's common builtins the server lacked, under FTE's server numbers: recoding (QuakeServer.Extensions.Encoding: strconv 224, strlennocol 476, strdecolorize 477, crc16 494, uri_escape 510, uri_unescape 511, argescape 295, digest_hex 639, digest_ptr, base64encode, base64decode, ftou, utof), memory as bytes (Memory: createbuffer, memrealloc, memcmp, memstrsize), JSON (Json: json_parse and the json_get_ readers, json_free, js_run_script), files (Files: fwrite, search_getfilemtime, search_fopen, fcopy 650, frename 651, fexists 653, rmtree 654), string buffers (Buffers: buf_copy 463, buf_sort 464, buf_loadfile 535, buf_writefile 536, bufstr_find 537), entity text (EntityText: generateentitydata, writetofile 606, parseentitydata 613, loadfromdata 529, loadfromfile 530, find_list, entityprotection), abort 211 and checkcommand 294 (Control), and the network (Web: uri_get 513 and uri_post, netaddress_resolve 625, sendpacket 242). Replies to uri_get reach URI_Get_Callback at the start of a server frame (QuakeServer.Extensions.Web.run/1).

  • QuakeServer.Markup: text with FTE's markup reduced to what it shows.

  • QuakeServer.Extensions.Store: entry/3, put_entry/4, new_handle/1, write/3, remove/2, written?/2, modified/2, buffer_size/1.

  • The :inets, :ssl and :public_key applications start with the server.

  • QuakeServer.Runner acknowledges the client's commands: a command's sequence is set as the program's input_sequence and sent back with every packet after it runs (FTE's PEXT2_PREDINFO last_sequence, 0 when the program does not run its players' commands); a command's integer msec is used as given.

  • The cl_netfps engine variable.

  • The server modules that were in ElixirQuake, loading through QuakeServer.Source.

  • FTE program support: CheckSpawn spawning, init/initents, SV_RunClientCommand, a console, entity networking (QuakeServer.Network), multicast packets, and about 170 extension builtins (QuakeServer.Extensions).

  • Server.open/2, a bare world for client programs; options :builtins, :budget, :hull, :inhibit, :coords, :data_dir, :trace.

  • QuakeServer.Pmove, QuakeWorld player movement for runstandardplayerphysics; openportal, getlight, coloured lightstyle, getlightstyle, getlightstylergb, getsurfacepointattribute, server polygons (R_BeginPolygon and the rest) and the physics_ builtins (no rigid-body engine).

  • Pushers turn by avelocity, swinging and turning what they carry (a client gets fixangle 2); brush models with angles collide turned.

  • SOLID_BSPTRIGGER (22): triggers shaped by their brush model; Q.trigger?/1, Q.brush?/1.

  • customphysics: an entity holding a function there has it run each frame in place of its move type.

  • changelevel(map, startspot) (QuakeServer.Extensions.Levels, and the console's changelevel map startspot); Server.start/2 options :startspot and :written; Server.resume/3 for a level kept from before.

  • Models.share/2: a client world uses its server's model numbers, its own models numbered below zero.

  • Pushers move and turn as FTE's (Physics.Push, Physics.PushTurn): riders are moved by setting and testing their spot, the clock advances only when the push went through, and a pusher its think moved is pushed that far.

  • QuakeServer.Runner: a level in its own process on its own clock, a physics frame once the cvar sv_mintic (0.013) has passed, as long as has passed up to sv_maxtic (0.1), following level changes; thinks repeat within a frame under sv_gameplayfix_multiplethinks (on). A client takes the world when it is ready (Runner.snapshot/1), as FTE sends a client its snapshot.

  • Server.release/1; the runner releases the levels it drops.

  • search_begin keeps the game files a pattern matches for the server's life.

  • FTE's console variables (QuakeServer.Cvars): one ETS table any number of processes read and write without waiting on each other, so a server, its client program and its menu share one set as FTE's single process does (Server.start/2 and open/2 option :cvar_table). FTE's flags (archive, user and server info, read only, map, renderer and video latches, not from server, private, ...), defaults and engine defaults, second names, descriptions, Q_atof/Q_atoi values, the engine's checks on r_meshpitch, r_meshroll, fov and viewsize, cvar_lockdefaults, cvar_purgedefaults, cvar_resettodefaults_*, cvarreset, latching and Cvar_LockFromServer.

  • QuakeServer.Cvars.Engine: the engine variables FTE registers that the server, the client and FreeHL's programs and configs name, with FTE's defaults, flags, descriptions and second names (maxplayers for sv_playerslots, loadas8bit for s_loadas8bit, ...).

  • QuakeServer.Autocvars: a program's autocvar_ globals follow their variables, made with the global's value when missing, as FTE's PR_AutoCvar, for server, client and menu programs alike; :serverinfo and :userinfo variables are a server's info keys and its players'. Server.exec_opts/1 brings a program up to date before each call.

  • The console's variable commands as FTE's: set with the rest of the line as value and // description, seta, setfl, set_tp, toggle name [a [b]], inc, a variable's name printing its value, default and latch, cvarreset, vid_reload, vid_restart; exec with FTE's rules (config.cfg superseded by an earlier default.cfg, fte.cfg after it, cvar_lockdefaults after default.cfg, name.cfg for name); cfg_save, cfg_save_ifmodified; aliases kept with the variables; unknown commands reported only while cl_warncmd is set; Console.run/3 option from: :server for commands a server sends.

  • Cvar builtins as FTE's: cvar makes a missing variable while pr_autocreatecvars is set and answers pr_checkextension and halflifebsp; cvar_string gives a latched value; cvar_set refuses :notfromserver variables; registercvar takes DarkPlaces flags; cvar_type gives FTE's bits; cvar_defstring, cvar_description, buf_cvarlist (517) and cvars_haveunsaved.

  • QuakeServer.Source callback depth/1, where a file is in the search order.

Changed

  • checkbuiltin asks the program's own builtins module whether it knows a builtin.
  • A server that ends (QuakeServer.Runner) no longer counts as running in the console variable table.
  • Server.cvar/2 reads a value as FTE's Q_atof (a 32-bit float); Server.put_cvar/3 sets as FTE's Cvar_Set and writes a number as Cvar_SetValue does. A server's own table registers the engine variables; the defaults the server used to keep in itself are the engine list's. Server.release/1 frees a table the server made itself.
  • exec no longer looks under cfg/ for a file it cannot find; sets and setu are gone, as FTE has neither.

Fixed

  • ftos writes a fraction as FTE's Q_ftoa (every significant digit of the 32-bit float, no padding) and vtos as '%f %f %f'; they wrote Quake's %5.1f, so a value such as 0.75 became 0.8, and the numbers FreeHL writes into cvars, info keys and save files lost their fractions. pr_brokenfloatconvert brings back %5.1f for ftos.

  • random is FTE's: steps of 1/32768 offset by half a step (never 0 or 1), scaled by one argument or spanning two; it ignored its arguments and could return 0.

  • findradius is FTE's: an optional field to chain through, entities with solid 0 found when their flags hold FL_FINDABLE_NONSOLID (16384), and the cvars sv_gameplayfix_blowupfallenzombies and sv_gameplayfix_findradiusdistancetobox.

  • aim is FTE's: off (straight along v_forward) while sv_aim is 1 or more, the shot from the shooter's view_ofs height rather than Quake's fixed 20 units, and teammates skipped under teamplay.

  • objerror prints Program error: with its arguments joined, removes self and stops every running function, as FTE's does; it went on running the caller.

  • breakpoint prints break statement and dprint prints while developer is set, as FTE's do; both did nothing.

  • setmodel sizes the entity by FTE's rule: always for a world submodel, for any model when the program is not a QuakeWorld one or sv_gameplayfix_setmodelsize_qw is set, and otherwise only for a found model that is not a Quake alias model.

  • MOVETYPE_FOLLOW turns the follower's offset with its punchangle and the leader's angles scaled by r_meshpitch and r_meshroll, as FTE's WPhys_Physics_Follow does.

  • The move types FTE runs that the server left still: 13 (H2PUSHPULL) moves as a pusher, 14 (H2SWIM) and 33 (FLY_WORLDONLY) as tossed things without gravity (33 as a walking player for the player), and 32 (PHYSICS) thinks and is relinked.

  • Monster steps, walking and getting unstuck use FTE's step height (QuakeServer.Physics.step_size/1): pm_stepheight, 18 while it is empty.

  • Coordinates in eighths and angles in 256ths of a turn are rounded half away from zero, as FTE's MSG_ToCoord and MSG_WriteAngle8 write them; they were cut toward zero.

  • vectoangles is FTE's (QuakeServer.Angles.vectoangles/3): angles keep their fractions of a degree, up gives the roll, and pitch and roll follow r_meshpitch and r_meshroll; it gave whole degrees, as Quake's does. With FTE's sv_mintic of 0 a frame lasts a few milliseconds, and FreeHL's monsters turn less than a degree a frame, so a turn was lost to the whole degree each frame: Barney in c0a0e stopped halfway down the walkway once his heading was 5 degrees off his route, gave up the route there and played his door sequences on the spot. He now walks to the tram button and to the retina scanner and opens the doors there.

  • Entity references in a file carried to the next level follow Half-Life's level transition rule (GoldSrc, the HLSDK's CSaveRestoreBuffer) instead of FreeHL's under FTE (QuakeServer.EntityTable): each written file keeps an entity table of its { } blocks (the entity that wrote each), carried with the written files (server option :entity_tables), and while the program reads a carried file edict_num gives the entity a block was restored into for an entity that was carried, keeps the world and player slots, and gives the world for any other number. FreeHL's c0a0a no longer has the tram's timer from c0a0 take over the entity of the narration's trigger: the tram gets a fresh timer, keeps moving, and tride/c0a0_tr_dest.wav plays.

  • externvalue, isfunction, callfunction and function keys find 0:name as FTE's ED_FindFunction does, so a level brought back keeps its entities' functions.

  • Numbers in text are read as C reads them (QuakeServer.TextNumbers): stof and the other atof readings take .3, -.5, +1, hex and C white space, giving a 32-bit float; entity keys are read as FTE's ED_ParseEpair reads them (strtod for floats and each vector word, strtol base 0 for integer and entity fields). .3 read as 0, so FreeHL's c0a0 fired the start_titles trigger_auto (delay .3) at once and the tram left 0.3 seconds before FTE's.

  • stov stops at the first word that is not a number and at a closing ', as FTE's PF_stov.

  • getentityfieldstring is FTE's: values as PR_UglyValueString writes them (functions as 0:name) and FTE's left-out fields; function keys read 0:name as FTE does.

  • New entities get drawflags 96, as FTE spawns them.

  • checkpvs (240) decides as FTE's PF_checkpvs: from the leaves recorded when the entity was last linked, not its box as it stands; an entity never linked is not seen, one in more than 32 leaves always is (QuakeServer.Visibility.in_pvs?/3). FreeHL's level change from c0a0 to c0a0a now carries the four scripted_sequences and the train, as under FTE, instead of sixteen path_tracks.

  • QuakeServer.Visibility.pvs/2 takes a point on a node's plane to the back side, as FTE's ClusterForPoint does.

  • Server.start/2 runs FTE's SV_SpawnServer steps in its order: the info keys mapname, *cheats, *bspversion and *startspot before the program loads (an empty one removed, not kept empty); init(-1) at time 0 on fresh memory, before the model list and the world entity; the world's box (mins, maxs, absmin, absmax); the player slots' entities allocated from the start; time 1, and coop or deathmatch from their cvars, before initents; the world's angles cleared and the level's name taken from its message (ext.levelname) after spawning; the map key set after the settle frames. Option :serverinfo carries the server's info keys from level to level, as FTE's server info lasts.

  • Server.connect/3 is FTE's SV_SetUpClientEdict and SV_Begin_Core: the player gets its name from its info keys (Player by default), clientcolors, gravity, maxspeed, movetype noclip and no frags; ClientConnect is given 1 for a client running a client program (option csqc: true); oldorigin follows origin; a program running its players' commands runs one empty command; parm_string is set and carried with the spawn parameters. A NetQuake program's player entity is not cleared on connecting but on leaving.

  • Server.save_spawn_parms/1 is FTE's SV_SaveSpawnparms: the player's parameters go back into the globals before SetChangeParms, and parm_string and serverflags are carried (serverflags was not carried by the runner). change_parms/1 uses it.

  • Server.disconnect/1: FTE's SV_DespawnClient, run (ClientDisconnect) on a level kept for its landmark, as FTE's SV_SaveLevelCache does.

  • A level left through a landmark is kept as FTE's level cache keeps it: a program with SV_PerformSave and SV_PerformLoad (FreeHL) has the level saved in FTE's format (Server.save_level/1, after ClientDisconnect) and, on a return, started afresh with the save's console variables and restored by the program (Server.load_level/2, the world relinked and the saved time taken up); the level was kept in memory as it stood and the program's own save and restore never ran. Store.open_buffer/4, Store.open_output/3 and Store.output/3 hand the program FTE's engine files. Another program's level is still kept as it stands; Server.resume/2 brings it back as SV_LoadLevelCache does: the star keys again, the server's current info keys under the level's, serverflags and time.

  • The runner follows FTE's listen server: a level starts without the player and stays paused (the clock held at 1.5) until its client begins (Runner.begin/3, after the client sent {:serverdata, …} has loaded); the view set on beginning is the player's angles under fixangle, else v_angle, with roll. A level change sends the reliable messages still queued as a last packet before {:changing, map}, and carries the player's info keys. Its levels share one console variable table (the :cvar_table option, or one of its own) instead of replaying the console commands given before into each new level, which repeated a changelevel typed at the console forever. The client's server info comes in the serverdata (FTE's fullserverinfo, without mapname) instead of a stuffed command in the first packet.

  • QuakeServer.Signon: static entities (makestatic), static sounds (ambientsound) and the player's info keys are sent to the client before it begins, as FTE's svc_spawnstatic, svc_spawnstaticsound and svc_updateuserinfo; they were kept on the server and never sent.

  • Players are sent the server's sound list and particle effect list, and what is added to them later (QuakeServer.Precaches, from QuakeServer.Network.frame/2), as FTE's signon and delayed precaches send them.

  • pointparticles and trailparticles go to the players that may hear them as FTE's svcfte_pointparticles, svcfte_pointparticles1 and svcfte_trailparticles, besides the server's events.

  • getsoundindex adds a sound not on the list unless asked only to look it up.

  • QuakeServer.Builtins.Write.coord/2 and coords/2: coordinates as WriteCoord writes them.

  • serverkey joins its arguments, gives null for an empty value, and answers FTE's engine keys (Settings.server_key/2).

  • A level change sends the client {:changing, map} first.

  • Sounds start as FTE starts them (QuakeServer.Sounds): a sound message in the players' packets, reliable for brush entities, to everyone when reliable or unattenuated, to the players that may hear it otherwise; sound takes FTE's pitch, flags and offset arguments and a QuakeWorld program's reliable channel bit; pointsound too.

  • Changes to the server's info keys reach the players (svc_serverinfo), and each level starts by sending them all (fullserverinfo).

  • Light style colours reach the client with the styles.

  • A server-side pose takes all five bone controllers and both sub-blends as floats.

  • The console's sky name sets the sky drawn.

  • QuakeServer.Runner pushes each packet to its subscribed client (subscribe/2) and hands events, the player's eye and view angles over in a table (handoff/1, take/1), so neither the client nor the game waits on a busy server.

  • processmodelevents is FTE's: a looping sequence's events come round every cycle, events fire from the base time up to but not including the target, and nothing fires when the two are equal.

  • Entity delivery is FTE's: SendFlags are kept every server frame and cleared after, updates go out with each packet a player is sent, entities never sent or sent back into sight get every bit, PVSF_NOREMOVE keeps an entity on the client, and the player may see what FTE's SV_Snapshot_BuildQ1 lets through: the fat PVS (leaves within 8 units of the eye), leaves recorded when an entity is linked, PVSF_USEPHS never culled on Half-Life maps or without a PHS, EF_NODEPTHTEST, tag_entity roots, viewmodelforclient, customizeentityforclient, nodrawtoclient, drawonlytoclient and dimensions.

  • multicast reaches only the players FTE sends to (PVS, PHS within 1024 units or by the PHS, msg_entity, dimension_send), reliably or in the datagram, and a packet carries the reliable messages before the entity updates and the datagram after.

  • bprint and sprint read a QuakeWorld program's print level before the text, join every text argument, and reach the player as svc_print; centerprint keeps the last one for the next packet.

  • Entities spawn with dimension_see, dimension_seen, dimension_solid and dimension_hit at dimension_default (255), and Version and uniquespawnid set; dimension_send starts at dimension_default.

  • Freeing an entity clears pvsflags and SendEntity (and classname for a QuakeWorld program) and is refused for the world, a player or an entity already free.

  • Every brush model is traced with the hull FTE chooses (World.choose_hull/2, the default :hull).

  • route_calculate is FTE's: the waypoints are read once a level, the start and end waypoints are those whose radius holds the point or that it can see through the world, the node list ends with the entity's place and starts with the destination flagged LF_DESTINATION, a route with no path still gives the destination and the start, and the callback runs at the start of the next frame.

  • A trace that starts inside one entity is still stopped by the next thing in its way, as FTE combines its traces; it was taken whole from the entity it started in.

  • A pusher tests what it carries as if it were not solid by setting solid and putting it back, as FTE does, and an entity it nudges moves an eighth of a unit once.

  • A tossed, bouncing or flying entity on the ground keeps what its think did.

  • tokenize finds the end of a word without testing each character against a list.

  • An entity's solid is read when it is traced or touched, not when it was last linked.

  • All 64 spawn parameters are carried between levels, each in its own place.

  • fopen of a name without data/ reads data/name first, then name.

  • externvalue and externset take program numbers 0 to -3 and names spread over several arguments, return vectors whole, and &name gives a global's address.

  • World.stuck? counts allsolid and tests non-solid and trigger entities against brush models only.