diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e9f100cc..d76a2c63f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,7 +14,7 @@ on: jobs: windows: name: 'Windows' - runs-on: windows-latest + runs-on: windows-2019 env: solution: 'msvc/ReHLDS.sln' @@ -25,12 +25,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup MSBuild - uses: microsoft/setup-msbuild@v1.0.2 + uses: microsoft/setup-msbuild@v1.1.3 - name: Build and Run unittests run: | @@ -70,7 +70,7 @@ jobs: move msvc\${{ env.buildRelease }}\director.pdb publish\debug\director.pdb - name: Deploy artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.1 with: name: win32 path: publish/* @@ -92,7 +92,7 @@ jobs: steps: - name: Deploying windows artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: win32 @@ -155,7 +155,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: fetch-depth: 0 @@ -228,7 +228,7 @@ jobs: shell: bash - name: Deploy artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v3.1.1 id: upload-job with: name: linux32 @@ -247,12 +247,12 @@ jobs: steps: - name: Deploying linux artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: linux32 - name: Deploying windows artifacts - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: win32 @@ -277,7 +277,7 @@ jobs: github.event.action == 'published' && startsWith(github.ref, 'refs/tags/') run: | - 7z a -tzip rehlds-bin-${{ env.APP_VERSION }}.zip bin/linux32/ hlsdk/ + 7z a -tzip rehlds-bin-${{ env.APP_VERSION }}.zip bin/ hlsdk/ 7z a -t7z -m0=lzma2 -mx=9 -mfb=64 -aoa rehlds-dbg-${{ env.APP_VERSION }}.7z debug/ - name: Publish artifacts diff --git a/README.md b/README.md index 8802f7b77..196512c47 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
  • sv_rehlds_stringcmdrate_burst_punish // Time in minutes for which the player will be banned (0 - Permanent, use a negative number for a kick). Default: 5
  • sv_rehlds_userinfo_transmitted_fields // Userinfo fields only with these keys will be transmitted to clients via network. If not set then all fields will be transmitted (except prefixed with underscore). Each key must be prefixed by backslash, for example "\name\model\*sid\*hltv\bottomcolor\topcolor". See [wiki](https://github.com/dreamstalker/rehlds/wiki/Userinfo-keys) to collect sufficient set of keys for your server. Default: ""
  • sv_rehlds_attachedentities_playeranimationspeed_fix // Fixes bug with gait animation speed increase when player has some attached entities (aiments). Can cause animation lags when cl_updaterate is low. Default: 0 -
  • sv_rehlds_maxclients_from_single_ip // Limit number of connections from the single ip address. Default: 5 +
  • sv_rehlds_maxclients_from_single_ip // Limit number of connections at the same time from single IP address, not confuse to already connected players. Default: 5
  • sv_rehlds_local_gametime <1|0> // A feature of local gametime which decrease "lags" if you run same map for a long time. Default: 0
  • sv_use_entity_file // Use custom entity file for a map. Path to an entity file will be "maps/[map name].ent". 0 - use original entities. 1 - use .ent files from maps directory. 2 - use .ent files from maps directory and create new .ent file if not exist.
  • sv_rcon_allowexternal <1|0> // Usage of Rcon from externals IPs. Default: 1 diff --git a/rehlds/HLTV/Core/src/BSPModel.cpp b/rehlds/HLTV/Core/src/BSPModel.cpp index 15f091d0b..65892bcfd 100644 --- a/rehlds/HLTV/Core/src/BSPModel.cpp +++ b/rehlds/HLTV/Core/src/BSPModel.cpp @@ -170,7 +170,7 @@ byte *BSPModel::LeafPVS(mleaf_t *leaf) byte *BSPModel::DecompressVis(unsigned char *in) { - static unsigned char decompressed[MODEL_MAX_PVS]; + static unsigned char decompressed[MAX_MAP_LEAFS / 8]; if (in == nullptr) { return m_novis; } diff --git a/rehlds/HLTV/Core/src/BSPModel.h b/rehlds/HLTV/Core/src/BSPModel.h index 259960fcb..1ceb8dfdc 100644 --- a/rehlds/HLTV/Core/src/BSPModel.h +++ b/rehlds/HLTV/Core/src/BSPModel.h @@ -32,6 +32,7 @@ #include "l_studio.h" #include "edict.h" +#include "bspfile.h" // values for model_t's needload #define NL_PRESENT 0 @@ -87,9 +88,7 @@ class BSPModel: public IBSPModel { protected: model_t m_model; - - enum { MODEL_MAX_PVS = 1024 }; - byte m_novis[MODEL_MAX_PVS]; + byte m_novis[MAX_MAP_LEAFS / 8]; byte *m_base; int m_visframecount; diff --git a/rehlds/common/cvardef.h b/rehlds/common/cvardef.h index aea0f9e03..5d9e45ec4 100644 --- a/rehlds/common/cvardef.h +++ b/rehlds/common/cvardef.h @@ -47,4 +47,16 @@ struct cvar_listener_t const char *name; }; +typedef void (*pfnCvar_HookVariable_t) (cvar_t *pCvar); + +struct cvarhook_t +{ + pfnCvar_HookVariable_t hook; + + cvar_t *cvar; + cvarhook_t *next; +}; + +qboolean Cvar_HookVariable(const char *var_name, cvarhook_t *pHook); + #endif // CVARDEF_H diff --git a/rehlds/engine/cmodel.cpp b/rehlds/engine/cmodel.cpp index 1e4851230..3236e1bb4 100644 --- a/rehlds/engine/cmodel.cpp +++ b/rehlds/engine/cmodel.cpp @@ -31,17 +31,17 @@ unsigned char *gPAS; unsigned char *gPVS; int gPVSRowBytes; -unsigned char mod_novis[MODEL_MAX_PVS]; +unsigned char mod_novis[MAX_MAP_LEAFS / 8]; void Mod_Init(void) { SW_Mod_Init(); - Q_memset(mod_novis, 255, MODEL_MAX_PVS); + Q_memset(mod_novis, 0xFF, MAX_MAP_LEAFS / 8); } unsigned char *Mod_DecompressVis(unsigned char *in, model_t *model) { - static unsigned char decompressed[MODEL_MAX_PVS]; + static unsigned char decompressed[MAX_MAP_LEAFS / 8]; if (in == NULL) { diff --git a/rehlds/engine/cmodel.h b/rehlds/engine/cmodel.h index 5b8d5eb1f..dd99cec07 100644 --- a/rehlds/engine/cmodel.h +++ b/rehlds/engine/cmodel.h @@ -29,15 +29,10 @@ #pragma once #include "maintypes.h" -#include "model.h" - -// Looks like no more than 8096 visibility leafs per world model -const int MODEL_MAX_PVS = 1024; extern unsigned char *gPAS; extern unsigned char *gPVS; extern int gPVSRowBytes; -extern unsigned char mod_novis[MODEL_MAX_PVS]; void Mod_Init(void); unsigned char *Mod_DecompressVis(unsigned char *in, model_t *model); diff --git a/rehlds/engine/common.cpp b/rehlds/engine/common.cpp index c9f07a656..e70d03ca1 100644 --- a/rehlds/engine/common.cpp +++ b/rehlds/engine/common.cpp @@ -1446,31 +1446,64 @@ char *COM_FileExtension(char *in) #endif // #ifdef REHLDS_FIXES } -// Fills "out" with the file name without path and extension. +// Fills "out" with the file name without path and extension void COM_FileBase(const char *in, char *out) { - const char *start, *end; - int len; + COM_FileBase_s(in, out, -1); +} - *out = 0; +// Extracts the base name of a file (no path, no extension, assumes '/' as path separator) +const char *COM_FileBase_s(const char *in, char *out, int size) +{ + if (!in || !in[0]) + { + *out = '\0'; + return NULL; + } - len = Q_strlen(in); + int len = Q_strlen(in); if (len <= 0) - return; + return NULL; - start = in + len - 1; - end = in + len; - while (start >= in && *start != '/' && *start != '\\') + // scan backward for '.' + int end = len - 1; + while (end && in[end] != '.' && !PATHSEPARATOR(in[end])) + end--; + + // no '.', copy to end + if (in[end] != '.') { - if (*start == '.') - end = start; + end = len - 1; + } + else + { + // Found ',', copy to left of '.' + end--; + } + + // Scan backward for '/' + int start = len - 1; + while (start >= 0 && !PATHSEPARATOR(in[start])) start--; + + if (start < 0 || !PATHSEPARATOR(in[start])) + { + start = 0; + } + else + { + start++; } - start++; - len = end - start; - Q_strncpy(out, start, len); - out[len] = 0; + // Length of new sting + int maxcopy = end - start + 1; + if (size >= 0 && maxcopy >= size) + return NULL; + + // Copy partial string + Q_strncpy(out, &in[start], maxcopy); + out[maxcopy] = '\0'; + return out; } void COM_DefaultExtension(char *path, char *extension) @@ -1945,6 +1978,34 @@ NOXREF int COM_ExpandFilename(char *filename) return *filename != 0; } +// small helper function shared by lots of modules +qboolean COM_IsAbsolutePath(const char *pStr) +{ + if (strchr(pStr, ':') || pStr[0] == '/' || pStr[0] == '\\') + return FALSE; + + return TRUE; +} + +qboolean COM_IsValidPath(const char *pszFilename) +{ + if (!pszFilename) + return FALSE; + + if (Q_strlen(pszFilename) <= 0 || + Q_strstr(pszFilename, "\\\\") || // to protect network paths + Q_strstr(pszFilename, ":") || // to protect absolute paths + Q_strstr(pszFilename, "..") || // to protect relative paths + Q_strstr(pszFilename, "~") || + Q_strstr(pszFilename, "\n") || // CFileSystem_Stdio::FS_fopen doesn't allow this + Q_strstr(pszFilename, "\r")) // CFileSystem_Stdio::FS_fopen doesn't allow this + { + return FALSE; + } + + return TRUE; +} + int EXT_FUNC COM_FileSize(const char *filename) { FileHandle_t fp; @@ -1962,7 +2023,10 @@ int EXT_FUNC COM_FileSize(const char *filename) unsigned char* EXT_FUNC COM_LoadFile(const char *path, int usehunk, int *pLength) { - char base[33]; + if (!path || !path[0]) + return NULL; + + char base[MAX_PATH]; unsigned char *buf = NULL; #ifndef SWDS @@ -1982,8 +2046,10 @@ unsigned char* EXT_FUNC COM_LoadFile(const char *path, int usehunk, int *pLength } int len = FS_Size(hFile); - COM_FileBase(path, base); - base[32] = 0; + if (!COM_FileBase_s(path, base, sizeof(base))) + Sys_Error("%s: Bad path length: %s", __func__, path); + + base[32] = '\0'; switch (usehunk) { diff --git a/rehlds/engine/common.h b/rehlds/engine/common.h index 22d07e998..94d940480 100644 --- a/rehlds/engine/common.h +++ b/rehlds/engine/common.h @@ -169,6 +169,7 @@ NOXREF char *COM_SkipPath(char *pathname); void COM_StripExtension(char *in, char *out); char *COM_FileExtension(char *in); void COM_FileBase(const char *in, char *out); +const char *COM_FileBase_s(const char *in, char *out, int size); void COM_DefaultExtension(char *path, char *extension); void COM_UngetToken(void); char *COM_Parse(char *data); @@ -186,6 +187,8 @@ void COM_CreatePath(char *path); NOXREF void COM_CopyFile(char *netpath, char *cachepath); NOXREF int COM_ExpandFilename(char *filename); int COM_FileSize(const char *filename); +qboolean COM_IsAbsolutePath(const char *pStr); +qboolean COM_IsValidPath(const char *pszFilename); unsigned char *COM_LoadFile(const char *path, int usehunk, int *pLength); void COM_FreeFile(void *buffer); void COM_CopyFileChunk(FileHandle_t dst, FileHandle_t src, int nSize); diff --git a/rehlds/engine/cvar.cpp b/rehlds/engine/cvar.cpp index 9285fa254..7c7ce2b7f 100644 --- a/rehlds/engine/cvar.cpp +++ b/rehlds/engine/cvar.cpp @@ -32,7 +32,8 @@ All cvar names are case insensitive! Values not. */ -cvar_t *cvar_vars; +cvar_t *cvar_vars = NULL; +cvarhook_t *cvar_hooks = NULL; char cvar_null_string[] = ""; void Cvar_Init(void) @@ -319,8 +320,10 @@ void Cvar_DirectSet(struct cvar_s *var, const char *value) void Cvar_Set(const char *var_name, const char *value) { - cvar_t *var = Cvar_FindVar(var_name); + cvar_t *var; + cvarhook_t *pHook; + var = Cvar_FindVar(var_name); if (!var) { Con_DPrintf("%s: variable \"%s\" not found\n", __func__, var_name); @@ -328,6 +331,15 @@ void Cvar_Set(const char *var_name, const char *value) } Cvar_DirectSet(var, value); + + for (pHook = cvar_hooks; pHook; pHook = pHook->next) + { + if (pHook->cvar == var) + { + pHook->hook(var); + break; + } + } } void Cvar_SetValue(const char *var_name, float value) @@ -730,3 +742,36 @@ void Cvar_CmdInit(void) { Cmd_AddCommand("cvarlist", Cmd_CvarList_f); } + +qboolean Cvar_HookVariable(const char *var_name, cvarhook_t *pHook) +{ + cvar_t *cvar; + + if (!pHook || !pHook->hook) + return FALSE; + + if (pHook->cvar || pHook->next) + return FALSE; + + cvar = Cvar_FindVar(var_name); + if (!cvar) + return FALSE; + + cvarhook_t *pCur = cvar_hooks; + pHook->cvar = cvar; + + if (pCur) + { + while (pCur->next) + pCur = pCur->next; + + pCur->next = pHook; + } + else + { + // First in chain is null, assign pHook to it + cvar_hooks = pHook; + } + + return TRUE; +} diff --git a/rehlds/engine/host.cpp b/rehlds/engine/host.cpp index f30cfad65..673b3297f 100644 --- a/rehlds/engine/host.cpp +++ b/rehlds/engine/host.cpp @@ -57,7 +57,11 @@ cvar_t deathmatch = { "deathmatch", "0", FCVAR_SERVER, 0.0f, NULL }; cvar_t coop = { "coop", "0", FCVAR_SERVER, 0.0f, NULL }; cvar_t sys_ticrate = { "sys_ticrate", "100.0", 0, 0.0f, NULL }; + +void sys_timescale_hook_callback(cvar_t *cvar); +cvarhook_t sys_timescale_hook = { sys_timescale_hook_callback, NULL, NULL }; cvar_t sys_timescale = { "sys_timescale", "1.0", 0, 0.0f, NULL }; + cvar_t fps_max = { "fps_max", "100.0", FCVAR_ARCHIVE, 0.0f, NULL }; cvar_t host_killtime = { "host_killtime", "0.0", 0, 0.0f, NULL }; cvar_t sv_stats = { "sv_stats", "1", 0, 0.0f, NULL }; @@ -143,6 +147,9 @@ void Host_InitLocal(void) Host_InitCommands(); Cvar_RegisterVariable(&host_killtime); Cvar_RegisterVariable(&sys_ticrate); + Cvar_RegisterVariable(&sys_timescale); + Cvar_HookVariable(sys_timescale.name, &sys_timescale_hook); + Cvar_RegisterVariable(&fps_max); Cvar_RegisterVariable(&fps_override); Cvar_RegisterVariable(&host_name); @@ -351,22 +358,28 @@ void Host_WriteCustomConfig(void) void SV_ClientPrintf(const char *fmt, ...) { - va_list va; - char string[1024]; - if (!host_client->fakeclient) { + va_list va; + char string[1024]; + va_start(va, fmt); Q_vsnprintf(string, ARRAYSIZE(string) - 1, fmt, va); va_end(va); - string[ARRAYSIZE(string) - 1] = 0; - - MSG_WriteByte(&host_client->netchan.message, svc_print); - MSG_WriteString(&host_client->netchan.message, string); + g_RehldsHookchains.m_SV_ClientPrintf.callChain(SV_ClientPrintf_internal, string); } } +void EXT_FUNC SV_ClientPrintf_internal(const char *Dest) +{ + char string[1024]; + + Q_strlcpy(string, Dest, min(strlen(Dest) + 1, sizeof(string))); + MSG_WriteByte(&host_client->netchan.message, svc_print); + MSG_WriteString(&host_client->netchan.message, string); +} + void SV_BroadcastPrintf(const char *fmt, ...) { va_list argptr; @@ -1184,7 +1197,11 @@ int Host_Init(quakeparms_t *parms) else { Cvar_RegisterVariable(&suitvolume); +#ifdef REHLDS_FIXES + Cvar_RegisterVariable(&r_cachestudio); +#endif } + Cbuf_InsertText("exec valve.rc\n"); Hunk_AllocName(0, "-HOST_HUNKLEVEL-"); host_hunklevel = Hunk_LowMark(); @@ -1269,3 +1286,23 @@ void Host_Shutdown(void) g_psv.time = 0.0f; g_pcl.time = 0.0f; } + +void sys_timescale_hook_callback(cvar_t *cvar) +{ + int i; + client_t *client = NULL; + + if (!Host_IsServerActive()) + return; + + for (i = 0; i < g_psvs.maxclients; i++) + { + client = &g_psvs.clients[i]; + + if (!client->fakeclient && (client->active || client->spawned || client->connected)) + { + MSG_WriteByte(&client->netchan.message, svc_timescale); + MSG_WriteFloat(&client->netchan.message, max(0.1f, sys_timescale.value)); + } + } +} diff --git a/rehlds/engine/host.h b/rehlds/engine/host.h index fa656f7c6..cfe84abfd 100644 --- a/rehlds/engine/host.h +++ b/rehlds/engine/host.h @@ -85,6 +85,7 @@ NOXREF void Info_WriteVars(FileHandle_t fp); void Host_WriteConfiguration(void); void Host_WriteCustomConfig(void); void SV_ClientPrintf(const char *fmt, ...); +void SV_ClientPrintf_internal(const char *Dest); void SV_BroadcastPrintf(const char *fmt, ...); void Host_ClientCommands(const char *fmt, ...); void SV_DropClient_api(IGameClient* cl, bool crash, const char* fmt, ...); diff --git a/rehlds/engine/host_cmd.cpp b/rehlds/engine/host_cmd.cpp index d76815cb8..993e56abd 100644 --- a/rehlds/engine/host_cmd.cpp +++ b/rehlds/engine/host_cmd.cpp @@ -205,11 +205,19 @@ void Host_Motd_f(void) char *next; pFileList = motdfile.string; - if (*pFileList == '/' || Q_strstr(pFileList, ":") || Q_strstr(pFileList, "..") || Q_strstr(pFileList, "\\")) + if (!COM_IsValidPath(pFileList) || COM_IsAbsolutePath(pFileList)) { Con_Printf("Unable to open %s (contains illegal characters)\n", pFileList); return; } + + const char *pchExtension = COM_FileExtension(pFileList); + if (Q_stricmp(pchExtension, "txt") != 0) + { + Con_Printf("Invalid motdfile name %s (wrong file extension, must be .txt)\n", pFileList); + return; + } + pFile = FS_Open(pFileList, "rb"); if (!pFile) { @@ -753,6 +761,111 @@ void Host_Status_Formatted_f(void) Host_Status_Printf(conprint, log, "%i users\n", nClients); } +// Sets client to godmode +void Host_God_f(void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + return; + } + + if (!sv_cheats.value) + return; + + sv_player->v.flags = (int)sv_player->v.flags ^ FL_GODMODE; + if (!((int)sv_player->v.flags & FL_GODMODE)) + SV_ClientPrintf("godmode OFF\n"); + else + SV_ClientPrintf("godmode ON\n"); +} + +// Sets client to notarget mode +void Host_Notarget_f(void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + return; + } + + if (!sv_cheats.value) + return; + + sv_player->v.flags = (int)sv_player->v.flags ^ FL_NOTARGET; + if (!((int)sv_player->v.flags & FL_NOTARGET)) + SV_ClientPrintf("notarget OFF\n"); + else + SV_ClientPrintf("notarget ON\n"); +} + +// Searches along the direction ray in steps of "step" to see if +// the entity position is passible +// Used for putting the player in valid space when toggling off noclip mode +int FindPassableSpace(edict_t *pEdict, vec_t *direction, float step) +{ + int i; + + for (i = 0; i < 100; i++) + { + VectorMA(pEdict->v.origin, step, direction, pEdict->v.origin); + + if (!SV_TestEntityPosition(pEdict)) + { + // Store old origin + VectorCopy(pEdict->v.origin, pEdict->v.oldorigin); + return TRUE; + } + } + + return FALSE; +} + +void Host_Noclip_f(void) +{ + if (cmd_source == src_command) + { + Cmd_ForwardToServer(); + return; + } + + if (!sv_cheats.value) + return; + + if (sv_player->v.movetype != MOVETYPE_NOCLIP) + { + sv_player->v.movetype = MOVETYPE_NOCLIP; + SV_ClientPrintf("noclip ON\n"); + } + else + { + sv_player->v.movetype = MOVETYPE_WALK; + + // Store old origin + VectorCopy(sv_player->v.origin, sv_player->v.oldorigin); + + SV_ClientPrintf("noclip OFF\n"); + + if (SV_TestEntityPosition(sv_player)) + { + vec3_t forward, right, up; + AngleVectors(sv_player->v.v_angle, forward, right, up); + + if (!FindPassableSpace(sv_player, forward, 1.0) + && !FindPassableSpace(sv_player, right, 1.0) + && !FindPassableSpace(sv_player, right, -1.0) // left + && !FindPassableSpace(sv_player, up, 1.0) // up + && !FindPassableSpace(sv_player, up, -1.0) // down + && !FindPassableSpace(sv_player, forward, -1.0)) // back + { + Con_DPrintf("Can't find the world\n"); + } + + VectorCopy(sv_player->v.oldorigin, sv_player->v.origin); + } + } +} + void Host_Ping_f(void) { int i; @@ -3138,11 +3251,12 @@ void Host_InitCommands(void) Cmd_AddCommand("setinfo", Host_SetInfo_f); Cmd_AddCommand("fullinfo", Host_FullInfo_f); -#ifndef SWDS Cmd_AddCommand("god", Host_God_f); Cmd_AddCommand("notarget", Host_Notarget_f); - Cmd_AddCommand("fly", Host_Fly_f); Cmd_AddCommand("noclip", Host_Noclip_f); + +#ifndef SWDS + Cmd_AddCommand("fly", Host_Fly_f); Cmd_AddCommand("viewmodel", Host_Viewmodel_f); Cmd_AddCommand("viewframe", Host_Viewframe_f); Cmd_AddCommand("viewnext", Host_Viewnext_f); diff --git a/rehlds/engine/mathlib.cpp b/rehlds/engine/mathlib.cpp index 4647b9722..667f510e1 100644 --- a/rehlds/engine/mathlib.cpp +++ b/rehlds/engine/mathlib.cpp @@ -418,3 +418,12 @@ qboolean VectorCompare(const vec_t *v1, const vec_t *v2) } #endif // #if !defined(REHLDS_SSE) + +qboolean BoundsIntersect(const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2) +{ + if (mins1[0] > maxs2[0] || mins1[1] > maxs2[1] || mins1[2] > maxs2[2]) + return FALSE; + if (maxs1[0] < mins2[0] || maxs1[1] < mins2[1] || maxs1[2] < mins2[2]) + return FALSE; + return TRUE; +} diff --git a/rehlds/engine/mathlib_e.h b/rehlds/engine/mathlib_e.h index 3b265a10f..921c701ed 100644 --- a/rehlds/engine/mathlib_e.h +++ b/rehlds/engine/mathlib_e.h @@ -171,3 +171,4 @@ void R_ConcatTransforms(float in1[3][4], float in2[3][4], float out[3][4]); NOBODY void FloorDivMod(double numer, double denom, int *quotient, int *rem); NOBODY int GreatestCommonDivisor(int i1, int i2); NOBODY fixed16_t Invert24To16(fixed16_t val); +qboolean BoundsIntersect(const vec3_t mins1, const vec3_t maxs1, const vec3_t mins2, const vec3_t maxs2); diff --git a/rehlds/engine/model.cpp b/rehlds/engine/model.cpp index a4581e539..12bd90fd7 100644 --- a/rehlds/engine/model.cpp +++ b/rehlds/engine/model.cpp @@ -29,7 +29,7 @@ #include "precompiled.h" model_t *loadmodel; -char loadname[32]; +char loadname[MAX_MODEL_NAME]; model_t mod_known[MAX_KNOWN_MODELS]; int mod_numknown; unsigned char* mod_base; @@ -330,7 +330,12 @@ model_t *Mod_LoadModel(model_t *mod, qboolean crash, qboolean trackCRC) Con_DPrintf("loading %s\n", mod->name); // allocate a new model - COM_FileBase(mod->name, loadname); + if (!COM_FileBase_s(mod->name, loadname, sizeof(loadname))) + { + Sys_Error("%s: Bad model name length: %s", __func__, mod->name); + return NULL; + } + loadmodel = mod; mod->needload = NL_PRESENT; diff --git a/rehlds/engine/model_rehlds.h b/rehlds/engine/model_rehlds.h index f36018187..61906f0c8 100644 --- a/rehlds/engine/model_rehlds.h +++ b/rehlds/engine/model_rehlds.h @@ -46,7 +46,7 @@ #include "crc.h" extern model_t* loadmodel; -extern char loadname[32]; +extern char loadname[MAX_MODEL_NAME]; extern model_t mod_known[MAX_KNOWN_MODELS]; extern int mod_numknown; extern unsigned char* mod_base; diff --git a/rehlds/engine/net_chan.cpp b/rehlds/engine/net_chan.cpp index 88c5bae4a..94149bc20 100644 --- a/rehlds/engine/net_chan.cpp +++ b/rehlds/engine/net_chan.cpp @@ -1158,7 +1158,10 @@ void Netchan_CreateFileFragmentsFromBuffer(qboolean server, netchan_t *chan, con MSG_WriteString(&buf->frag_message, filename); MSG_WriteString(&buf->frag_message, bCompressed ? "bz2" : "uncompressed"); MSG_WriteLong(&buf->frag_message, uncompressed_size); - send -= buf->frag_message.cursize; + + // Check if we aren't send more than we should + if ((chunksize - send) < buf->frag_message.cursize) + send -= buf->frag_message.cursize; } buf->isbuffer = TRUE; @@ -1321,7 +1324,7 @@ int Netchan_CreateFileFragments_(qboolean server, netchan_t *chan, const char *f remaining = filesize; pos = 0; - while (remaining) + while (remaining > 0) { send = min(chunksize, remaining); buf = Netchan_AllocFragbuf(); @@ -1353,7 +1356,10 @@ int Netchan_CreateFileFragments_(qboolean server, netchan_t *chan, const char *f MSG_WriteString(&buf->frag_message, filename); MSG_WriteString(&buf->frag_message, bCompressed ? "bz2" : "uncompressed"); MSG_WriteLong(&buf->frag_message, uncompressed_size); - send -= buf->frag_message.cursize; + + // Check if we aren't send more than we should + if ((chunksize - send) < buf->frag_message.cursize) + send -= buf->frag_message.cursize; } buf->isfile = TRUE; buf->iscompressed = bCompressed; @@ -1389,8 +1395,13 @@ void Netchan_FlushIncoming(netchan_t *chan, int stream) { fragbuf_t *p, *n; - SZ_Clear(&net_message); - msg_readcount = 0; +#ifdef REHLDS_FIXES + if ((chan->player_slot - 1) == host_client - g_psvs.clients) +#endif + { + SZ_Clear(&net_message); + msg_readcount = 0; + } p = chan->incomingbufs[stream]; while (p) diff --git a/rehlds/engine/net_ws.cpp b/rehlds/engine/net_ws.cpp index 219b73a07..4880da9b7 100644 --- a/rehlds/engine/net_ws.cpp +++ b/rehlds/engine/net_ws.cpp @@ -651,7 +651,7 @@ void NET_AdjustLag() } lasttime = realtime; - if (allow_cheats || fakelag.value == 0.0) + if (sv_cheats.value || fakelag.value == 0.0) { if (fakelag.value != gFakeLag) { @@ -689,7 +689,7 @@ qboolean NET_LagPacket(qboolean newdata, netsrc_t sock, netadr_t *from, sizebuf_ { if (fakeloss.value != 0.0) { - if (allow_cheats) + if (sv_cheats.value) { static int losscount[NS_MAX] = {}; ++losscount[sock]; diff --git a/rehlds/engine/pr_cmds.cpp b/rehlds/engine/pr_cmds.cpp index df82bce5f..dfd284683 100644 --- a/rehlds/engine/pr_cmds.cpp +++ b/rehlds/engine/pr_cmds.cpp @@ -1011,6 +1011,11 @@ qboolean EXT_FUNC PR_IsEmptyString(const char *s) } int EXT_FUNC PF_precache_sound_I(const char *s) +{ + return g_RehldsHookchains.m_PF_precache_sound_I.callChain(PF_precache_sound_I_internal, s); +} + +int EXT_FUNC PF_precache_sound_I_internal(const char *s) { if (!s) Host_Error("%s: NULL pointer", __func__); @@ -1058,6 +1063,11 @@ int EXT_FUNC PF_precache_sound_I(const char *s) } unsigned short EXT_FUNC EV_Precache(int type, const char *psz) +{ + return g_RehldsHookchains.m_EV_Precache.callChain(EV_Precache_internal, type, psz); +} + +unsigned short EXT_FUNC EV_Precache_internal(int type, const char *psz) { if (!psz) Host_Error("%s: NULL pointer", __func__); @@ -1377,6 +1387,11 @@ int SV_LookupModelIndex(const char *name) } int EXT_FUNC PF_precache_model_I(const char *s) +{ + return g_RehldsHookchains.m_PF_precache_model_I.callChain(PF_precache_model_I_internal, s); +} + +int EXT_FUNC PF_precache_model_I_internal(const char *s) { int iOptional = 0; if (!s) @@ -1432,6 +1447,9 @@ int EXT_FUNC PF_precache_model_I(const char *s) { for (int i = 0; i < MAX_MODELS; i++) { + if (!g_psv.model_precache[i]) + continue; + // use case-sensitive names to increase performance #ifdef REHLDS_FIXES if (!Q_strcmp(g_psv.model_precache[i], s)) @@ -1445,8 +1463,13 @@ int EXT_FUNC PF_precache_model_I(const char *s) } } -#ifdef REHLDS_FIXES int EXT_FUNC PF_precache_generic_I(const char *s) +{ + return g_RehldsHookchains.m_PF_precache_generic_I.callChain(PF_precache_generic_I_internal, s); +} + +#ifdef REHLDS_FIXES +int EXT_FUNC PF_precache_generic_I_internal(const char *s) { if (!s) Host_Error("%s: NULL pointer", __func__); @@ -1493,7 +1516,7 @@ int EXT_FUNC PF_precache_generic_I(const char *s) return g_rehlds_sv.precachedGenericResourceCount++; } #else // REHLDS_FIXES -int EXT_FUNC PF_precache_generic_I(const char *s) +int EXT_FUNC PF_precache_generic_I_internal(const char *s) { if (!s) Host_Error("%s: NULL pointer", __func__); @@ -1525,7 +1548,7 @@ int EXT_FUNC PF_precache_generic_I(const char *s) { for (int i = 0; i < MAX_GENERIC; i++) { - if (!Q_stricmp(g_psv.generic_precache[i], s)) + if (g_psv.generic_precache[i] && !Q_stricmp(g_psv.generic_precache[i], s)) return i; } Host_Error("%s: '%s' Precache can only be done in spawn functions", __func__, s); @@ -1779,7 +1802,14 @@ void EXT_FUNC PF_aim_I(edict_t *ent, float speed, float *rgflReturn) bestdir[1] = dir[1]; bestdir[2] = dir[2]; bestdir[0] = dir[0]; - bestdist = sv_aim.value; + if (sv_allow_autoaim.value) + { + bestdist = sv_aim.value; + } + else + { + bestdist = 0.0f; + } for (int i = 1; i < g_psv.num_edicts; i++) { diff --git a/rehlds/engine/pr_cmds.h b/rehlds/engine/pr_cmds.h index e7c20bc01..a7d424c23 100644 --- a/rehlds/engine/pr_cmds.h +++ b/rehlds/engine/pr_cmds.h @@ -120,14 +120,18 @@ edict_t *FindEntityByString(edict_t *pEdictStartSearchAfter, const char *pszFiel int GetEntityIllum(edict_t *pEnt); qboolean PR_IsEmptyString(const char *s); int PF_precache_sound_I(const char *s); +int PF_precache_sound_I_internal(const char *s); unsigned short EV_Precache(int type, const char *psz); +unsigned short EV_Precache_internal(int type, const char *psz); void EV_PlayReliableEvent_api(IGameClient *cl, int entindex, unsigned short eventindex, float delay, event_args_t *pargs); void EV_PlayReliableEvent(client_t *cl, int entindex, unsigned short eventindex, float delay, event_args_t *pargs); void EV_PlayReliableEvent_internal(client_t *cl, int entindex, unsigned short eventindex, float delay, event_args_t *pargs); void EV_Playback(int flags, const edict_t *pInvoker, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2); void EV_SV_Playback(int flags, int clientindex, unsigned short eventindex, float delay, float *origin, float *angles, float fparam1, float fparam2, int iparam1, int iparam2, int bparam1, int bparam2); int PF_precache_model_I(const char *s); +int PF_precache_model_I_internal(const char *s); int PF_precache_generic_I(const char *s); +int PF_precache_generic_I_internal(const char *s); int PF_IsMapValid_I(const char *mapname); int PF_NumberOfEntities_I(void); char *PF_GetInfoKeyBuffer_I(edict_t *e); diff --git a/rehlds/engine/r_studio.cpp b/rehlds/engine/r_studio.cpp index 5b12b835e..4124bf67f 100644 --- a/rehlds/engine/r_studio.cpp +++ b/rehlds/engine/r_studio.cpp @@ -881,6 +881,15 @@ void EXT_FUNC AnimationAutomove(const edict_t *pEdict, float flTime) void EXT_FUNC GetBonePosition(const edict_t *pEdict, int iBone, float *rgflOrigin, float *rgflAngles) { pstudiohdr = (studiohdr_t *)Mod_Extradata(g_psv.models[pEdict->v.modelindex]); + +#ifdef REHLDS_FIXES + if (!pstudiohdr) + return; + + if (iBone < 0 || iBone >= pstudiohdr->numbones) + return; // invalid bone +#endif + g_pSvBlendingAPI->SV_StudioSetupBones( g_psv.models[pEdict->v.modelindex], pEdict->v.frame, @@ -906,14 +915,23 @@ void EXT_FUNC GetAttachment(const edict_t *pEdict, int iAttachment, float *rgflO mstudioattachment_t *pattachment; vec3_t angles; - angles[0] = -pEdict->v.angles[0]; - angles[1] = pEdict->v.angles[1]; - angles[2] = pEdict->v.angles[2]; - pstudiohdr = (studiohdr_t *)Mod_Extradata(g_psv.models[pEdict->v.modelindex]); + +#ifdef REHLDS_FIXES + if (!pstudiohdr) + return; + + if (iAttachment < 0 || iAttachment >= pstudiohdr->numattachments) + return; // invalid attachment +#endif + pattachment = (mstudioattachment_t *)((char *)pstudiohdr + pstudiohdr->attachmentindex); pattachment += iAttachment; + angles[0] = -pEdict->v.angles[0]; + angles[1] = pEdict->v.angles[1]; + angles[2] = pEdict->v.angles[2]; + g_pSvBlendingAPI->SV_StudioSetupBones( g_psv.models[pEdict->v.modelindex], pEdict->v.frame, diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h index 53792cf2b..5fda2c1ba 100644 --- a/rehlds/engine/server.h +++ b/rehlds/engine/server.h @@ -283,6 +283,7 @@ extern rehlds_server_t g_rehlds_sv; extern cvar_t sv_lan; extern cvar_t sv_lan_rate; extern cvar_t sv_aim; +extern cvar_t sv_allow_autoaim; extern cvar_t sv_skycolor_r; extern cvar_t sv_skycolor_g; @@ -349,7 +350,6 @@ extern cvar_t sv_proxies; extern cvar_t sv_outofdatetime; extern cvar_t mapchangecfgfile; -extern qboolean allow_cheats; extern cvar_t mp_logecho; extern cvar_t mp_logfile; extern cvar_t sv_allow_download; @@ -405,7 +405,6 @@ enum GameType_e extern GameType_e g_eGameType; -extern int fatbytes; extern int giNextUserMsg; extern int hashstrings_collisions; @@ -418,10 +417,6 @@ extern delta_t *g_pweapondelta; extern delta_t *g_pusercmddelta; #endif -extern unsigned char fatpvs[1024]; -extern int fatpasbytes; -extern unsigned char fatpas[1024]; - extern int gPacketSuppressed; extern char localinfo[MAX_LOCALINFO]; @@ -498,6 +493,7 @@ int SV_CheckKeyInfo_internal(netadr_t *adr, char *protinfo, unsigned short *port int SV_CheckForDuplicateSteamID(client_t *client); qboolean SV_CheckForDuplicateNames(char *userinfo, qboolean bIsReconnecting, int nExcludeSlot); int SV_CheckUserInfo(netadr_t *adr, char *userinfo, qboolean bIsReconnecting, int nReconnectSlot, char *name); +int SV_CheckUserInfo_internal(netadr_t *adr, char *userinfo, qboolean bIsReconnecting, int nReconnectSlot, char *name); int SV_FindEmptySlot(netadr_t *adr, int *pslot, client_t ** ppClient); void SV_ConnectClient(void); void SV_ConnectClient_internal(void); @@ -569,6 +565,7 @@ void SV_SendClientMessages(void); void SV_ExtractFromUserinfo(client_t *cl); int SV_ModelIndex(const char *name); void SV_AddResource(resourcetype_t type, const char *name, int size, unsigned char flags, int index); +void SV_AddResource_internal(resourcetype_t type, const char *name, int size, unsigned char flags, int index); size_t SV_CountResourceByType(resourcetype_t type, resource_t **pResourceList = nullptr, size_t nListMax = 0, size_t *nWidthFileNameMax = nullptr); void SV_CreateGenericResources(void); void SV_CreateResourceList(void); diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 59bc84200..69f5f1243 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -100,7 +100,6 @@ redirect_t sv_redirected; netadr_t sv_redirectto; GameType_e g_eGameType = GT_Unitialized; -qboolean allow_cheats; char *gNullString = ""; int SV_UPDATE_BACKUP = SINGLEPLAYER_BACKUP; @@ -115,6 +114,7 @@ int giNextUserMsg = 64; cvar_t sv_lan = { "sv_lan", "0", 0, 0.0f, NULL }; cvar_t sv_lan_rate = { "sv_lan_rate", "20000.0", 0, 0.0f, NULL }; cvar_t sv_aim = { "sv_aim", "1", FCVAR_SERVER | FCVAR_ARCHIVE , 0.0f, NULL }; +cvar_t sv_allow_autoaim = { "sv_allow_autoaim", "1", FCVAR_SERVER | FCVAR_ARCHIVE, 0.0f, NULL }; cvar_t sv_skycolor_r = { "sv_skycolor_r", "0", 0, 0.0f, NULL }; cvar_t sv_skycolor_g = { "sv_skycolor_g", "0", 0, 0.0f, NULL }; @@ -130,6 +130,12 @@ cvar_t sv_waterfriction = { "sv_waterfriction", "1", FCVAR_SERVER, 0.0f, NULL }; cvar_t sv_zmax = { "sv_zmax", "4096", FCVAR_SPONLY, 0.0f, NULL }; cvar_t sv_wateramp = { "sv_wateramp", "0", 0, 0.0f, NULL }; +void sv_cheats_hook_callback(cvar_t *cvar); +void mapcyclefile_hook_callback(cvar_t *cvar); + +cvarhook_t sv_cheats_hook = { sv_cheats_hook_callback, NULL, NULL }; +cvarhook_t mapcyclefile_hook = { mapcyclefile_hook_callback, NULL, NULL }; + cvar_t sv_skyname = { "sv_skyname", "desert", 0, 0.0f, NULL }; cvar_t mapcyclefile = { "mapcyclefile", "mapcycle.txt", 0, 0.0f, NULL }; cvar_t motdfile = { "motdfile", "motd.txt", 0, 0.0f, NULL }; @@ -683,22 +689,22 @@ qboolean SV_BuildSoundMsg(edict_t *entity, int channel, const char *sample, int if (volume < 0 || volume > 255) { - Con_Printf("%s: volume = %i", __func__, volume); + Con_Printf("%s: volume = %i\n", __func__, volume); volume = (volume < 0) ? 0 : 255; } if (attenuation < 0.0f || attenuation > 4.0f) { - Con_Printf("%s: attenuation = %f", __func__, attenuation); + Con_Printf("%s: attenuation = %f\n", __func__, attenuation); attenuation = (attenuation < 0.0f) ? 0.0f : 4.0f; } if (channel < 0 || channel > 7) { - Con_Printf("%s: channel = %i", __func__, channel); + Con_Printf("%s: channel = %i\n", __func__, channel); channel = (channel < 0) ? CHAN_AUTO : CHAN_NETWORKVOICE_BASE; } if (pitch < 0 || pitch > 255) { - Con_Printf("%s: pitch = %i", __func__, pitch); + Con_Printf("%s: pitch = %i\n", __func__, pitch); pitch = (pitch < 0) ? 0 : 255; } @@ -710,7 +716,7 @@ qboolean SV_BuildSoundMsg(edict_t *entity, int channel, const char *sample, int sound_num = Q_atoi(sample + 1); if (sound_num >= CVOXFILESENTENCEMAX) { - Con_Printf("%s: invalid sentence number: %s", __func__, sample + 1); + Con_Printf("%s: invalid sentence number: %s\n", __func__, sample + 1); return FALSE; } } @@ -1115,8 +1121,18 @@ void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client) else MSG_WriteByte(msg, 0); - COM_FileBase(com_gamedir, message); - MSG_WriteString(msg, message); + const char *pszGameDir = message; + +#ifdef REHLDS_FIXES + // Give the client a chance to connect in to the server with different game + const char *gd = Info_ValueForKey(client->userinfo, "_gd"); + if (gd[0]) + pszGameDir = gd; + else +#endif + COM_FileBase(com_gamedir, message); + + MSG_WriteString(msg, pszGameDir); MSG_WriteString(msg, Cvar_VariableString("hostname")); MSG_WriteString(msg, g_psv.modelname); @@ -1166,7 +1182,7 @@ void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client) MSG_WriteByte(msg, svc_sendextrainfo); MSG_WriteString(msg, com_clientfallback); - MSG_WriteByte(msg, allow_cheats); + MSG_WriteByte(msg, sv_cheats.value != 0); SV_WriteDeltaDescriptionsToClient(msg); SV_SetMoveVars(); @@ -2118,6 +2134,11 @@ void SV_ReplaceSpecialCharactersInName(char *newname, const char *oldname) #endif int SV_CheckUserInfo(netadr_t *adr, char *userinfo, qboolean bIsReconnecting, int nReconnectSlot, char *name) +{ + return g_RehldsHookchains.m_SV_CheckUserInfo.callChain(SV_CheckUserInfo_internal, adr, userinfo, bIsReconnecting, nReconnectSlot, name); +} + +int EXT_FUNC SV_CheckUserInfo_internal(netadr_t *adr, char *userinfo, qboolean bIsReconnecting, int nReconnectSlot, char *name) { const char *s; char newname[MAX_NAME]; @@ -4037,9 +4058,10 @@ void SV_EmitEvents_internal(client_t *cl, packet_entities_t *pack, sizebuf_t *ms } int fatbytes; -unsigned char fatpvs[1024]; +unsigned char fatpvs[MAX_MAP_LEAFS / 8]; + int fatpasbytes; -unsigned char fatpas[1024]; +unsigned char fatpas[MAX_MAP_LEAFS / 8]; void SV_AddToFatPVS(vec_t *org, mnode_t *node) { @@ -4081,6 +4103,9 @@ unsigned char* EXT_FUNC SV_FatPVS(float *org) #endif // REHLDS_FIXES fatbytes = (g_psv.worldmodel->numleafs + 31) >> 3; + if (fatbytes >= (MAX_MAP_LEAFS / 8)) + Sys_Error("%s: MAX_MAP_LEAFS limit exceeded\n", __func__); + Q_memset(fatpvs, 0, fatbytes); SV_AddToFatPVS(org, g_psv.worldmodel->nodes); return fatpvs; @@ -4138,6 +4163,9 @@ unsigned char* EXT_FUNC SV_FatPAS(float *org) #endif // REHLDS_FIXES fatpasbytes = (g_psv.worldmodel->numleafs + 31) >> 3; + if (fatpasbytes >= (MAX_MAP_LEAFS / 8)) + Sys_Error("%s: MAX_MAP_LEAFS limit exceeded\n", __func__); + Q_memset(fatpas, 0, fatpasbytes); SV_AddToFatPAS(org, g_psv.worldmodel->nodes); return fatpas; @@ -5132,7 +5160,17 @@ int SV_ModelIndex(const char *name) Sys_Error("%s: SV_ModelIndex: model %s not precached", __func__, name); } +void EXT_FUNC SV_AddResource_hook(resourcetype_t type, const char *name, int size, unsigned char flags, int index) +{ + SV_AddResource_internal(type, name, size, flags, index); +} + void EXT_FUNC SV_AddResource(resourcetype_t type, const char *name, int size, unsigned char flags, int index) +{ + g_RehldsHookchains.m_SV_AddResource.callChain(SV_AddResource_hook, type, name, size, flags, index); +} + +void SV_AddResource_internal(resourcetype_t type, const char *name, int size, unsigned char flags, int index) { resource_t *r; #ifdef REHLDS_FIXES @@ -6152,7 +6190,7 @@ int SV_SpawnServer(qboolean bIsDemo, char *server, char *startspot) if (g_psvs.maxclients <= 1) { int row = (g_psv.worldmodel->numleafs + 7) / 8; - if (row < 0 || row > MODEL_MAX_PVS) + if (row < 0 || row > (MAX_MAP_LEAFS / 8)) { Sys_Error("%s: oversized g_psv.worldmodel->numleafs: %i", __func__, g_psv.worldmodel->numleafs); } @@ -6237,7 +6275,6 @@ int SV_SpawnServer(qboolean bIsDemo, char *server, char *startspot) gGlobalVariables.serverflags = g_psvs.serverflags; gGlobalVariables.mapname = (size_t)g_psv.name - (size_t)pr_strings; gGlobalVariables.startspot = (size_t)g_psv.startspot - (size_t)pr_strings; - allow_cheats = sv_cheats.value; SV_SetMoveVars(); return 1; @@ -6556,6 +6593,42 @@ void EXT_FUNC SV_SerializeSteamid(USERID_t* id, USERID_t* serialized) *serialized = *id; } +void sv_cheats_hook_callback(cvar_t *cvar) +{ + int i; + client_t *client = NULL; + + if (!Host_IsServerActive()) + return; + + for (i = 0; i < g_psvs.maxclients; i++) + { + client = &g_psvs.clients[i]; + + if (!client->fakeclient && (client->active || client->spawned || client->connected)) + { + MSG_WriteByte(&client->netchan.message, svc_sendextrainfo); + MSG_WriteString(&client->netchan.message, ""); + MSG_WriteByte(&client->netchan.message, sv_cheats.value != 0); + } + } +} + +void mapcyclefile_hook_callback(cvar_t *cvar) +{ + char buf[MAX_PATH + 4]; + + if (!Q_strcmp(COM_FileExtension(cvar->string), "txt")) + return; + + Q_snprintf(buf, sizeof(buf) - 3, "%s.txt", cvar->string); + + if (!Q_strcmp(COM_FileExtension(buf), "txt")) + Cvar_DirectSet(cvar, buf); + else + Cvar_DirectSet(cvar, "mapcycle.txt"); +} + void SV_BanId_f(void) { char szreason[256]; @@ -7964,6 +8037,9 @@ void SV_Init(void) Cvar_RegisterVariable(&sv_visiblemaxplayers); Cvar_RegisterVariable(&sv_password); Cvar_RegisterVariable(&sv_aim); +#ifdef REHLDS_FIXES + Cvar_RegisterVariable(&sv_allow_autoaim); +#endif Cvar_RegisterVariable(&violence_hblood); Cvar_RegisterVariable(&violence_ablood); Cvar_RegisterVariable(&violence_hgibs); @@ -7994,6 +8070,7 @@ void SV_Init(void) Cvar_RegisterVariable(&sv_skyname); Cvar_RegisterVariable(&sv_maxvelocity); Cvar_RegisterVariable(&sv_cheats); + Cvar_HookVariable(sv_cheats.name, &sv_cheats_hook); if (COM_CheckParm("-dev")) Cvar_SetValue("sv_cheats", 1.0); Cvar_RegisterVariable(&sv_spectatormaxspeed); @@ -8005,6 +8082,7 @@ void SV_Init(void) Cvar_RegisterVariable(&sv_logbans); Cvar_RegisterVariable(&hpk_maxsize); Cvar_RegisterVariable(&mapcyclefile); + Cvar_HookVariable(mapcyclefile.name, &mapcyclefile_hook); Cvar_RegisterVariable(&motdfile); Cvar_RegisterVariable(&servercfgfile); Cvar_RegisterVariable(&mapchangecfgfile); diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index 5e2c0018b..34ec65176 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -42,7 +42,7 @@ edict_t *sv_player; qboolean nofind; #if defined(SWDS) && defined(REHLDS_FIXES) -const char *clcommands[] = { "status", "name", "kill", "pause", "spawn", "new", "sendres", "dropclient", "kick", "ping", "dlfile", "setinfo", "sendents", "fullupdate", "setpause", "unpause", NULL }; +const char *clcommands[] = { "status", "name", "kill", "pause", "spawn", "new", "sendres", "dropclient", "kick", "ping", "dlfile", "setinfo", "sendents", "fullupdate", "setpause", "unpause", "noclip", "god", "notarget", NULL }; #else const char *clcommands[23] = { "status", "god", "notarget", "fly", "name", "noclip", "kill", "pause", "spawn", "new", "sendres", "dropclient", "kick", "ping", "dlfile", "nextdl", "setinfo", "showinfo", "sendents", "fullupdate", "setpause", "unpause", NULL }; #endif @@ -511,13 +511,20 @@ void SV_CopyEdictToPhysent(physent_t *pe, int e, edict_t *check) pe->vuser4[2] = check->v.vuser4[2]; } +bool EXT_FUNC SV_AllowPhysent_mod(edict_t* check, edict_t* sv_player) { + return true; +} + +bool SV_AllowPhysent(edict_t* check, edict_t* sv_player) { + return g_RehldsHookchains.m_SV_AllowPhysent.callChain(SV_AllowPhysent_mod, check, sv_player); +} + void SV_AddLinksToPM_(areanode_t *node, float *pmove_mins, float *pmove_maxs) { struct link_s *l; edict_t *check; int e; physent_t *ve; - int i; link_t *next; float *fmax; float *fmin; @@ -547,6 +554,11 @@ void SV_AddLinksToPM_(areanode_t *node, float *pmove_mins, float *pmove_maxs) if (check->v.solid != SOLID_BSP && check->v.solid != SOLID_BBOX && check->v.solid != SOLID_SLIDEBOX && check->v.solid != SOLID_NOT) continue; + // Apply our own custom checks + if (!SV_AllowPhysent(check, sv_player)) { + continue; + } + e = NUM_FOR_EDICT(check); ve = &pmove->visents[pmove->numvisent]; pmove->numvisent = pmove->numvisent + 1; @@ -575,13 +587,7 @@ void SV_AddLinksToPM_(areanode_t *node, float *pmove_mins, float *pmove_maxs) if (check->v.flags & FL_CLIENT) SV_GetTrueMinMax(e - 1, &fmin, &fmax); - for (i = 0; i < 3; i++) - { - if (fmin[i] > pmove_maxs[i] || fmax[i] < pmove_mins[i]) - break; - } - - if (i != 3) + if (!BoundsIntersect(pmove_mins, pmove_maxs, fmin, fmax)) continue; if (check->v.solid || check->v.skin != -16) diff --git a/rehlds/engine/unicode_strtools.cpp b/rehlds/engine/unicode_strtools.cpp index 818145b23..385cee971 100644 --- a/rehlds/engine/unicode_strtools.cpp +++ b/rehlds/engine/unicode_strtools.cpp @@ -285,7 +285,7 @@ static const uint32_t g_isPrintTable[2048] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFF0000, 0xFFFFFFFF, 0xFFFCFFFF, 0xFFFFFFFF, 0x000000FF, 0x0FFF0000, 0x03FF0000, 0xFFFF0000, 0xFFF7FFFF, 0xFFDF0D0B, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x9FFFFFFF, - 0x8FFFF7EE, 0xBFFFFFFF, 0xAFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF, 0x1CFCFCFC, 0x00000000 + 0x8FFFF7EE, 0xBFFFFFFF, 0xAFFFFFFE, 0xFFFFFFFF, 0xFFFFFFFF, 0x7FFFFFFE, 0x1CFCFCFC, 0x00000000 }; //----------------------------------------------------------------------------- @@ -461,7 +461,7 @@ static uchar16 *StripWhitespaceWorker(uchar16 *pwch, int cchLength, bool *pbStri // walk backwards from the end of the string, killing any whitespace *pbStrippedWhitespace = false; - uchar16 *pwchEnd = pwch + cchLength; + uchar16 *pwchEnd = pwch + cchLength - 1; while (--pwchEnd >= pwch) { if (!iswspace(*pwchEnd) && !Q_IsMeanSpaceW(*pwchEnd)) @@ -474,7 +474,7 @@ static uchar16 *StripWhitespaceWorker(uchar16 *pwch, int cchLength, bool *pbStri // walk forward in the string while (pwch < pwchEnd) { - if (!iswspace(*pwch)) + if (!iswspace(*pwch) && !Q_IsMeanSpaceW(*pwch)) break; *pbStrippedWhitespace = true; @@ -484,7 +484,7 @@ static uchar16 *StripWhitespaceWorker(uchar16 *pwch, int cchLength, bool *pbStri return pwch; } -uchar16 *__cdecl StripUnprintableWorker(uchar16 *pwch, bool *pStripped) +uchar16 *__cdecl StripUnprintableWorker(uchar16 *pwch, int *pLength, bool *pStripped) { uchar16* rPos = pwch; uchar16* wPos = pwch; @@ -503,6 +503,10 @@ uchar16 *__cdecl StripUnprintableWorker(uchar16 *pwch, bool *pStripped) *wPos = 0; *pStripped = rPos != wPos; + + if (*pStripped) + *pLength = (wPos - pwch) + 1; // null termination + return pwch; } @@ -736,8 +740,8 @@ qboolean Q_StripUnprintableAndSpace(char *pch) bStrippedAny = false; bStrippedWhitespace = false; int cwch = (unsigned int)Q_UTF8ToUTF16(pch, (uchar16 *)pwch_alloced, cubDest, _STRINGCONVERTFLAG_ASSERT) >> 1; - uchar16 * pwch = StripUnprintableWorker(pwch_alloced, &bStrippedAny); - pwch = StripWhitespaceWorker(pwch, cwch - 1, &bStrippedWhitespace); + uchar16 * pwch = StripUnprintableWorker(pwch_alloced, &cwch, &bStrippedAny); + pwch = StripWhitespaceWorker(pwch, cwch, &bStrippedWhitespace); if (bStrippedWhitespace || bStrippedAny) Q_UTF16ToUTF8(pwch, pch, cch, STRINGCONVERT_ASSERT_REPLACE); diff --git a/rehlds/engine/world.cpp b/rehlds/engine/world.cpp index 4cec668ff..8038ab755 100644 --- a/rehlds/engine/world.cpp +++ b/rehlds/engine/world.cpp @@ -359,12 +359,7 @@ void SV_TouchLinks(edict_t *ent, areanode_t *node) if (touch->v.solid != SOLID_TRIGGER) continue; - if (ent->v.absmin[0] > touch->v.absmax[0] - || ent->v.absmin[1] > touch->v.absmax[1] - || ent->v.absmin[2] > touch->v.absmax[2] - || ent->v.absmax[0] < touch->v.absmin[0] - || ent->v.absmax[1] < touch->v.absmin[1] - || ent->v.absmax[2] < touch->v.absmin[2]) + if (!BoundsIntersect(ent->v.absmin, ent->v.absmax, touch->v.absmin, touch->v.absmax)) continue; // check brush triggers accuracy @@ -647,12 +642,7 @@ int SV_LinkContents(areanode_t *node, const vec_t *pos) if (Mod_GetType(touch->v.modelindex) != mod_brush) continue; - if (pos[0] > touch->v.absmax[0] - || pos[1] > touch->v.absmax[1] - || pos[2] > touch->v.absmax[2] - || pos[0] < touch->v.absmin[0] - || pos[1] < touch->v.absmin[1] - || pos[2] < touch->v.absmin[2]) + if (!BoundsIntersect(pos, pos, touch->v.absmin, touch->v.absmax)) continue; int contents = touch->v.skin; @@ -1190,13 +1180,15 @@ void SV_ClipToLinks(areanode_t *node, moveclip_t *clip) if (touch->v.solid == SOLID_TRIGGER) Sys_Error("%s: Trigger in clipping list", __func__); +#ifndef REHLDS_OPT_PEDANTIC if (gNewDLLFunctions.pfnShouldCollide && !gNewDLLFunctions.pfnShouldCollide(touch, clip->passedict)) #ifdef REHLDS_FIXES // https://github.com/dreamstalker/rehlds/issues/46 continue; #else return; -#endif +#endif // REHLDS_FIXES +#endif // REHLDS_OPT_PEDANTIC // monsterclip filter if (touch->v.solid == SOLID_BSP) @@ -1214,12 +1206,7 @@ void SV_ClipToLinks(areanode_t *node, moveclip_t *clip) if (clip->ignoretrans && touch->v.rendermode != kRenderNormal && !(touch->v.flags & FL_WORLDBRUSH)) continue; - if (clip->boxmins[0] > touch->v.absmax[0] - || clip->boxmins[1] > touch->v.absmax[1] - || clip->boxmins[2] > touch->v.absmax[2] - || clip->boxmaxs[0] < touch->v.absmin[0] - || clip->boxmaxs[1] < touch->v.absmin[1] - || clip->boxmaxs[2] < touch->v.absmin[2]) + if (!BoundsIntersect(clip->boxmins, clip->boxmaxs, touch->v.absmin, touch->v.absmax)) continue; if (touch->v.solid != SOLID_SLIDEBOX @@ -1248,6 +1235,16 @@ void SV_ClipToLinks(areanode_t *node, moveclip_t *clip) continue; // don't clip against owner } +#ifdef REHLDS_OPT_PEDANTIC + if (gNewDLLFunctions.pfnShouldCollide && !gNewDLLFunctions.pfnShouldCollide(touch, clip->passedict)) +#ifdef REHLDS_FIXES + // https://github.com/dreamstalker/rehlds/issues/46 + continue; +#else + return; +#endif // REHLDS_FIXES +#endif // REHLDS_OPT_PEDANTIC + trace_t trace; if (touch->v.flags & FL_MONSTER) trace = SV_ClipMoveToEntity(touch, clip->start, clip->mins2, clip->maxs2, clip->end); @@ -1298,12 +1295,7 @@ void SV_ClipToWorldbrush(areanode_t *node, moveclip_t *clip) if (!(touch->v.flags & FL_WORLDBRUSH)) continue; - if (clip->boxmins[0] > touch->v.absmax[0] - || clip->boxmins[1] > touch->v.absmax[1] - || clip->boxmins[2] > touch->v.absmax[2] - || clip->boxmaxs[0] < touch->v.absmin[0] - || clip->boxmaxs[1] < touch->v.absmin[1] - || clip->boxmaxs[2] < touch->v.absmin[2]) + if (!BoundsIntersect(clip->boxmins, clip->boxmaxs, touch->v.absmin, touch->v.absmax)) continue; if (clip->trace.allsolid) diff --git a/rehlds/engine/zone.cpp b/rehlds/engine/zone.cpp index afd84697a..3a1ab65f7 100644 --- a/rehlds/engine/zone.cpp +++ b/rehlds/engine/zone.cpp @@ -679,7 +679,7 @@ void Cache_Force_Flush() void Cache_Flush() { - if (g_pcl.maxclients <= 1 || allow_cheats) + if (g_pcl.maxclients <= 1 || sv_cheats.value) { Cache_Force_Flush(); } diff --git a/rehlds/engine/zone.h b/rehlds/engine/zone.h index 46dcd22cc..8c7b43776 100644 --- a/rehlds/engine/zone.h +++ b/rehlds/engine/zone.h @@ -30,7 +30,7 @@ #include "maintypes.h" -#define ZONE_DYNAMIC_SIZE 0x20000 +#define ZONE_DYNAMIC_SIZE 0x200000 typedef struct memblock_s memblock_t; typedef struct memzone_s memzone_t; diff --git a/rehlds/msvc/ReHLDS.vcxproj b/rehlds/msvc/ReHLDS.vcxproj index 2078e8301..dc153551e 100644 --- a/rehlds/msvc/ReHLDS.vcxproj +++ b/rehlds/msvc/ReHLDS.vcxproj @@ -463,6 +463,7 @@ {6973dca5-253c-4d84-b51e-187e035eae06} + false diff --git a/rehlds/public/rehlds/bspfile.h b/rehlds/public/rehlds/bspfile.h index 3c4dfee7b..4998fcf81 100644 --- a/rehlds/public/rehlds/bspfile.h +++ b/rehlds/public/rehlds/bspfile.h @@ -32,6 +32,7 @@ #define HLBSP_VERSION 30 // half-life regular version #define MAX_MAP_HULLS 4 +#define MAX_MAP_LEAFS 32767 // signed short limit #define CONTENTS_ORIGIN -7 // removed at csg time #define CONTENTS_CLIP -8 // changed to contents_solid diff --git a/rehlds/public/rehlds/rehlds_api.h b/rehlds/public/rehlds/rehlds_api.h index 876752a6b..bc8358194 100644 --- a/rehlds/public/rehlds/rehlds_api.h +++ b/rehlds/public/rehlds/rehlds_api.h @@ -37,7 +37,7 @@ #include "pr_dlls.h" #define REHLDS_API_VERSION_MAJOR 3 -#define REHLDS_API_VERSION_MINOR 11 +#define REHLDS_API_VERSION_MINOR 13 //Steam_NotifyClientConnect hook typedef IHookChain IRehldsHook_Steam_NotifyClientConnect; @@ -227,6 +227,37 @@ typedef IVoidHookChainRegistry IRehldsHookRegistry_ED_Free; typedef IHookChain IRehldsHook_Con_Printf; typedef IHookChainRegistry IRehldsHookRegistry_Con_Printf; +//SV_CheckUserInfo hook +typedef IHookChain IRehldsHook_SV_CheckUserInfo; +typedef IHookChainRegistry IRehldsHookRegistry_SV_CheckUserInfo; + +//PF_precache_generic_I hook +typedef IHookChain IRehldsHook_PF_precache_generic_I; +typedef IHookChainRegistry IRehldsHookRegistry_PF_precache_generic_I; + +//PF_precache_model_I hook +typedef IHookChain IRehldsHook_PF_precache_model_I; +typedef IHookChainRegistry IRehldsHookRegistry_PF_precache_model_I; + +//PF_precache_sound_I hook +typedef IHookChain IRehldsHook_PF_precache_sound_I; +typedef IHookChainRegistry IRehldsHookRegistry_PF_precache_sound_I; + +//EV_Precache hook +typedef IHookChain IRehldsHook_EV_Precache; +typedef IHookChainRegistry IRehldsHookRegistry_EV_Precache; + +//SV_AddResource hook +typedef IVoidHookChain IRehldsHook_SV_AddResource; +typedef IVoidHookChainRegistry IRehldsHookRegistry_SV_AddResource; + +//SV_ClientPrintf hook +typedef IVoidHookChain IRehldsHook_SV_ClientPrintf; +typedef IVoidHookChainRegistry IRehldsHookRegistry_SV_ClientPrintf; + +//SV_AllowPhysent hook +typedef IHookChain IRehldsHook_SV_AllowPhysent; +typedef IHookChainRegistry IRehldsHookRegistry_SV_AllowPhysent; class IRehldsHookchains { public: @@ -279,6 +310,14 @@ class IRehldsHookchains { virtual IRehldsHookRegistry_ED_Alloc* ED_Alloc() = 0; virtual IRehldsHookRegistry_ED_Free* ED_Free() = 0; virtual IRehldsHookRegistry_Con_Printf* Con_Printf() = 0; + virtual IRehldsHookRegistry_SV_CheckUserInfo* SV_CheckUserInfo() = 0; + virtual IRehldsHookRegistry_PF_precache_generic_I* PF_precache_generic_I() = 0; + virtual IRehldsHookRegistry_PF_precache_model_I* PF_precache_model_I() = 0; + virtual IRehldsHookRegistry_PF_precache_sound_I* PF_precache_sound_I() = 0; + virtual IRehldsHookRegistry_EV_Precache* EV_Precache() = 0; + virtual IRehldsHookRegistry_SV_AddResource* SV_AddResource() = 0; + virtual IRehldsHookRegistry_SV_ClientPrintf* SV_ClientPrintf() = 0; + virtual IRehldsHookRegistry_SV_AllowPhysent* SV_AllowPhysent() = 0; }; struct RehldsFuncs_t { diff --git a/rehlds/rehlds/rehlds_api_impl.cpp b/rehlds/rehlds/rehlds_api_impl.cpp index 1124e057e..8d7f9dad6 100644 --- a/rehlds/rehlds/rehlds_api_impl.cpp +++ b/rehlds/rehlds/rehlds_api_impl.cpp @@ -52,7 +52,9 @@ char* EXT_FUNC GetClientFallback_api() { } int* EXT_FUNC GetAllowCheats_api() { - return &allow_cheats; + static int sv_cheats_stub = 0; + Con_Printf("WARNING! allow_cheats marked as deprecated! Use sv_cheats cvar directly!\n"); + return &sv_cheats_stub; } bool EXT_FUNC GSBSecure_api() { @@ -851,6 +853,38 @@ IRehldsHookRegistry_Con_Printf* CRehldsHookchains::Con_Printf() { return &m_Con_Printf; } +IRehldsHookRegistry_SV_CheckUserInfo* CRehldsHookchains::SV_CheckUserInfo() { + return &m_SV_CheckUserInfo; +} + +IRehldsHookRegistry_PF_precache_generic_I* CRehldsHookchains::PF_precache_generic_I() { + return &m_PF_precache_generic_I; +} + +IRehldsHookRegistry_PF_precache_model_I* CRehldsHookchains::PF_precache_model_I() { + return &m_PF_precache_model_I; +} + +IRehldsHookRegistry_PF_precache_sound_I* CRehldsHookchains::PF_precache_sound_I() { + return &m_PF_precache_sound_I; +} + +IRehldsHookRegistry_EV_Precache* CRehldsHookchains::EV_Precache() { + return &m_EV_Precache; +} + +IRehldsHookRegistry_SV_AddResource* CRehldsHookchains::SV_AddResource(){ + return &m_SV_AddResource; +} + +IRehldsHookRegistry_SV_ClientPrintf* CRehldsHookchains::SV_ClientPrintf(){ + return &m_SV_ClientPrintf; +} + +IRehldsHookRegistry_SV_AllowPhysent* CRehldsHookchains::SV_AllowPhysent() { + return &m_SV_AllowPhysent; +} + int EXT_FUNC CRehldsApi::GetMajorVersion() { return REHLDS_API_VERSION_MAJOR; diff --git a/rehlds/rehlds/rehlds_api_impl.h b/rehlds/rehlds/rehlds_api_impl.h index 19929c9e5..699bd1649 100644 --- a/rehlds/rehlds/rehlds_api_impl.h +++ b/rehlds/rehlds/rehlds_api_impl.h @@ -222,6 +222,38 @@ typedef IVoidHookChainRegistryImpl CRehldsHookRegistry_ED_Free; typedef IHookChainImpl CRehldsHook_Con_Printf; typedef IHookChainRegistryImpl CRehldsHookRegistry_Con_Printf; +//SV_CheckUserInfo hook +typedef IHookChainImpl CRehldsHook_SV_CheckUserInfo; +typedef IHookChainRegistryImpl CRehldsHookRegistry_SV_CheckUserInfo; + +//PF_precache_generic_I hook +typedef IHookChainImpl CRehldsHook_PF_precache_generic_I; +typedef IHookChainRegistryImpl CRehldsHookRegistry_PF_precache_generic_I; + +//PF_precache_model_I hook +typedef IHookChainImpl CRehldsHook_PF_precache_model_I; +typedef IHookChainRegistryImpl CRehldsHookRegistry_PF_precache_model_I; + +//PF_precache_sound_I hook +typedef IHookChainImpl CRehldsHook_PF_precache_sound_I; +typedef IHookChainRegistryImpl CRehldsHookRegistry_PF_precache_sound_I; + +//EV_Precache hook +typedef IHookChainImpl CRehldsHook_EV_Precache; +typedef IHookChainRegistryImpl CRehldsHookRegistry_EV_Precache; + +//SV_AddResource hook +typedef IVoidHookChainImpl CRehldsHook_SV_AddResource; +typedef IVoidHookChainRegistryImpl CRehldsHookRegistry_SV_AddResource; + +//SV_ClientPrintf hook +typedef IVoidHookChainImpl CRehldsHook_SV_ClientPrintf; +typedef IVoidHookChainRegistryImpl CRehldsHookRegistry_SV_ClientPrintf; + +//SV_AllowPhysent hook +typedef IHookChainImpl CRehldsHook_SV_AllowPhysent; +typedef IHookChainRegistryImpl CRehldsHookRegistry_SV_AllowPhysent; + class CRehldsHookchains : public IRehldsHookchains { public: CRehldsHookRegistry_Steam_NotifyClientConnect m_Steam_NotifyClientConnect; @@ -271,6 +303,14 @@ class CRehldsHookchains : public IRehldsHookchains { CRehldsHookRegistry_ED_Alloc m_ED_Alloc; CRehldsHookRegistry_ED_Free m_ED_Free; CRehldsHookRegistry_Con_Printf m_Con_Printf; + CRehldsHookRegistry_SV_CheckUserInfo m_SV_CheckUserInfo; + CRehldsHookRegistry_PF_precache_generic_I m_PF_precache_generic_I; + CRehldsHookRegistry_PF_precache_model_I m_PF_precache_model_I; + CRehldsHookRegistry_PF_precache_sound_I m_PF_precache_sound_I; + CRehldsHookRegistry_EV_Precache m_EV_Precache; + CRehldsHookRegistry_SV_AddResource m_SV_AddResource; + CRehldsHookRegistry_SV_ClientPrintf m_SV_ClientPrintf; + CRehldsHookRegistry_SV_AllowPhysent m_SV_AllowPhysent; public: EXT_FUNC virtual IRehldsHookRegistry_Steam_NotifyClientConnect* Steam_NotifyClientConnect(); @@ -320,6 +360,14 @@ class CRehldsHookchains : public IRehldsHookchains { EXT_FUNC virtual IRehldsHookRegistry_ED_Alloc* ED_Alloc(); EXT_FUNC virtual IRehldsHookRegistry_ED_Free* ED_Free(); EXT_FUNC virtual IRehldsHookRegistry_Con_Printf* Con_Printf(); + EXT_FUNC virtual IRehldsHookRegistry_SV_CheckUserInfo* SV_CheckUserInfo(); + EXT_FUNC virtual IRehldsHookRegistry_PF_precache_generic_I* PF_precache_generic_I(); + EXT_FUNC virtual IRehldsHookRegistry_PF_precache_model_I* PF_precache_model_I(); + EXT_FUNC virtual IRehldsHookRegistry_PF_precache_sound_I* PF_precache_sound_I(); + EXT_FUNC virtual IRehldsHookRegistry_EV_Precache* EV_Precache(); + EXT_FUNC virtual IRehldsHookRegistry_SV_AddResource* SV_AddResource(); + EXT_FUNC virtual IRehldsHookRegistry_SV_ClientPrintf* SV_ClientPrintf(); + EXT_FUNC virtual IRehldsHookRegistry_SV_AllowPhysent* SV_AllowPhysent(); }; extern CRehldsHookchains g_RehldsHookchains; diff --git a/rehlds/version/version.h b/rehlds/version/version.h index 0b177c4c1..55a454b1e 100644 --- a/rehlds/version/version.h +++ b/rehlds/version/version.h @@ -6,5 +6,5 @@ #pragma once #define VERSION_MAJOR 3 -#define VERSION_MINOR 11 +#define VERSION_MINOR 13 #define VERSION_MAINTENANCE 0