{"id":18776610,"url":"https://github.com/paril/q2horde","last_synced_at":"2025-04-13T09:32:01.858Z","repository":{"id":212189701,"uuid":"728425266","full_name":"Paril/q2horde","owner":"Paril","description":"Quake II Horde mode","archived":false,"fork":false,"pushed_at":"2023-12-13T00:26:58.000Z","size":1588,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-27T01:12:29.497Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Paril.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-12-06T23:08:36.000Z","updated_at":"2025-01-08T04:37:20.000Z","dependencies_parsed_at":null,"dependency_job_id":"a8516842-7002-4915-bef3-e31f6aa1fcca","html_url":"https://github.com/Paril/q2horde","commit_stats":null,"previous_names":["paril/q2horde"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Paril%2Fq2horde","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Paril%2Fq2horde/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Paril%2Fq2horde/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Paril%2Fq2horde/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Paril","download_url":"https://codeload.github.com/Paril/q2horde/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248690853,"owners_count":21146218,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-07T19:47:37.046Z","updated_at":"2025-04-13T09:32:01.844Z","avatar_url":"https://github.com/Paril.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Quake II Rerelease Game Source\n\nThis repository contains the game code for the 2023 rerelease of Quake II, for users who wish to mod the game, along with the original game code that was use for reference. Mods can be loaded into the rerelease the same way as the original game: launch the game with `+set game mymod` or type `game mymod` into the console while the game is running. We recommend installing mods into your `%USERPROFILE%\\Saved Games\\Nightdive Studios\\Quake II` directory to ensure the original game files do not get modified.\n\nid Software is unable to provide support for this release, however we urge you to take advantage of the depth of community-driven resources already available.\n\nThe rerelease of Quake II uses a new version of the API to communicate between the server \u0026 the game module. It also introduces a very thin \"client game\" module, akin to Quake III Arena's cgame module, to allow for extended modding opportunities that change previously hardcoded client behavior. It also has a new network protocol, version 2023.\n\nThis codebase is a combination of the separate game modules that were part of the original game: baseq2, ctf, rogue, and xatrix. It requires a C++17 compiler. In cases of conflicting spawnflags, maps were modified in order to resolve issues, so original expansion pack maps may not load correctly with this DLL. The combined FGD as used for development is also available for users wishing to make new maps. A modified TrenchBroom Quake II `gameconfig.cfg` is included as there are modified textureflags.\n\nBecause the game export interface has changed, existing mods may be able to be moved over to support the API changes. However, in order to support all expansion packs under one codebase and new features in the rerelease, there have been some major changes to structure and layout, so old mods wishing to use the new codebase may need to be rewritten.\n\nThe API changes discussed here are written from the perspective of the game DLL.\n\n## Compiling\n\nThe game DLL has only been tested with Clang, VS2019 and VS2022.\n\nThe code can compile under both C++17 and C++20. Using C++20 allows you to skip `fmtlib` as a dependency.\n\n### Required preprocessor definitions\n* `GAME_INCLUDE` must be defined, tells `game.h` that this is the game DLL compiling it.\n* `KEX_Q2GAME_EXPORTS` must be defined, tells `game.h` that we are exporting the `GetGameAPI` function.\n* `KEX_Q2GAME_DYNAMIC` must be defined. The game DLL supports static linking on console platforms, but is always dynamic on PC.\n* `NO_FMT_SOURCE`: this is only here because of a limitation in the internal build system. It must be defined.\n\n### Optional preprocessor definitions\n* `KEX_Q2_GAME`: must be defined if compiling for Kex. This changes the behavior of the `say` command to go through the lobby.\n* `KEX_Q2GAME_IMPORTS`: only used by engine, tells `game.h` that we are importing `GetGameAPI`.\n* `USE_CPP20_FORMAT`: if explicitly defined, C++20's `\u003cformat\u003e` library will be used instead of `fmtlib`; otherwise `fmtlib` usage will be autodetected.\n\n\n### Dependencies\n* [fmtlib](https://github.com/fmtlib/fmt): If `USE_CPP20_FORMAT` is not set, the library needs to be available in the `fmt` subdirectory.\n* [jsoncpp](https://github.com/open-source-parsers/jsoncpp): Must be placed inside `json` subdirectory.\n\nBoth of these can also be installed via vcpkg: `vcpkg install jsoncpp:x64-windows fmt:x64-windows`\n\n### Windows (Visual Studio 2019 / 2022):\n* We recommend placing the source in a subfolder within a mod directory. For example, alongside `baseq2`, make a folder called `mymod`, enter that folder, make a folder called `src`, and copying the contents of the `rerelease` directory into the newly-created `src` subfolder.\n* Open `game.sln`\n* Build solution\n\nDebugging the DLL is possible when attaching to the engine EXE. Note that if you are using VS2022 Hot Reload, due to an internal Hot Reload issue, current edits will be lost when disconnecting from the server, or changing levels using the `map` command.\n\n## 40hz Tickrate Support\n\nAs part of this release, all internal logic in the game DLL has been adjusted to run at 40hz compared to the original 10hz of the original engine. This provides a better gameplay experience, and allows maps and game logic to run at more precise steps than the original 100ms. 40hz was chosen as it is a multiple of the original 10hz, operates at an integral 25ms step, and was the best balance between bandwidth and CPU concerns around the original tech.\n\n## Print Adjustments\n\nAs part of the API cleanup, the game DLL no longer uses varargs in any of its functions. Varargs are compiler-specific and not very portable, so instead, the onus is on the caller to handle formatting. As a bonus, this allows the game DLL to more easily hook in modern formatting providers; the game DLL uses `fmt` almost exclusively. Several built-in types, like `edict_t` and `vec3_t`, can be formatted directly.\n\n## Math Changes\n\nSince C++ is now used in the game DLL, math functions were made constexpr where appropriate, and operator overloads are used to make math easier to work with and closer to QuakeC. For instance, `VectorMA(a, s, b, c)` can now be written as `c = a + (b * s)`, which expresses the operation better.\n\n## Type Changes\n\n`qboolean`, which was aliased to `int32_t`, is now instead aliased to `bool`. This type should be equivalent to C's `_Bool`.\n\n## Info Keys\n\nIn the original Quake II, all infokey values are purely ASCII with upper bits stripped. Kex and the Quake II rerelease engine supports UTF-8 for things like player names, which necessated a change to the way info keys work. Instead of implementing a whole UTF-8 system in the game DLL, these functions are now imports, so the engine is in control of the parsing and string management.\n\nUserinfo variables are now suffixed with a number for split screen support. For instance, `name` and `skin` have become `name_0` and `skin_0` for the first player, `name_1` and `skin_1` for the second player, and so on.\n\n## Extensions\n\nIn an attempt to remain compatible with future community engines, all engine interfaces contain stubbed functions for GetExtension. This is currently unused and will only return nullptr, however other engines may wish to support returning named structs containing extra function pointers based on string parameters. This is similar to `getextension` that has become standard in many QuakeC environments.\n\nConforming engines should return nullptr if an extension is not supported, otherwise they should return a non null pointer value that is relevant to the requested feature. Supporting engines should use namespaces for features to prevent name collisions around common and possibly incompatible implementations.\n\n## Player Movement\n\nPlayer movement (\"pmove\") is now handled as an export in both `game_export_t` *and* `cgame_export_t`. This allows a game module to modify physics while still working with client prediction. Pmove also received several upgrades, such as more bits for flags and full float positioning.\n\nBecause a lot of movement quirks in Quake II were indirectly caused by the compression, these behaviors were retained. Trick jumping is now an explicit movement type, to allow for things like the Mega Health box jumps to still work. Some fixes were made, like jumping higher below the 0 z point of the map.\n\n## Frame visibility\n\nAs part of network improvements, some changes were made to the \"entity is visible to client in frame\" methods:\n* Split-screen support since all clients share a frame.\n* Entities with shadows will be visible if their shadows may be visible to a player.\n* Sound attenuation culling is now calculated formulaically to match the sound system, and takes loop_attenuation into account.\n\n## Entity linkage\n\nTo fix a legacy bug where lasers only relied on one position for culling, RF_BEAM entities now set their `absmin` \u0026 `absmax` properly. This can cause them to be a bit inflated if they are angled, but should not cause any issues otherwise.\n\nIn a similar vein, `gi.linkentity` no longer automatically sets `old_origin` for beams. This is to make it a bit easier to work with beams, as otherwise you'd be forced to link twice to get it linked into the world correctly. This *might* break old mods that depends on this behavior.\n\n## Audio positioning\n\nEntity spatialization underwent an overhaul (`CL_GetEntitySoundOrigin` mainly).\n\n* Brush models will use the closest point on the bmodel's absmin/absmax to the listener's origin. This allows moving brush models with sounds to make consistent sounds, and be full volume if you are inside of them.\n* Beams now support `s.sound`, and plays their sound on the nearest point between the two beam origins.\n\nAs a secondary fix to the above, `S_StartSound` has slightly different logic now surrounding what origin to pick when playing a sound:\n\n```cpp\nif (entnum \u003c MAX_EDICTS \u0026\u0026 (origin || fixed_origin))\n{\n\t// [Paril-KEX] if we can currently see the entity in question\n\t// and it's a bmodel or beam, don't use a fixed origin so it sounds correct\n\tif (!fixed_origin \u0026\u0026 entnum \u003e 1 \u0026\u0026 (cl_entities[entnum].serverframe == cl.frame.serverframe || \n\t\t(cl_entities[entnum].current.solid == PACKED_SOLID_BSP || (cl_entities[entnum].current.renderfx \u0026 RF_BEAM))))\n\t{\n\t\tps-\u003efixed_origin = false;\n\t}\n\telse\n\t{\n\t\tVectorCopy (origin, ps-\u003eorigin);\n\t\tps-\u003efixed_origin = true;\n\t}\n}\nelse\n\tps-\u003efixed_origin = false;\n```\n\n`fixed_origin` is set to `flags \u0026 SND_EXPLICIT_POS` for svc_sound packets, and is `false` otherwise. If the playsounds' `fixed_origin` field is set, then the `ps-\u003eorigin` value will *always* be used over automatically trying to determine its position.\n\n## Client entity adjustments\n\n* Beam origin points are interpolated if they exist between frames.\n* `TE_ELECTRIC_SPARKS` and `TE_SCREEN_SPARKS`/`TE_SHIELD_SPARKS` will only play sounds once on any given frame.\n* Entities will never play the same footstep sound twice in a row.\n* Beams now squash the ends of their beams so they don't intersect walls or end early.\n* Alpha and transparency settings now get copied over to all sub-modelindices.\n* Lightramps are now interpolated, which looks nicer and helps with epilepsy.\n* `delta_angles` are interpolated now, although this is never used at all in the game.\n* `screen_blend` and `damage_blend` are interpolated now, but only if the frame prior didn't have a clear color.\n\n## Configstrings\n\nConfigstrings have been overhauled. There is now a theoretical maximum of `32767` entries, although you are still bound by the game APIs value of `MAX_CONFIGSTRINGS`.\n\nThe maximum length of a configstring has increased from `64` to `96`.\n\nThe API now canonizes that certain spans (`CS_STATUSBAR` and `CS_GENERAL`) can span multiple lines. A `CS_SIZE` function is provided to calculate the total size (in bytes) that can be written to for a given configstring ID.\n\nA convenience function, `CS_REMAP`, is provided to help remap old configstring IDs to new ones. This is used in our engine to provide old demo support.\n\n`MAX_MODELS`, `MAX_SOUNDS` and `MAX_IMAGES` have been increased from 256 to 8192, 2048, and 512, respectively.\n\n### CS_STATUSBAR\n\nThis entry now spans entries 5 to 58 instead of 5 to 28, giving you an effective size of 5184 bytes (minus one for the null terminator) for the statusbar strings, up from 1536 bytes.\n\n### CS_SHADOWLIGHTS\n\nThis new span each consists of a shadow light entry. Its format is semicolon-separated numerical values, in the following type \u0026 order:\n\n* int entity_order\n* int type (0 = point, 1 = cone)\n* float radius\n* int resolution\n* float intensity\n* float fade_start\n* float fade_end\n* int lightstyle\n* float coneangle\n* float direction_x\n* float direction_y\n* float direction_z\n\n### CS_WHEEL_WEAPONS\n\nThis new span consists of entries for the weapon wheel. It consists of pipe-separated integral values, in the following order:\n\n* CS_ITEMS item index\n* CS_IMAGES image index\n* CS_WHEEL_AMMO ammo index (or -1 for no ammo)\n* minimum ammo to use weapon\n* whether the weapon is on the powerup wheel or the weapon wheel\n* additional sort integer\n* quantity to warn on low ammo on\n* whether this weapon is droppable or not\n\n### CS_WHEEL_AMMO\n\nThis new span consists of entries for the weapon wheel ammo types. It consists of pipe-separated integral values, in the following order:\n\n* CS_ITEMS item index\n* CS_IMAGES image index\n\n### CS_WHEEL_POWERUPS\n\nThis new span consists of entries for the powerup wheel. It consists of pipe-separated integral values, in the following order:\n\n* CS_ITEMS image index\n* CS_IMAGES image index\n* if 1, it is a togglable powerup instead of having a count\n* additional sort integer\n* whether we can drop this powerup or not\n* CS_WHEEL_AMMO ammo index, if applicable (-1 for no ammo)\n\n### CS_CD_LOOP_COUNT\n\nInteger which determines how many times to loop the music before switching to the ambient track. Leave blank to use the clients' preferred value, otherwise it is forced to this value (a value of zero means never switch to ambient track).\n\n### CS_GAME_STYLE\n\nInform the client about the type of game being played.\n\n# Structures\n\nQuake II has two main shared structures: `gclient_t` and `edict_t`. These split off into various other shared structures that have to be the same between the game \u0026 server.\n\nLike the original release, the \"shared\" aspects of these structs must be identical, and are stored in `game.h` as `edict_shared_t` and `gclient_shared_t` respectively.\n\nThe structure changes will be listed from the bottom-up. Full listings of the structures can be found in the source.\n\n## cvar_flags_t\n\n### CVAR_USER_PROFILE (bit 5)\n\nThis is a new flag that is solely for the client; it indicates that a cvar is treated like userinfo for the purposes of storage, but is not sent to the server like userinfo usually is. For example, this flag is applied to `cl_run_N`, which controls the individual Always Run flags for each split screen player.\n\n## contents_t\n\n### CONTENTS_PROJECTILECLIP (bit 14)\n\nThis new content flag will be collided against by `CONTENTS_PROJECTILE` entities.\n\n### CONTENTS_PLAYER (bit 30)\n\nThis special content type flag is set only on player entities, and allows tracing to exclude/include them.\n\n### CONTENTS_PROJECTILE (bit 31)\n\nThis special content type flag is set only on projectiles, and allows tracing to exclude/include them.\n\n## surfflags_t\n\n### SURF_ALPHATEST (bit 25)\n\nThis bit is widely supported by other engines and is supported in the rerelease.\n\n### SURF_N64_UV (bit 28)\n\nThis flag is specific to N64, and halves texture sizes.\n\n### SURF_N64_SCROLL_X (bit 29)\n\nThis flag is specific to N64, and causes textures to scroll in the X axis.\n\n### SURF_N64_SCROLL_Y (bit 30)\n\nThis flag is specific to N64, and causes textures to scroll in the Y axis.\n\n### SURF_N64_SCROLL_FLIP (bit 31)\n\nThis flag is specific to N64, and flips the scroll axis.\n\n## csurface_t\n\nThis structure has undergone canonization of the 3.2x changes by Zoid.\n\n### char[32] name\n\nUses the proper name length now.\n\n### uint32_t id\n\nThis value must represent a unique ID that corresponds to its texinfo. The same ID must always reference the same texinfo, but they don't necessarily have to be sequential. Zero must always mean \"no texinfo\".\n\nThis is used by the client for indexing footstep sounds.\n\n### char[16] material\n\nThe material ID for this texinfo, from the corresponding `.mat` file.\n\n## trace_t\n\n### csurface_t *surface\n\nThe only change is a contractual one: this value must never be null.\n\n### cplane_t plane2 / csurface_t *surface2\n\nWhen a trace impacts multiple planes at the destination, the collision system will now report both of them. The \"second best\" plane and surface are stored here. `surface2` *must* be null if a second surface was not hit.\n\nThis is used to solve some epsilon issues with the player movement system.\n\n## cvar_t\n\n### int32_t modified_count\n\nThe old `qboolean modified;` has been changed into an integral value. This value is increased when the cvar has been changed, but is **never** zero. The reason for this is so that \"is cvar modified\" checks always succeed on the first check, assuming you initialize the last modified value to 0.\n\nThe function `Cvar_WasModified` is provided as a convenience function to perform this task for you.\n\n### int32_t integer\n\nA common extension to Quake II, the integral value is stored at the end of the struct for you to use.\n\n## player_state_t\n\n### int32_t gunskin\n\nThis is a new value which sets the skin number used on the weapon.\n\n### int32_t gunrate\n\nThis value sets the frame rate (in hz) of the players' weapon. For backwards compatibility, a value of 0 is equivalent to a value of 10. This ia mainly used for the Haste powerup, but in theory it could be used for future mods to have higher tickrate weapons in general.\n\n### float[4] screen_blend / damage_blend\n\nThe full-screen `blend` value was split into two values: `screen_blend` and `damage_blend`.\n\n`screen_blend` is the same as the original one, and is a full-screen color change. It is mainly used now for full-screen fades. To reduce the amount of screen flashing, the base game avoids flashing the screen whenever possible.\n\n`damage_blend` is a new type of blend that occurs around the edge of the screen; this is used to replace many events that previously would flash the full screen.\n\n### refdef_flags_t rdflags\n\nThe only adjustment to `rdflags` was the addition of a new flag: `RDF_NO_WEAPON_LERP`. This occupies bit 4, and can be used to temporarily disable interpolation on weapons.\n\n### short[64] stats\n\n`MAX_STATS` was increased from 32 to 64. Note that because stats are now handled by the game \u0026 cgame modules, you are not limited to a short for the purposes of packing down data/\n\n### uint8_t team_id\n\nFor teamplay-oriented games, the player's team is sent in player state. While the client could derive this from entity state in theory, in practice that's a bit ugly since the players' entity may not even be visible (for instance if you've been gibbed), so this was the cleaner approach.\n\n## usercmd_t\n\nThe fields `upmove`, `impulse` and `lightlevel` have been removed.\n\n### button_t buttons\n\n#### BUTTON_HOLSTER (bit 2)\n\nThis button corresponds to the new `+holster` command, which will keep the weapon holstered until depressed. It is used by the weapon wheel to allow the player to start switching weapons before the weapon wheel is dismissed.\n\n#### BUTTON_JUMP (bit 3)\n#### BUTTON_CROUCH (bit 4)\n\nThese two new bits replace `usercmd_t::upmove`, and determine the players' jumping and crouch states.\n\n### vec3_t angles\n\nThese are now full float precision, allowing for players to aim more precisely.\n\n### float forwardmove / sidemove\n\nThese are now full float, to allow controller inputs to be more precise. \n\n### uint32_t server_frame\n\nNew entry sent along with every usercmd, which tells the server which server frame that the input was depressed on. This is used for integrity checks, as well as for anti-lag hitscan.\n\n## pmove_state_t\n\n### pmtype_t pm_type\n\nTwo new pmove types have been added before `PM_SPECTATOR`, offsetting it and its subsequent entries by 2.\n\n#### PM_GRAPPLE (1)\n\nUsed for the grappling hook; it informs client prediction that you should be pulled towards `velocity` and are not affected by gravity.\n\n#### PM_NOCLIP (2)\n\nThis is what `PM_SPECTATOR` used to be, and prevents all clipping.\n\n#### PM_SPECTATOR (3)\n\nThis value now represents spectator mode; you cannot enter walls, but can go through brush entities.\n\n### vec3_t origin / vec3_t velocity / vec3_t delta_angles\n\nThese fields now have full float precision versus the original release. See [Pmove](#pmove) for more details.\n\n### pmflags_t pm_flags\n\nThis type has had its capacity increased to `int16`. The following flags are new or adjusted:\n\n#### PMF_NO_POSITIONAL_PREDICTION\n\nThis flag was originally called `PMF_NO_PREDICTION`; it now only disables prediction on origin, allowing angles to be predicted. **This is a backwards-incompatible change**, but should have very minimal impact on running old mods. This improves the feeling of the grappling hook.\n\n#### PMF_ON_LADDER\n\nThis bit is used to signal back to the game that we are currently attached to a ladder.\n\n#### PMF_NO_ANGULAR_PREDICTION\n\nThe angular equivalent of `PMF_NO_POSITIONAL_PREDICTION`.\n\n#### PMF_IGNORE_PLAYER_COLLISION\n\nThis flag is input only, and tells Pmove to ignore `CONTENTS_PLAYER` contents.\n\n#### PMF_TIME_TRICK\n\nIf set, then `pm_time` is the time remaining to start a trick jump.\n\n### uint16_t pm_time\n\n`pm_time` is now expressed in milliseconds instead of 8 * ms; since the code clamped subtractions on this to 1, it meant that high framerate players experienced slightly different physics, and in the case of trick jumps, had a smaller time gap to perform them.\n\n### int8_t viewheight\n\nA new field describing the viewheight output; this is for crouch prediction.\n\n## pmove_t\n\nThe field `viewheight` has been removed, since it is now part of `pmove_state_t`.\n\n### touch_list_t touch\n\nThe list of touched entities has been replaced with a list of traces, allowing the game to react better to touches.\n\n### cplane_t groundplane\n\nThe plane that you're standing on is now returned by pmove.\n\n### edict_t *player\n\nAn opaque handle to the player object, passed back to `trace`.\n\n### trace / clip\n\n`trace` is now sent the `passent` and `contentmask`, so it can perform more complex tracing routines.\n\n`clip` is also now available to pmove, should you need it. It is currently only used in spectator movement, to clip solely against the world and nothing else.\n\n### vec3_t viewoffset\n\nThe player's viewoffset is now passed in, to allow for accurate blending. Pmove is now semi-responsible for screen blends.\n\n### vec3_t screen_blend\n\nAn output variable containing full-screen blends to apply to the view.\n\n### refdef_flags_t rdflags\n\nAn output variable containing flags that should be merged with the server's representation.\n\n### bool jump_sound\n\nAn output variable to tell the game to play a jumping sound.\n\n### float impact_delta\n\nWhen new ground is achieved, the impact is stored here for fall damage checks.\n\n## edict_shared_t\n\nNOTE: the following members of the old `edict_t` struct have been removed, and were moved server-side:\n* `link_t area`\n* `int num_clusters`\n* `int clusternums[]`\n* `int headnode`\n\n### sv_entity_t sv\n\nMost of the meat of the bot system is contained in the server code, and doesn't have direct access to the games' representation of the state of the game.\n\nBots use this thin interpretation of the game state data about entities to understand how to use entities to its advantage - similar to how the client receives a thin portion of entities to understand how to render them.\n\n### bool linked\n\nThis boolean indicates whether the entity is currently linked into the world or not. It is the replacement of checking for `area.prev` being non-null.\n\n### svflags_t svflags\n\nFor new functionality, some new flags were added to `svflags`. **This may cause backwards-incompatibility in older mods that have modified this enum!** This enum is server-specific, so it is always incorrect for mods to modify this.\n\n#### SVF_PLAYER\n\nThis flag causes the object to be treated as `CONTENTS_PLAYER` for collision. All players have this flag.\n\n#### SVF_BOT\n\nThis flag marks the entity as a bot.\n\n#### SVF_NOBOTS\n\nThis flag tells the bot subsystem to ignore this entity.\n\n#### SVF_RESPAWNING\n\nThis flag is a hint to the bot subsystem to inform it about how items respawn.\n\n#### SVF_PROJECTILE\n\nThis flag treats the entity as `CONTENTS_PROJECTILE` for collision.\n\n#### SVF_INSTANCED\n\nThis flag marks the entity as being instanced - it may be invisible or hidden for certain players.\n\n#### SVF_DOOR\n\nThis flag is for the bot subsystem, and informs it that this entity is a door.\n\n#### SVF_NOCULL\n\nThis flag overrides the client frame building culling routines, causing an entity to always be sent regardless of where it is (ignoring PVS/PHS, essentially). Its only use in our code is to keep no-attenuation looping speakers in frame always.\n\n#### SVF_HULL\n\nThis flag adjusts the servers' method of clipping movement to entities. Normally, only `SOLID_BSP` entities will use their proper clipping bounds for collision, but if this is set on a `SOLID_TRIGGER` brush entity, traces will have to collide with the actual BSP tree of the trigger instead of solely touching its bounding box.\n\nThis is used in our game DLL to allow for certain triggers (like `trigger_gravity` or `trigger_flashlight`) to be activated when you are actually touching their brushes, allowing for angled triggers to finally exist.\n\n## entity_state_t\n\n### uint32_t number\n\nNumber was changed to `uint32_t` (from `int32_t`) to better represent its use and to only have to catch out of bounds in one direction.\n\n### int32_t skinnum\n\nSkinnum now packs a bit more data into it.\n\n```cpp\n// [Paril-KEX] player s.skinnum's encode additional data\nunion player_skinnum_t\n{\n    int32_t         skinnum;\n    struct {\n        uint8_t     client_num; // client index\n        uint8_t     vwep_index; // vwep index\n        int8_t      viewheight; // viewheight\n        uint8_t     team_index : 4; // team #; note that teams are 1-indexed here, with 0 meaning no team\n                                    // (spectators in CTF would be 0, for instance)\n        uint8_t     poi_icon : 4;   // poi icon; 0 default friendly, 1 dead, others unused\n    };\n};\n```\n\n### effects_t effects\n\nThe type `effects_t` was changed from `uint32_t` to `uint64_t` since we have way more effects to express.\n\n#### EF_BOB (bit 4)\n\nBit was unused in Quake II. This was repurposed into a weapon bobbing effect, similar to Quake III.\n\n#### EF_POWERSCREEN (bit 9)\n\nThis effect uses a different model that is scaled to the monster's size now.\n\n#### EF_DUALFIRE (bit 32)\n\nThis bit is used for a special effect, similar to `EF_QUAD`, but for Dualfire Damage.\n\n#### EF_HOLOGRAM (bit 33)\n\nThis bit is used for the N64 hologram effect; it adds a spinning ball of green particles around the object.\n\n#### EF_FLASHLIGHT (bit 34)\n\nThis bit marks a player entity as having a flashlight enabled. The effect itself is rendered separately by the client.\n\n#### EF_BARREL_EXPLODING (bit 35)\n\nThis effect is used before an explobox explodes; it emits steam particles from the barrel, as if it is experiencing a decompression event.\n\n#### EF_TELEPORTER2 (bit 36)\n\nThis effect is used for the teleporter FX in the N64.\n\n#### EF_GRENADE_LIGHT (bit 37)\n\nThis effect creates a small light on monster grenades, to make them slightly easier to track visually.\n\n### EF_FIREBALL (EF_ROCKET | EF_GIB)\n\nThis mutually-exclusive bit combo did nothing in the original game, since these special trails could only render one or the other. In the rerelease, it will render a fireball trail that begins yellow and large, tapering off into an orange trail, to mimick the effect on N64.\n\n### renderfx_t renderfx\n\n#### RF_NO_ORIGIN_LERP (bit 6)\n\nThis effect had a confusing name originally. Its name now reflects what it does: it disables origin interpolation.\n\n#### RF_BEAM (bit 7)\n\nYou can now create custom segmented beams by setting a non-one modelindex on beams.\n\n#### RF_CUSTOMSKIN (bit 8)\n\nThis effect was unused originally. It is now implemented and works as intended: specifying a `skinnum` will change the skin on the model to the skin specified in `CS_IMAGES + skinnum`. For `RF_FLARE`, `frame` must be used instead however, as `skinnum` is used for color data.\n\n#### RF_NOSHADOW (bit 13)\n\nThis effect was client-only originally.\n\n#### RF_CASTSHADOW (bit 14)\n\nThis effect marks an entity that casts light in the world; it is only used by `dynamic_light` (or dynamic `light` entities), and should not be used otherwise.\n\n#### RF_SHELL_LITE_GREEN (bit 19)\n\nThis is the equivalent shell color for `EF_DUALFIRE`.\n\n#### RF_CUSTOM_LIGHT (bit 20)\n\nThis flag creates a custom dynamic light at the position of the object. Its used in the N64 campaign, as it has custom light entities (`target_light`). `s.frame` is the light's radius, and `s.skinnum` is the light's current color (packed RGB).\n\n#### RF_FLARE (bit 21)\n\nThis flag marks an entity as being rendered with a flare instead of the usual entity rendering. Flares overload some fields:\n* `s.renderfx \u0026 RF_SHELL_RED` causes the flare to have an outer red rim.\n* `s.renderfx \u0026 RF_SHELL_GREEN` causes the flare to have an outer green rim.\n* `s.renderfx \u0026 RF_SHELL_BLUE` causes the flare to have an outer blue rim.\n* `s.renderfx \u0026 RF_FLARE_LOCK_ANGLE` causes the flare to not rotate towards the viewer.\n* `s.renderfx \u0026 RF_CUSTOMSKIN` causes the flare to use the custom image index in `s.frame`.\n* `s.modelindex2` is the start distance of fading the flare out.\n* `s.modelindex3` is the end distance of fading the flare out.\n* `s.skinnum` is the RGBA of the flare.\n\n#### RF_OLD_FRAME_LERP (bit 22)\n\nThis flag signals that `s.old_frame` should be used for the next frame and respected by the client. This can be used for custom frame interpolation; its use in this engine is specific to fixing interpolation bugs on certain monster animations.\n\n#### RF_DOT_SHADOW (bit 23)\n\nDraw a blob shadow underneath the entity.\n\n#### RF_LOW_PRIORITY (bit 24)\n\nThis flag marks an entity as low priority; if the renderer runs out of entity slots, these entities will be eligible for replacement. For instance, a monster is more important than a gib, so gibs are marked low priority so they can be replaced by a monster if the limit is reached.\n\n#### RF_NO_LOD (bit 25)\n\nThe original MD2 models will be used for LOD. Setting this bit prevents this behavior.\n\n#### RF_NO_STEREO (bit 2)\n\nThis is an overloaded flag that only applies to non-rendered entities that contain sounds. If set, stereo panning is disabled on this entity.\n\n#### RF_STAIR_STEP (bit 26)\n\nThe tick rate increase caused a bit of a visual bug with monsters and players: they now stepped up steps within 0.025 seconds instead of 0.1, causing jarring hitching. To fix this, entities set this flag when they detect they have stepped up a stair, and the client will interpolate their height difference over 0.1 seconds.\n\n#### RF_BEAM_LIGHTNING (RF_BEAM | RF_GLOW)\n\nThis mutually-exclusive bit combo causes a laser to become a lightning bolt, for N64 support.\n\n### uint32_t solid\n\nThis was changed from `int32_t` to `uint32_t`, and now packs more data into it to better represent bounding boxes to clients. \n\nFor backwards compatibility, 31 is still the magic value used for BSP entities. The actual packed data, however, is now as follows:\n\n```cpp\nunion solid_packed_t\n{\n    struct {\n        uint8_t x;\n        uint8_t y;\n        uint8_t zd; // always negative\n        uint8_t zu; // encoded as + 32\n    } p;\n\n    uint32_t u;\n};\n\n// packing:\n\npacked.p.x = ent-\u003emaxs[0];\npacked.p.y = ent-\u003emaxs[1];\npacked.p.zd = -ent-\u003emins[2];\npacked.p.zu = ent-\u003emaxs[2] + 32;\n\n// unpacking:\npacked.u = state-\u003esolid;\nent-\u003emins[0] = -packed.p.x;  ent-\u003emaxs[0] = packed.p.x;\nent-\u003emins[1] = -packed.p.y;  ent-\u003emaxs[1] = packed.p.y;\nent-\u003emins[2] = -packed.p.zd; ent-\u003emaxs[2] = packed.p.zu - 32;\n```\n\nThis is similar to Quake III Arena, and essentially allows any integral bbox to make it to the clients unchanged.\n\n### entity_event_t event\n\nTwo new events were added:\n\n#### EV_OTHER_FOOTSTEP (8)\n\nAllows non-players to send footsteps. They have idle attenuation, whereas regular footsteps have normal attenuation.\n\n#### EV_LADDER_STEP (9)\n\nLadder climbing 'footstep' event.\n\n### float alpha\n\nThis value allows you to specify exact transparency values for entities. For backwards compatibility, setting it to zero should be equivalent to an unchanged value, but any non-zero value should be respected as changed.\n\n### float scale\n\nThis value allows you to scale an entity by the given amount. For backwards compatibility, setting it to zero should be equivalent to an unchanged value, but any non-zero value should be respected as changed.\n\n### uint8_t instance_bits\n\nThis value is not meant to be set directly by the game code, but will have non-zero bits set for split-screen players that cannot see this entity.\n\n### float loop_volume / loop_attenuation\n\nLooping noises can now have volume and attenuation explicitly specified. For both, a value of zero indicates default/unchanged, for backwards compatibility. For `loop_attenuation`, a value of `-1` indicates full level audio (like `ATTN_NONE`).\n\n### int32_t owner\n\nAn entity's owner is now networked, allowing for it to ignore collision properly.\n\n### int32_t old_frame\n\nOnly sent when `renderfx \u0026 RF_OLD_FRAME_LERP` - indicates that this frame is the frame to lerp from.\n\n# Import/Exports\n\n## Game Import\n\n### (read-only) tick_rate / frame_time_s / frame_time_ms\n\nThis holds the server's tick variables. They will be set at the start of the server, before [PreInit](#preinit).\n`tick_rate` stores the tick rate, in hz.\n`frame_time_s` is the time a game frame will take in seconds.\n`frame_time_ms` is the time a game frame will take in ms.\n\nThese are provided pre-calculated for convenience.\n\n### Broadcast_Print\n\nThis function writes `message` with the print type of `printlevel` to all players. See [Print Adjustments](#print-adjustments). This is kept for compatibility purposes, [Loc_Print](#loc_print) replaces it.\n\n### Com_Print\n\nThis function writes `message` to the server. See [Print Adjustments](#print-adjustments).\n\n### Client_Print\n\nThis function writes out `message` with the print type of `printlevel` to the specified `ent` player. See [Print Adjustments](#print-adjustments). This is kept for compatibility purposes, [Loc_Print](#loc_print) replaces it.\n\n### Center_Print\n\nThis function writes `message` to the specified `ent` player in the center of their screen. See [Print Adjustments](#print-adjustments). This is kept for compatibility purposes, [Loc_Print](#loc_print) replaces it.\n\n### sound / positioned_sound\n\nThe `channel` enum has a single new flag:\n\n#### CHAN_FORCE_POS (bit 5)\n\nIf set (and an origin is **not** supplied), the entity's origin will be forced to be used as the origin point of the sound even if there is a better position available.\n\n### local_sound\n\nThis function was introduced to deal with some split-screen issues that popped up. It's designed to mimick `localsound` of QuakeC; it will directly send a sound packet to the specified player, using a `dupe_key` if supplied (see [unicast](#unicast)).\n\nSee [sound](#sound--positioned_sound) for info about the channels.\n\n### get_configstring\n\nThis function fetches a configstring from the servers' current configstring data.\n\n### Com_Error\n\nSee [Print Adjustments](#print-adjustments).\n\n### clip\n\nThis is a new function designed to fit a specific purpose: it will test if the box specified by `mins` \u0026 `maxs`, moved from `start` to `end`, will clip against the specified `entity` with the given `contentmask`. As an example, you could use this to detect if an entity is actually intersecting a brush in a trigger instead of just being within its bounding box.\n\n### inPVS / inPHS\n\nThis function now accepts a boolean, `portals`, which changes whether or not it should ignore areaportals.\n\n### BoxEdicts\n\nThis function was modified with a simple filtering callback, greatly extending its purpose and removing some limitations that would occur with previous uses. The filter callback is called for every entity discovered, and you can choose to include or skip entities that it finds, or even completely abort the search. In addition, you can now call the function with a 0 `maxcount`, and the function will still continue to filter and find entities, reporting the final count. To match the old behavior, if a non-zero `maxcount` is supplied, the return count will cap out at `maxcount`.\n\nNote that it is disallowed to modify world links (linkentity/unlinkentity, etc) in a filter callback, it can only be used for filtering.\n\n### unicast\n\nThe `dupe_key` parameter is new, and is to solve a very peculiar issue with split screen players. When unicast is used to spawn effects or sounds, it may not be desirable to replay the same effect on multiple split screens, since split screen players are all the same client and share views. For example, if you do a unicast for a `TE_BLASTER` somewhere in the world for every player, for a split screen client with 4 players, that effect will play 4 times - even though all four players are viewing the same world. The game DLL also has no knowledge or understanding of split screen, so there's no way for the game to work around it.\n\nInstead of having the game need to know this kind of implementation detail and prevent double-sending, for effects that are going to potentially be sent to multiple players that *may* be on a split screen, you can specify a dupe key value. This value, when non-zero, will be marked as \"already sent\" for that client, and won't be sent again for the next packet if it was already tripped. The game DLL provides the `GetUnicastKey` global which will give you a rolling value to directly pass into unicast or local_sound.\n\n### WriteFloat\n\nImplemented; this was stubbed out of the old code.\n\n### WritePosition\n\nNow sends full float positions.\n\n### WriteDir / WriteAngle\n\nUnchanged - WriteAngle is for compressed angles, when high precision is not necessary.\n\n### WriteEntity\n\nNew function to write an entity, to make it easier to write them without needing to WriteShort directly.\n\n### GetExtension\n\nSee [Extensions](#extensions).\n\n### Bot_RegisterEdict\n\nInforms the bot subsystem that an entity needs to be registered.\n\n### Bot_UnRegisterEdict\n\nInforms the bot subsystem that an entity needs to be unregistered.\n\n### Bot_MoveToPoint\n\nForces a bot to move to the specified point.\n\n### Bot_FollowActor\n\nForces a bot to follow the specified actor.\n\n### GetPathToGoal\n\nThe main pathfinding function; with the given pathfinding `request`, you'll be given `info` about the operation, the path, etc.\n\n### Loc_Print\n\nThe new primary entry point for printing. This function replaces all of the others (except Com_Print).\nFor basic usage, it can be called on an entity (or nullptr for broadcasting) with the correct `level`, with the message to send in `base`, and nullptr `args` along with 0 `num_args`. For actual localized messages, however, you can send additional arguments via the `args`/`num_args` parameters which are sent to the client for further processing.\n\nIn addition to localization, `level` now has new values and bit flags.\n\n#### PRINT_TYPEWRITER (4)\n\nCauses the message to be printed out one at a time, like a typewriter. Used for objectives, similar to the N64 version.\n\n#### PRINT_CENTER (5)\n\nAn instant centerprint, like the legacy centerprints.\n\n#### PRINT_TTS (6)\n\nIdentical to `PRINT_HIGH` in importance, but additionally causes text to speech narration to activate if enabled on the client.\n\n#### PRINT_BROADCAST (bit 3)\n\nMessage will be sent to all players.\n\n#### PRINT_NO_NOTIFY (bit 4)\n\nMessage will not be sent to the notify system.\n\n### Draw_Line / Draw_Point / Draw_Circle / Draw_Bounds / Draw_Sphere / Draw_OrientedWorldText / Draw_StaticWorldText / Draw_Cylinder / Draw_Ray\n\nThese functions are debugging aids that only render on the server.\n\n### ReportMatchDetails_Multicast\n\nThis function is solely for platforms that need match result data.\n\n### ServerFrame\n\nReturns the server's frame number.\n\n### SendToClipBoard\n\nCopy data to the server's clipboard, useful for debugging.\n\n### Info_ValueForKey / Info_RemoveKey / Info_SetValueForKey\n\nSee [Info Keys](#info-keys).\n\n## Save Games\n\nOne of the major changes to this release of Quake II is the save system. Instead of storing pointer offsets and copies of memory, the level \u0026 game data is written to UTF-8 JSON. This makes save data much easier to navigate for a human \u0026 developer that wants to look into a bug, while also being quick and efficient for storage.\n\nThe save system, as a result, no longer interfaces with the filesystem at all. Other mods are not required to use JSON, any text format will work as the server and client do not interact with the data.\n\n## Game Export\n\n### (read-only) apiversion\n\nThe version # reported by the server.\n\n### PreInit\n\nThis function is called before InitGame, and should be where you initialize your mod's latched cvars. This can be used to fix any conflicting latched cvars, which will be \"locked in\" after this is called.\n\n### SpawnEntities\n\nAll three parameters are now properly marked const.\n\n### WriteGameJson\n\nSee [Save Games](#save-games).\n\n### ReadGameJson\n\nSee [Save Games](#save-games).\n\n### WriteLevelJson\n\nThis function is now informed whether the level write is from a level transition or a manual save.\nSee [Save Games](#save-games).\n\n### ReadLevelJson\n\nSee [Save Games](#save-games).\n\n### CanSave\n\nThis new export now dictates whether the game is saveable or not.\n\n### ClientChooseSlot\n\nClientChooseSlot is intended to take in a bunch of information about the client that is connecting, and choose which `edict_t` entity this player should occupy. It is used in the rerelease to reorder players consistently throughout coop games, and ensure that everybody always gets the correct slot.\n\nCallers are given the player's `userinfo` and `social_id` (the social ID is a unique value per player on certain platforms), which you can use to find the correct slot from the current saved client data. You're also told whether the client `isBot`, which should always use non-saved available slots first. The `ignore` field will give you a list of slots up to `num_ignore` entities that are already occupied or were reported as such, so they can be safely skipped over. Finally, the `cinematic` parameters will tell you whether the loaded map is a video, which in most cases reordering will not be necessary.\n\n### ClientConnect\n\nThe function is now given the `social_id` and `isBot` state of the connecting client.\n\n### RunFrame\n\nThis function now receives a boolean to tell whether the call is from the main game loop, or from some other source (the game is settling, or running frames to advance level transitions). If the latter is occurring, you can use this boolean to speed up level transitions by skipping logic that is not necessary but is CPU-intensive, such as enemies searching for players to attack.\n\n### PrepFrame\n\nThis function used to be in the server, but is now controlled by the game DLL. It's ran after the game has execute a frame \u0026 has sent the packet data over to all players. Things like hit markers and one-shot events are cleared in here.\n\n### edict_size / num_edicts / max_edicts\n\nThese were changed to size_t and uint32_t/uint32_t respectively, to better represent their use.\n\n### server_flags\n\nThis is an integer shared between server and game, which stores bits for special states that the server cares about.\n\n### Pmove\n\nSee [Pmove](#pmove).\n\n### GetExtension\n\nSee [Extensions](#extensions).\n\n### Bot_SetWeapon\n\nCalled by the bot subsystem to switch weapons.\n\n### Bot_TriggerEdict\n\nCalled by the bot subsystem to trigger an entity.\n\n### Bot_UseItem\n\nCalled by the bot subsystem to use an item.\n\n### Bot_GetItemID\n\nFetch an item ID by a classname; for the bot subsystem.\n\n### Edict_ForceLookAtPoint\n\nForce the player to look at the given point - used for the nav editor.\n\n### Entity_IsVisibleToPlayer\n\nThis function is for item instancing; the rerelease of Quake II supports instanced items, which will display only for the players who haven't picked it up yet. For online players, this simply removes the item if you've gotten it, but for split screen players it will show a ghost where the entity was on players that have already picked it up.\n\n### GetShadowLightData\n\nThis function fetches data for the given shadow light for building client frames.\n\n## Player State\n\nIn the original client, player state was often accessed directly to perform various tasks or render things. Much of this has been moved into the cgame module to allow increased customization.\n\n## Client Game Import\n\n### (read-only) tick_rate\n### (read-only) frame_time_s\n### (read-only) frame_time_ms\n\nThis holds the server's tick variables. They will be set at the start of the client, before Init.\n`tick_rate` stores the tick rate, in hz.\n`frame_time_s` is the time a game frame will take in seconds.\n`frame_time_ms` is the time a game frame will take in ms.\n\nThese are provided pre-calculated for convenience.\n\n### Com_Print\n\nPrint a debug message to the client.\n\n### get_configstring\n\nFetch the given configstring data from the client.\n\n### Com_Error\n\nAbort error for client.\n\n### TagMalloc / TagFree / FreeTags\n\nSame as server.\n\n### cvar / cvar_set / cvar_forceset\n\nSame as server.\n\n### AddCommandString\n\nPush command(s) into the command buffer on the client side.\n\n### GetExtension\n\nSee [Extensions](#extensions).\n\n### CL_FrameValid\n\nReturns true if the current frame being rendered is valid.\n\n### CL_FrameTime\n\nReturns the current frame time delta.\n\n### CL_ClientTime\n\nReturns the client's current time (server-bound).\n\n### CL_ClientRealTime\n\nReturns the client's current real, unbound time.\n\n### CL_ServerFrame\n\nReturns the client's server frame.\n\n### CL_ServerProtocol\n\nReturns the client's connected server protocol.\n\n### CL_GetClientName\n\nReturns a UTF-8 string containing the givern player's name.\n\n### CL_GetClientPic\n\nReturns a string containing the given player's icon.\n\n### CL_GetClientDogtag\n\nReturns a string containing the given player's dogtag.\n\n### CL_GetKeyBinding\n\nReturns a key binding for the given key. Returns an empty string if the key is unbound.\n\n### Draw_RegisterPic\n\nPrecache the given image.\n\n### Draw_GetPicSize\n\nReturns the size of the given image.\n\n### SCR_DrawChar\n\nDraw the given conchars char at the specified position. A `shadow` parameter has been added to draw a drop shadow.\n\n### SCR_DrawPic\n\nDraw the given pic at the specified position.\n\n### SCR_DrawColorPic\n\nDraw the given pic at the specified location, with the specified color.\n\n### SCR_SetAltTypeface\n\nChange whether the alternate (accessibility) typeface is in use or not.\n\n### SCR_DrawFontString\n\nDraw a string to the screen, using the Kex KFONT which includes non-latin characters.\n\n### SCR_MeasureFontString\n\nMeasure the size of the string as it would be rendered.\n\n### SCR_FontLineHeight\n\nReturns the line height of the font.\n\n### CL_GetTextInput\n\nReturns a pointer to the current text input, and whether this input is for team say or not.\n\n### CL_GetWarnAmmoCount\n\nFor the given weapon ID, get the amount that is considered to be low ammo.\n\n### Localize\n\nLocalize the given string and arguments to an output buffer.\n\n### SCR_DrawBind\n\nDraw a user bind to the screen, returns the Y offset from rendering.\n\n### CL_InAutoDemoLoop\n\nReturns true if the engine is running the attract demo loop.\n\n## Client Game Export\n\n### (read-only) apiversion\n\nAPI version.\n\n### Init / Shutdown\n\nLifecycle functions for the client game. Note that the cgame does not control UI, so the cgame only exists when you are connected and in-game.\n\n### DrawHUD\n\nThis function is called by the client when their HUD needs to be rendered.\n- `isplit` contains the split screen index of the player.\n- `data` contains a pointer to some transient information from the server. This includes currently active layout, and the player's active inventory when the inventory is open.\n- `hud_vrect` contains the unpadded rectangle of the HUD being rendered.\n- `hud_safe` contains the size of the safe area. Only x and y are set, w and h are unused.\n- `scale` is the integral scale of the HUD being rendered.\n- `playernum` is the player's client index.\n- `ps` is a pointer to the player's current player state.\n\n### TouchPics\n\nFunction called for precaching images used by the HUD.\n\n### LayoutFlags\n\nFor the given player state, return the `layout_flags_t` that would match it.\n\n### GetActiveWeaponWheelWeapon / GetOwnedWeaponWheelWeapons / GetWeaponWheelAmmoCount / GetPowerupWheelCount\n\nThe weapon wheel is in the client, but uses these callbacks to fetch data from `player_state_t`.\n\n### GetHitMarkerDamage\n\nReturns how much damage was done for this player.\n\n### Pmove\n\nSee [Pmove](#pmove).\n\n### ParseConfigString\n\nWhen a configstring is received, the cgame is also notified of changes. The cgame module can react to configstring updates here.\n\n### ParseCenterPrint\n\nWhen a centerprint-like message is received by the client, it is sent to the cgame via this function.\n- `isplit` is the split screen player it was sent to.\n- `instant` is true if the message is a centerprint that is drawn without the typewriter effect.\n\n### ClearNotify\n\nThe client will call this when the notification area should be cleared.\n\n### ClearCenterprint\n\nThe client will call this when centerprints should be cleared.\n\n### NotifyMessage\n\nWhen a notify message is received, the client will send it to this function.\n- `isplit` is the split screen player it was sent to.\n- `is_chat` is true if it was a chat-like message.\n\n### GetMonsterFlashOffset\n\nTo simplify the server to client muzzleflash communication, the cgame now exports muzzleflash origins via this function.\n\n### GetExtension\n\nSee [Extensions](#extensions).\n\n# Quake II server protocol - version 2023\n\nThe Quake II rerelease features an updated server protocol. Most of the messages are backwards compatible, but some needed adjustments to work with new or changed features, or raised limits.\n\nThis document will only outline the changes since the original release, rather than the whole protocol.\n\n## (out of band, client \u003c-\u003e server) challenges\n\nThe out of band challenges have been removed.\n\n## (out of band, client -\u003e server) connect\n\nThe `connect` message is similar to the original, but has redundant information removed. Port and challenge are handled at a lower level, so that information is not included. The `connect` message is in the following format:\n\n`connect {protocol} {num split} {socials...} {userinfo...}`\n\n- `protocol` is 2023\n- `num split` is the number of split screen players\n- `socials` is `num split` number of arguments containing each players' social identifiers\n- `userinfo` is the clients' userinfo string, split up by groups of 510 characters each (since command arguments have a maximum length). This can often span 2 or more arguments, since each userinfo var has a value per player. See [Info Keys](#info-keys).\n\n## (out of band, server -\u003e client) client_connect\n\nThis message is sent when the server accepts the connection. It is in the following format:\n\n`client_connect {protocol}`\n\n- `protocol` is 2023\n\nThe protocol version is sent mainly for backward compatibility with demos.\n\n## (packet, server -\u003e client) svc_muzzleflash (1)\n\nThe following enum values are now accepted.\n\n### MZ_BFG2 (19)\n\nSecondary muzzleflash for the BFG, sent when the BFG actually fires.\n\n### MZ_PHALANX2 (20)\n\nSecondary muzzleflash for the Phalanx, sent for the second projectile.\n\n### MZ_PROX (31)\n\nSent when the Prox Launcher is fired.\n\n### MZ_ETF_RIFLE_2 (32)\n\nSent when the other barrel of the ETF Rifle is fired.\n\n## (packet, server -\u003e client) svc_muzzleflash2 (2)\n\n### MZ2_BOSS2_MACHINEGUN_L2 / MZ2_BOSS2_MACHINEGUN_R2 (74 / 134)\n\nThese two values were just copies of L1/R1, but were repurposed to make Hyperblaster-specific sounds for the new Hornet.\n\nThe following enum values are now accepted.\n\n### MZ2_SOLDIER_RIPPER_1 - MZ2_SOLDIER_HYPERGUN_8 (211 - 226)\n\nMuzzleflashes for the ripper \u0026 blue hyperblaster guards.\n\n### MZ2_GUARDIAN_BLASTER - MZ2_ARACHNID_RAIL_UP2 (227 - 231)\n\nMuzzleflashes for the PSX monsters.\n\n### MZ2_INFANTRY_MACHINEGUN_14 - MZ2_INFANTRY_MACHINEGUN_21 (232 - 239)\n\nMuzzleflashes for the Infantry's run-attack animation.\n\n### MZ2_GUNCMDR_CHAINGUN_1 - MZ2_GUNCMDR_GRENADE_CROUCH_3 (240 - 250)\n\nMuzzleflashes for the Gunner Commander.\n\n### MZ2_SOLDIER_BLASTER_9 - MZ2_SOLDIER_HYPERGUN_9 (251 - 255)\n\nMuzzleflashes for the guards' new prone-firing animation.\n\n## (packet, server -\u003e client) svc_muzzleflash3 (32)\n\nThis packet was necessitated from running out of bits in svc_muzzleflash2. The only difference is the byte for `id` is a ushort.\n\n### MZ2_GUNNER_GRENADE2_1 - MZ2_GUNNER_GRENADE2_4 (256 - 259)\n\nAlternate firing animation for the Gunner's grenade launcher.\n\n### MZ2_INFANTRY_MACHINEGUN_22 (260)\n\nAlternate firing animation for the Infantry.\n\n### MZ2_SUPERTANK_GRENADE_1 (261 - 262)\n\nSupertank's grenade launcher.\n\n### MZ2_HOVER_BLASTER_2 / MZ2_DAEDALUS_BLASTER_2 (263 / 264)\n\nThe Icarus and Daedalus' opposite side blaster.\n\n### MZ2_MEDIC_HYPERBLASTER1_1 - MZ2_MEDIC_HYPERBLASTER1_12 / MZ2_MEDIC_HYPERBLASTER2_1 - MZ2_MEDIC_HYPERBLASTER2_12 (265 - 276 / 277 - 288)\n\nThe Medic and Medic Commander's Hyperblaster firing animation sweep.\n\n## (packet, server -\u003e client) svc_temp_entity (3)\n\nAs documented in [WritePosition](#writeposition), WritePos now writes full float precision, so ReadPos has to read full float.\n\n### TE_SPLASH (10)\n\nThe \"color/splash\" enumeration accepts a new value:\n\n#### SPLASH_ELECTRIC (7)\n\nA spark used exclusively in N64, which spawns blue/white particles and makes sparking noises.\n\nThe following new enum values are accepted:\n\n### TE_RAILTRAIL2 (31)\n\nThis effect was unused in Quake II, and was retooled to a lighter railgun effect used for Instagib mode.\n\n### TE_BLUEHYPERBLASTER (56)\n\"Correct\" version of the old buggy `TE_BLUEHYPERBLASTER`, which is now `TE_BLUEHYPERBLASTER_DUMMY`.\n- ReadPos\n- ReadDir\n\n### TE_BFG_ZAP (57)\nLaser when an entity has been zapped by a BFG explosion.\n- ReadPos (start)\n- ReadPos (end)\n\n### TE_BERSERK_SLAM (58)\nLarge blue flash \u0026 particles at impact point towards a direction.\n- ReadPos\n- ReadDir\n\n### TE_GRAPPLE_CABLE_2 (59)\nThe grappling hook in Quake II 3.20 used a larger message that didn't allow the cable to render like other player-derived beams.\n- ReadEntity\n- ReadPos (start)\n- ReadPos (end)\n\n### TE_POWER_SPLASH (60)\nEffect sent when a power shield evaporates.\n- ReadEntity\n- ReadByte (1 for screen, 0 for armor)\n\n### TE_LIGHTNING_BEAM (61)\nA lightning bolt that originates from the player, like the heat beam. Unused.\n- ReadEntity\n- ReadPos (start)\n- ReadPos (end)\n\n### TE_EXPLOSION1_NL / TE_EXPLOSION2_NL (62 / 63)\nVariants of explosion that don't include any dynamic light.\n- ReadPos\n\n## (packet, server -\u003e client) svc_sound (9)\n\nSince `MAX_EDICTS` is now 8192, this packet required changes to support higher entity numbers. `MAX_SOUNDS` being increased to 1024 also necessitated the sound index changing from byte to ushort.\n\n- ReadByte (flags)\n- ReadShort (soundindex)\n- [if flags \u0026 SND_VOLUME] ReadByte (volume)\n- [if flags \u0026 SND_ATTENUATION] ReadByte (attenuation)\n- [if flags \u0026 SND_OFFSET] ReadByte (offset)\n- [if flags \u0026 SND_ENT]\n  - [if flags \u0026 SND_LARGE_ENT] ReadLong (entchan)\n  - [if !(flags \u0026 SND_LARGE_ENT)] ReadShort (entchan)\n- [if flags \u0026 SND_POS] ReadPos (origin)\n\n`entchan` is encoded as such:\n```\nstruct sndchan_t\n{\n\tuint8_t\t\tchannel : 3;\n\tuint32_t\tentity : 29;\n}\n```\n\n`flags` contains the following bits:\n- SND_VOLUME (bit 0)\n- SND_ATTENUATION (bit 1)\n- SND_POS (bit 2)\n- SND_ENT (bit 3)\n- SND_OFFSET (bit 4)\n- SND_EXPLICIT_POS (bit 5)\n- SND_LARGE_ENT (bit 6)\n\nNote that `SND_POS` is **always** set. This is to fix a legacy bug where sounds played on entities outside of your PVS will play at the origin instead of their real location. The client should pick the real position if the entity is in their frame, but otherwise fall back to the sound packets' position.\n\n## (packet, server -\u003e client) svc_print (10)\n\nThis packet now supports `PRINT_TYPEWRITER` and `PRINT_CENTER` values. See [Loc_Print](#loc_print).\n\n## (packet, server -\u003e client) svc_stufftext (11)\n\nFor security reasons, this packet will only allow commands things to be executed.\n\n## (packet, server -\u003e client) svc_serverdata (12)\n\n- ReadLong (protocol)\n- ReadLong (spawncount)\n- ReadByte (0 = game, 1 = demo, 2 = server demo)\n- ReadByte (tickrate)\n- ReadString (gamedir)\n- ReadShort[N] (playernums; see below)\n- ReadString (level name)\n\nTo parse `playernums`, read the first short and check its value. If it is -2, then read an additional short, which is the number of split screen entities to follow. Read that number of shorts to get each entity number for each split screen player. Otherwise, the value returned by the initial ReadShort is the playernum of the client.\n\nThe special value -1 will be used in cinematics, to indicate that the player has no entity.\n\n## (packet, server -\u003e client) svc_frame (20)\n\n- ReadLong (serverframe)\n- ReadLong (deltaframe)\n- ReadByte (surpressCount)\n\nFor each player in this client's `numSplit` the following data is parsed:\n\n- ReadByte (areabits length)\n- ReadData (using above byte)\n- ReadByte (value will be `svc_playerinfo`)\n- ParsePlayerState (see [svc_playerinfo](#svc_playerinfo-17))\n\nThen, back to `svc_frame` data:\n\n- client entity `event`s should all be cleared back to `EV_NONE`\n- ReadByte (value will be `svc_packetentities`)\n- ParsePacketEntities (see [svc_packetentities](#svc_packetentities-18))\n\n### svc_playerinfo (17)\n\n#### Bits\n\n```c\n#define PS_M_TYPE           (1\u003c\u003c0)\n#define PS_M_ORIGIN         (1\u003c\u003c1)\n#define PS_M_VELOCITY       (1\u003c\u003c2)\n#define PS_M_TIME           (1\u003c\u003c3)\n#define PS_M_FLAGS          (1\u003c\u003c4)\n#define PS_M_GRAVITY        (1\u003c\u003c5)\n#define PS_M_DELTA_ANGLES   (1\u003c\u003c6)\n\n#define PS_VIEWOFFSET       (1\u003c\u003c7)\n#define PS_VIEWANGLES       (1\u003c\u003c8)\n#define PS_KICKANGLES       (1\u003c\u003c9)\n#define PS_BLEND            (1\u003c\u003c10)\n#define PS_FOV              (1\u003c\u003c11)\n#define PS_WEAPONINDEX      (1\u003c\u003c12)\n#define PS_WEAPONFRAME      (1\u003c\u003c13)\n#define PS_RDFLAGS          (1\u003c\u003c14)\n\n#define PS_MOREBITS         (1\u003c\u003c15)\n\n// [Paril-KEX]\n#define PS_DAMAGE_BLEND     (1\u003c\u003c16)\n#define PS_TEAM_ID          (1\u003c\u003c17)\n```\n\n#### Data\n\n- ReadUShort (flags)\n- [if flags \u0026 PS_MOREBITS] flags |= ReadUShort \\\u003c\\\u003c 16\n- [if flags \u0026 PS_M_TYPE] ReadByte (pm_type)\n- [if flags \u0026 PS_M_ORIGIN] ReadPos (pm_origin)\n- [if flags \u0026 PS_M_VELOCITY] ReadPos (pm_velocity)\n- [if flags \u0026 PS_M_TIME] ReadUShort (pm_time)\n- [if flags \u0026 PS_M_FLAGS] ReadUShort (pm_flags)\n- [if flags \u0026 PS_M_GRAVITY] ReadShort (pm_gravity)\n- [if flags \u0026 PS_M_DELTA_ANGLES] ReadPos (pm_delta_angles)\n- [if flags \u0026 PS_VIEWOFFSET]:\n```cpp\n\tviewoffset_x = ReadShort() * (1.f / 16.f)\n\tviewoffset_y = ReadShort() * (1.f / 16.f)\n\tviewoffset_z = ReadShort() * (1.f / 16.f)\n\tviewheight = ReadChar() // note: not in protocol 2022\n```\n- [if flags \u0026 PS_VIEWANGLES] ReadPos (viewangles)\n- [if flags \u0026 PS_KICKANGLES]\n```cpp\nkick_angles_x = ReadShort() / 1024.f\nkick_angles_y = ReadShort() / 1024.f\nkick_angles_z = ReadShort() / 1024.f\n```\n- [if flags \u0026 PS_WEAPONINDEX]\n```cpp\ngunindex_temp = ReadUShort()\ngunskin = (gunindex_temp \u0026 0xE000) \u003e\u003e 13\ngunindex = gunindex_temp \u0026 ~0xE000\n```\n- [if flags \u0026 PS_WEAPONFRAME]\n```cpp\n#define GUNBIT_OFFSET_X (1\u003c\u003c0)\n#define GUNBIT_OFFSET_Y (1\u003c\u003c1)\n#define GUNBIT_OFFSET_Z (1\u003c\u003c2)\n#define GUNBIT_ANGLES_X (1\u003c\u003c3)\n#define GUNBIT_ANGLES_Y (1\u003c\u003c4)\n#define GUNBIT_ANGLES_Z (1\u003c\u003c5)\n#define GUNBIT_GUNRATE (1\u003c\u003c6)\n```\n```cpp\ngunframe_temp = ReadUShort()\ngun_bits = (gunframe_temp \u0026 0xFE00) \u003e\u003e 9\ngunframe = (gunframe_temp \u0026 ~0xFE00)\n\n[if gun_bits \u0026 GUNBIT_OFFSET_X] gunoffset_x = ReadFloat()\n[if gun_bits \u0026 GUNBIT_OFFSET_Y] gunoffset_y = ReadFloat()\n[if gun_bits \u0026 GUNBIT_OFFSET_Z] gunoffset_z = ReadFloat()\n[if gun_bits \u0026 GUNBIT_ANGLES_X] gunangles_x = ReadFloat()\n[if gun_bits \u0026 GUNBIT_ANGLES_Y] gunangles_y = ReadFloat()\n[if gun_bits \u0026 GUNBIT_ANGLES_Z] gunangles_z = ReadFloat()\n[if gun_bits \u0026 GUNBIT_GUNRATE] gunrate = ReadByte()\n```\n- [if flags \u0026 PS_BLEND]\n```cpp\nscreen_blend_r = ReadByte() / 255.f\nscreen_blend_g = ReadByte() / 255.f\nscreen_blend_b = ReadByte() / 255.f\nscreen_blend_a = ReadByte() / 255.f\n```\n- [if flags \u0026 PS_FOV] ReadByte(fov)\n- [if flags \u0026 PS_RDFLAGS] ReadByte(rdflags)\n- ReadLong(statbits)\n```cpp\nfor (i = 0; i \u003c 32; i++)\n\tif (statbits \u0026 (1 \u003c\u003c i))\n\t\tReadShort(stats[i])\n```\n- ReadLong(morestatbits)\n```cpp\nfor (i = 32; i \u003c 64; i++)\n\tif (morestatbits \u0026 (1 \u003c\u003c (i - 32)))\n\t\tReadShort(stats[i])\n```\n- [if flags \u0026 PS_DAMAGE_BLEND]\n```cpp\ndamage_blend_r = ReadByte() / 255.f\ndamage_blend_g = ReadByte() / 255.f\ndamage_blend_b = ReadByte() / 255.f\ndamage_blend_a = ReadByte() / 255.f\n```\n- [if flags \u0026 PS_TEAM_ID]\n```cpp\nteam_id = ReadByte()\n```\n\n### svc_packetentities (18)\n\n#### Bits\n```c\n\n// try to pack the common update flags into the first byte\n#define U_ORIGIN1   (1\u003c\u003c0)\n#define U_ORIGIN2   (1\u003c\u003c1)\n#define U_ANGLE2    (1\u003c\u003c2)\n#define U_ANGLE3    (1\u003c\u003c3)\n#define U_FRAME8    (1\u003c\u003c4)      // frame is a byte\n#define U_EVENT     (1\u003c\u003c5)\n#define U_REMOVE    (1\u003c\u003c6)      // REMOVE this entity, don't add it\n#define U_MOREBITS1 (1\u003c\u003c7)      // read one additional byte\n\n// second byte\n#define U_NUMBER16  (1\u003c\u003c8)      // NUMBER8 is implicit if not set\n#define U_ORIGIN3   (1\u003c\u003c9)\n#define U_ANGLE1    (1\u003c\u003c10)\n#define U_MODEL     (1\u003c\u003c11)\n#define U_RENDERFX8 (1\u003c\u003c12)     // fullbright, etc\n#define U_EFFECTS8  (1\u003c\u003c14)     // autorotate, trails, etc\n#define U_MOREBITS2 (1\u003c\u003c15)     // read one additional byte\n\n// third byte\n#define U_SKIN8     (1\u003c\u003c16)\n#define U_FRAME16   (1\u003c\u003c17)     // frame is a short\n#define U_RENDERFX16 (1\u003c\u003c18)    // 8 + 16 = 32\n#define U_EFFECTS16 (1\u003c\u003c19)     // 8 + 16 = 32\n#define U_MODEL2    (1\u003c\u003c20)     // weapons, flags, etc\n#define U_MODEL3    (1\u003c\u003c21)\n#define U_MODEL4    (1\u003c\u003c22)\n#define U_MOREBITS3 (1\u003c\u003c23)     // read one additional byte\n\n// fourth byte\n#define U_OLDORIGIN (1\u003c\u003c24)     // FIXME: get rid of this\n#define U_SKIN16    (1\u003c\u003c25)\n#define U_SOUND     (1\u003c\u003c26)\n#define U_SOLID     (1\u003c\u003c27)\n#define U_MODEL16   (1\u003c\u003c28)\n#define U_EFFECTS64 (1\u003c\u003c29) // [Edward-KEX]\n#define U_ALPHA     (1\u003c\u003c30) // [Paril-KEX]\n#define U_MOREBITS4 (1\u003c\u003c31) // [Paril-KEX] read one additional byte\n#define U_SCALE     (1ull\u003c\u003c32ull) // [Paril-KEX]\n#define U_INSTANCE  (1ull\u003c\u003c33ull) // [Paril-KEX]\n#define U_OWNER     (1ull\u003c\u003c34ull) // [Paril-KEX]\n#define U_OLDFRAME  (1ull\u003c\u003c35ull) // [Paril-KEX]\n```\n\n#### Data\n\nThe regular process for deltaing entities has not changed, but the data bits have.\n\nParseEntityBits:\n- ReadByte(bits)\n- [if bits \u0026 U_MOREBITS1] bits |= ReadByte() \\\u003c\\\u003c 8\n- [if bits \u0026 U_MOREBITS2] bits |= ReadByte() \\\u003c\\\u003c 16\n- [if bits \u0026 U_MOREBITS3] bits |= ReadByte() \\\u003c\\\u003c 24\n- [if bits \u0026 U_MOREBITS4] bits |= ReadByte() \\\u003c\\\u003c 32\n- [if bits \u0026 U_NUMBER16] ReadShort(number) [else] ReadByte(number)\n\nParseDelta:\n- [if bits \u0026 U_MODEL16]\n```cpp\nReadShort(modelindex)\nReadShort(modelindex2)\nReadShort(modelindex3)\nReadShort(modelindex4)\n```\n- [else]\n```cpp\nReadByte(modelindex)\nReadByte(modelindex2)\nReadByte(modelindex3)\nReadByte(modelindex4)\n```\n- [if bits \u0026 U_FRAME8] ReadByte(frame)\n- [if bits \u0026 U_FRAME16] ReadShort(frame)\n- [if bits \u0026 (U_SKIN8 | U_SKIN16) == (U_SKIN8 | U_SKIN16)] ReadLong(skinnum)\n- [elseif bits \u0026 U_SKIN8] ReadByte(skinnum)\n- [elseif bits \u0026 U_SKIN16] ReadUShort(skinnum)\n- [if bits \u0026 (U_EFFECTS8 | U_EFFECTS16 | U_EFFECTS64)]\n```cpp\n// if 64-bit effects are sent, the low bits are sent first\n// and the high bits come after.\n[if bits \u0026 U_EFFECTS64] ReadULong(loeffects)\n\n[if bits \u0026 (U_EFFECTS8 | U_EFFECTS16) == (U_EFFECTS8 | U_EFFECTS16)] ReadULong(effects)\n[elseif bits \u0026 U_EFFECTS16] ReadUShort(effects)\n[else] ReadByte(effects)\n\n[if bits \u0026 U_EFFECTS64] effects = (effects \u003c\u003c 32) | loeffects \n```\n- [if bits \u0026 (U_RENDERFX8 | U_RENDERFX16) == (U_RENDERFX8 | U_RENDERFX16)] ReadLong(effects)\n- [elseif bits \u0026 renderfx] ReadByte(renderfx)\n- [elseif bits \u0026 U_RENDERFX16] ReadShort(renderfx)\n- [if bits \u0026 U_SOLID] ReadULong(solid)\n```cpp\n// note: for the protocol in the demos (2022), if `solid` is zero,\n// then the following reads are lower precision, using ReadShort() * (1.f / 8.f)\n[if bits \u0026 U_ORIGIN1] ReadFloat(origin_x)\n[if bits \u0026 U_ORIGIN2] ReadFloat(origin_y)\n[if bits \u0026 U_ORIGIN3] ReadFloat(origin_z)\n[if bits \u0026 U_OLDORIGIN] ReadPos(oldorigin)\n```\n- [if bits \u0026 U_ANGLE1] ReadFloat(angle_x)\n- [if bits \u0026 U_ANGLE2] ReadFloat(angle_y)\n- [if bits \u0026 U_ANGLE3] ReadFloat(angle_z)\n- [if bits \u0026 U_SOUND] ReadUShort(temp_sound)\n```cpp\nbool has_volume = temp_sound \u0026 0x4000\nbool has_attenuation = temp_sound \u0026 0x8000;\n\n// the sound index takes up 14 bits\nsound = temp_sound \u0026 ~(0x4000 | 0x8000)\n\n[if has_volume] loop_volume = ReadByte() / 255.f\n[else] loop_volume = 1.f\n\n[if has_attn] loop_attenuation = ReadByte()\n[else] loop_attenuation = ATTN_STATIC\n```\n- [if bits \u0026 U_EVENT] ReadByte(event) [else] event = 0\n- [if bits \u0026 U_ALPHA] alpha = ReadByte() / 255.f\n- [if bits \u0026 U_SCALE] scale = ReadByte() / 16.f\n- [if bits \u0026 U_INSTANCE] ReadByte(instance_bits)\n- [if bits \u0026 U_OWNER] ReadShort(owner)\n- [if bits \u0026 U_OLDFRAME] ReadUShort(old_frame)\n\n## (packet, server -\u003e client) svc_splitclient (21)\n\nThis packet indicates to the client which split screen player the next messages are directed towards, for unicast messages.\n\n- ReadByte (isplit)\n\nNote that `isplit` will be offset by 1 (that is to say, a value of 1 indicates split screen client 0).\n\n## (packet, server -\u003e client) svc_configblast (22)\n\nCompressed configstring data. This is to make connection faster by sending fewer packets.\n\n- ReadShort (compressed size)\n- ReadShort (uncompressed size)\n- ReadByte[compressed size] (buffer)\n\nThe received `buffer` is directly passed through to zlib's `uncompress`. After decompression, until the buffer is exhausted, the following data repeats:\n- ReadUShort (index)\n- ReadString (str) \n\n## (packet, server -\u003e client) svc_spawnbaselineblast (23)\n\nCompressed baseline data. This is to make connection faster by sending fewer packets.\n\n- ReadShort (compressed size)\n- ReadShort (uncompressed size)\n- ReadByte[compressed size] (buffer)\n\nThe received `buffer` is directly passed through to zlib's `uncompress`. After decompression, until the buffer is exhausted, read in the data contained in a `svc_spawnbaseline` packet.\n\n## (packet, server -\u003e client) svc_level_restart (24)\n\nSent when the server executes a `restart_level` command. The client should be prepared to do a \"soft wipe\" of their state, but might want to defer it until the full frame is read since effects might come in after this command is executed.\n\nThis message's data contains configstrings that were changed by restarting the level. The following should be repeated until an exit condition is met:\n\n- ReadShort (id)\n- [if id is -1, exit]\n- ReadString (str)\n\n## (packet, server -\u003e client) svc_damage (25)\n\nThis message is sent after accumulating damage on a player. It gives the player a rough idea of the damage they're receiving and from where.\n\n- ReadByte (count)\n\nFor `count` number of loops, read the following:\n\n- ReadByte (encoded)\n- ReadDir\n\n`encoded` is in the following format:\n```\nstruct packed_damage_t\n{\n\tuint8_t damage : 5;\n\tuint8_t health : 1;\n\tuint8_t armor : 1;\n\tuint8_t shield : 1;\n}\n```\n\n`health` provides a `1,0,0` addition to color.\n`armor` provides a `1,1,1` addition to color.\n`shield` provides a `0,1,0` addition to color.\n\nThe `damage` value is also divided by 3, so multiplying it by 3 will get you an approximation of the real damage amount.\n\nThe color is then normalized.\n\n## (packet, server -\u003e client) svc_locprint (26)\n\nThis packet is the new entry point for prints.\n\n- ReadByte (flags)\n- ReadString (base)\n- ReadByte (num args)\n- ReadString[num args] (args)\n\nThe `base` string is a `fmtlib` formatted string.\n\nThe information in [Print Adjustments](#print-adjustments) and [Loc_Print](#loc_print) explains how formatting works.\n\n## (packet, server -\u003e client) svc_fog (27)\n\n```cpp\nenum bits_t : uint16_t\n{\n\t// global fog\n\tBIT_DENSITY     = bit_v\u003c0\u003e,\n\tBIT_R           = bit_v\u003c1\u003e,\n\tBIT_G           = bit_v\u003c2\u003e,\n\tBIT_B           = bit_v\u003c3\u003e,\n\tBIT_TIME        = bit_v\u003c4\u003e, // if set, the transition takes place over N milliseconds\n\n\t// height fog\n\tBIT_HEIGHTFOG_FALLOFF   = bit_v\u003c5\u003e,\n\tBIT_HEIGHTFOG_DENSITY   = bit_v\u003c6\u003e,\n\tBIT_MORE_BITS           = bit_v\u003c7\u003e, // read additional bit\n\tBIT_HEIGHTFOG_START_R   = bit_v\u003c8\u003e,\n\tBIT_HEIGHTFOG_START_G   = bit_v\u003c9\u003e,\n\tBIT_HEIGHTFOG_START_B   = bit_v\u003c10\u003e,\n\tBIT_HEIGHTFOG_START_DIST= bit_v\u003c11\u003e,\n\tBIT_HEIGHTFOG_END_R     = bit_v\u003c12\u003e,\n\tBIT_HEIGHTFOG_END_G     = bit_v\u003c13\u003e,\n\tBIT_HEIGHTFOG_END_B     = bit_v\u003c14\u003e,\n\tBIT_HEIGHTFOG_END_DIST  = bit_v\u003c15\u003e\n};\n```\n\n- ReadByte (bits)\n- [if bits \u0026 BIT_MORE_BITS] ReadByte (morebits), bits |= (morebits \\\u003c\\\u003c 8)\n- [if bits \u0026 BIT_DENSITY] ReadFloat (density)\n- [if bits \u0026 BIT_DENSITY] ReadByte (skyfactor)\n- [if bits \u0026 BIT_R] ReadByte (red)\n- [if bits \u0026 BIT_G] ReadByte (green)\n- [if bits \u0026 BIT_B] ReadByte (blue)\n- [if bits \u0026 BIT_TIME] ReadUShort (time)\n- [if bits \u0026 BIT_HEIGHTFOG_FALLOFF] ReadFloat (heightfog falloff)\n- [if bits \u0026 BIT_HEIGHTFOG_DENSITY] ReadFloat (heightfog density)\n- [if bits \u0026 BIT_HEIGHTFOG_START_R] ReadByte (heightfog start red)\n- [if bits \u0026 BIT_HEIGHTFOG_START_G] ReadByte (heightfog start green)\n- [if bits \u0026 BIT_HEIGHTFOG_START_B] ReadByte (heightfog start blue)\n- [if bits \u0026 BIT_HEIGHTFOG_START_DIST] ReadLong (heightfog start distance)\n- [if bits \u0026 BIT_HEIGHTFOG_END_R] ReadByte (heightfog end red)\n- [if bits \u0026 BIT_HEIGHTFOG_END_G] ReadByte (heightfog end green)\n- [if bits \u0026 BIT_HEIGHTFOG_END_B] ReadByte (heightfog end blue)\n- [if bits \u0026 BIT_HEIGHTFOG_END_DIST] ReadLong (heightfog end distance)\n\n## (packet, server -\u003e client) svc_waitingforplayers (28)\n\nSent when there are players waiting to join before the game can start (or zero if all players are in).\n\n- ReadByte (count)\n\n## (packet, server -\u003e client) svc_bot_chat (29)\n\nBots talking to players.\n\n- ReadString (bot name)\n- ReadShort (client index, or 256 if no particular player)\n- ReadString (loc string)\n\n## (packet, server -\u003e client) svc_poi (30)\n\nSpawn a POI.\n\n```cpp\nenum svc_poi_flags\n{\n    POI_FLAG_NONE = 0,\n    POI_FLAG_HIDE_ON_AIM = 1, // hide the POI if we get close to it with our aim\n};\n```\n\n- ReadUShort (key)\n- ReadUShort (time)\n- ReadPos (pos)\n- ReadUShort (image index)\n- ReadByte (palette index)\n- ReadByte (flags)\n\nIf a non-zero `key` is specified, only one of that POI key can exist at any given time. If `time` is 0xFFFF, the POI that matches the key will be removed.\n\nIf `time` is zero, the POI will last forever, `key` should be set in order to allow the POI to be cleaned up later. \n\n## (packet, server -\u003e client) svc_help_path (31)\n\nSpawns the Compass help path effect at the given location.\n\n- ReadByte (start)\n- ReadPos (pos)\n- ReadDir (dir)\n\n## (packet, server -\u003e client) svc_achievement (32)\n\n- ReadString (id)\n\n## (packet, client -\u003e server) clc_stringcmd (4)\n\n- ReadByte (isplit)\n- ReadString (s)\n\nNote that `isplit` is offset by 1, so `1` is the first split screen client.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparil%2Fq2horde","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fparil%2Fq2horde","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fparil%2Fq2horde/lists"}