diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5e9f100cc..29a64b6d7 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' @@ -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 52e965ce4..2f41d254a 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ReHLDS is a result of reverse engineering of original HLDS (build 6152/6153) usi Along with reverse engineering, a lot of defects and (potential) bugs were found and fixed -You can try play on one of the servers that using rehlds: [Game Tracker](http://www.gametracker.com/search/?search_by=server_variable&search_by2=sv_version) +You can try playing on one of many servers that are using ReHLDS: [Game Tracker](http://www.gametracker.com/search/?search_by=server_variable&search_by2=sv_version) ## Goals of the project 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..31560b71f 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; @@ -1269,3 +1282,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..3d6558c2b 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) { 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..e83cbb429 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); 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/pr_edict.cpp b/rehlds/engine/pr_edict.cpp index 0768a568b..2d380c536 100644 --- a/rehlds/engine/pr_edict.cpp +++ b/rehlds/engine/pr_edict.cpp @@ -37,6 +37,11 @@ void ED_ClearEdict(edict_t *e) } edict_t *ED_Alloc(void) +{ + return g_RehldsHookchains.m_ED_Alloc.callChain(ED_Alloc_internal); +} + +edict_t *EXT_FUNC ED_Alloc_internal(void) { int i; edict_t *e; @@ -71,6 +76,11 @@ edict_t *ED_Alloc(void) } void ED_Free(edict_t *ed) +{ + g_RehldsHookchains.m_ED_Free.callChain(ED_Free_internal, ed); +} + +void EXT_FUNC ED_Free_internal(edict_t *ed) { if (!ed->free) { diff --git a/rehlds/engine/pr_edict.h b/rehlds/engine/pr_edict.h index 0c6be5ee5..da69ecafa 100644 --- a/rehlds/engine/pr_edict.h +++ b/rehlds/engine/pr_edict.h @@ -35,7 +35,9 @@ void ED_ClearEdict(edict_t *e); edict_t *ED_Alloc(void); +edict_t *ED_Alloc_internal(void); void ED_Free(edict_t *ed); +void ED_Free_internal(edict_t *ed); NOXREF void ED_Count(void); char *ED_NewString(const char *string); char *ED_ParseEdict(char *data, edict_t *ent); 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 962a97599..24e251808 100644 --- a/rehlds/engine/server.h +++ b/rehlds/engine/server.h @@ -349,7 +349,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; @@ -407,7 +406,6 @@ enum GameType_e extern GameType_e g_eGameType; -extern int fatbytes; extern int giNextUserMsg; extern int hashstrings_collisions; @@ -420,10 +418,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]; @@ -500,6 +494,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); @@ -560,6 +555,7 @@ NOXREF qboolean SV_HasEventsInQueue(client_t *client); void SV_GetNetInfo(client_t *client, int *ping, int *packet_loss); int SV_CheckVisibility(edict_t *entity, unsigned char *pset); void SV_EmitPings(client_t *client, sizebuf_t *msg); +void SV_EmitPings_internal(client_t *client, sizebuf_t *msg); void SV_WriteEntitiesToClient(client_t *client, sizebuf_t *msg); void SV_CleanupEnts(void); qboolean SV_SendClientDatagram(client_t *client); @@ -570,6 +566,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 2c663f75c..c176ee8ed 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; @@ -130,6 +129,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 }; @@ -682,22 +687,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; } @@ -709,7 +714,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; } } @@ -1114,8 +1119,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); @@ -1165,7 +1180,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(); @@ -2117,6 +2132,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]; @@ -4027,9 +4047,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) { @@ -4071,6 +4092,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; @@ -4128,6 +4152,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; @@ -4568,7 +4595,16 @@ int EXT_FUNC SV_CheckVisibility(edict_t *entity, unsigned char *pset) } } -void SV_EmitPings(client_t *client, sizebuf_t *msg) +void EXT_FUNC SV_EmitPings_hook(IGameClient *cl, sizebuf_t *msg) +{ + SV_EmitPings_internal(cl->GetClient(), msg); +} + +void SV_EmitPings(client_t *client, sizebuf_t *msg) { + g_RehldsHookchains.m_SV_EmitPings.callChain(SV_EmitPings_hook, GetRehldsApiClient(client), msg); +} + +void EXT_FUNC SV_EmitPings_internal(client_t *client, sizebuf_t *msg) { int ping; int packet_loss; @@ -5113,7 +5149,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 @@ -6133,7 +6179,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); } @@ -6218,7 +6264,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; @@ -6537,6 +6582,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]; @@ -7975,6 +8056,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); @@ -7986,6 +8068,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 118250ec6..64e388eda 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -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/sys_dll.cpp b/rehlds/engine/sys_dll.cpp index fb2a5fba8..293119d22 100644 --- a/rehlds/engine/sys_dll.cpp +++ b/rehlds/engine/sys_dll.cpp @@ -1318,7 +1318,7 @@ void Con_DebugLog(const char *file, const char *fmt, ...) #endif // _WIN32 } -void EXT_FUNC Con_Printf(const char *fmt, ...) +void Con_Printf(const char *fmt, ...) { char Dest[4096]; va_list va; @@ -1326,7 +1326,12 @@ void EXT_FUNC Con_Printf(const char *fmt, ...) va_start(va, fmt); Q_vsnprintf(Dest, sizeof(Dest), fmt, va); va_end(va); + + g_RehldsHookchains.m_Con_Printf.callChain(Con_Printf_internal, Dest); +} +void EXT_FUNC Con_Printf_internal(const char *Dest) +{ #ifdef REHLDS_FLIGHT_REC FR_Log("REHLDS_CON", Dest); #endif diff --git a/rehlds/engine/sys_dll.h b/rehlds/engine/sys_dll.h index 228b09aed..adab1551c 100644 --- a/rehlds/engine/sys_dll.h +++ b/rehlds/engine/sys_dll.h @@ -135,5 +135,6 @@ void Con_Debug_f(void); void Con_Init(void); void Con_DebugLog(const char *file, const char *fmt, ...); void Con_Printf(const char *fmt, ...); +void Con_Printf_internal(const char *Dest); void Con_SafePrintf(const char *fmt, ...); void Con_DPrintf(const char *fmt, ...); 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 74ccc3346..8038ab755 100644 --- a/rehlds/engine/world.cpp +++ b/rehlds/engine/world.cpp @@ -238,7 +238,8 @@ hull_t *SV_HullForEntity(edict_t *ent, const vec_t *mins, const vec_t *maxs, vec // explicit hulls in the BSP model if (ent->v.movetype != MOVETYPE_PUSH && ent->v.movetype != MOVETYPE_PUSHSTEP) { - Sys_Error("%s: SOLID_BSP without MOVETYPE_PUSH", __func__); + Sys_Error("%s: SOLID_BSP without MOVETYPE_PUSH\nEntity classname = %s, model = %s", + __func__, STRING(ent->v.classname), STRING(ent->v.model)); } return SV_HullForBsp(ent, mins, maxs, offset); @@ -358,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 @@ -646,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; @@ -1189,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) @@ -1213,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 @@ -1247,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); @@ -1297,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/progs.h b/rehlds/public/rehlds/progs.h index 808ad3014..5ea6cf8f0 100644 --- a/rehlds/public/rehlds/progs.h +++ b/rehlds/public/rehlds/progs.h @@ -68,6 +68,10 @@ struct event_state_s extern char *pr_strings; extern globalvars_t gGlobalVariables; +#if !defined(STRING) && defined(SWDS) +#define STRING(offset) ((const char *)(pr_strings + (unsigned int)(offset))) +#endif + //============================================================================ edict_t *ED_Alloc (void); diff --git a/rehlds/public/rehlds/rehlds_api.h b/rehlds/public/rehlds/rehlds_api.h index 4d0a74ede..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 10 +#define REHLDS_API_VERSION_MINOR 13 //Steam_NotifyClientConnect hook typedef IHookChain IRehldsHook_Steam_NotifyClientConnect; @@ -211,6 +211,53 @@ typedef IHookChainRegistry IRehldsHookRegistry_SV_Sho typedef IHookChain IRehldsHook_GetEntityInit; typedef IHookChainRegistry IRehldsHookRegistry_GetEntityInit; +//SV_EmitPings hook +typedef IHookChain IRehldsHook_SV_EmitPings; +typedef IHookChainRegistry IRehldsHookRegistry_SV_EmitPings; + +//ED_Alloc hook +typedef IHookChain IRehldsHook_ED_Alloc; +typedef IHookChainRegistry IRehldsHookRegistry_ED_Alloc; + +//ED_Free hook +typedef IVoidHookChain IRehldsHook_ED_Free; +typedef IVoidHookChainRegistry IRehldsHookRegistry_ED_Free; + +//Con_Printf hook +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: @@ -259,6 +306,18 @@ class IRehldsHookchains { virtual IRehldsHookRegistry_SV_Frame* SV_Frame() = 0; virtual IRehldsHookRegistry_SV_ShouldSendConsistencyList* SV_ShouldSendConsistencyList() = 0; virtual IRehldsHookRegistry_GetEntityInit* GetEntityInit() = 0; + virtual IRehldsHookRegistry_SV_EmitPings* SV_EmitPings() = 0; + 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 7cfe15e8a..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() { @@ -835,6 +837,54 @@ IRehldsHookRegistry_GetEntityInit* CRehldsHookchains::GetEntityInit() { return &m_GetEntityInit; } +IRehldsHookRegistry_SV_EmitPings* CRehldsHookchains::SV_EmitPings() { + return &m_SV_EmitPings; +} + +IRehldsHookRegistry_ED_Alloc* CRehldsHookchains::ED_Alloc() { + return &m_ED_Alloc; +} + +IRehldsHookRegistry_ED_Free* CRehldsHookchains::ED_Free() { + return &m_ED_Free; +} + +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 835a24b1d..699bd1649 100644 --- a/rehlds/rehlds/rehlds_api_impl.h +++ b/rehlds/rehlds/rehlds_api_impl.h @@ -206,6 +206,54 @@ typedef IHookChainRegistryImpl CRehldsHookRegistry_SV typedef IHookChainImpl CRehldsHook_GetEntityInit; typedef IHookChainRegistryImpl CRehldsHookRegistry_GetEntityInit; +//SV_EmitPings hook +typedef IHookChainImpl CRehldsHook_SV_EmitPings; +typedef IHookChainRegistryImpl CRehldsHookRegistry_SV_EmitPings; + +//ED_Alloc hook +typedef IHookChainImpl CRehldsHook_ED_Alloc; +typedef IHookChainRegistryImpl CRehldsHookRegistry_ED_Alloc; + +//ED_Free hook +typedef IVoidHookChainImpl CRehldsHook_ED_Free; +typedef IVoidHookChainRegistryImpl CRehldsHookRegistry_ED_Free; + +//Con_Printf hook +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; @@ -251,6 +299,18 @@ class CRehldsHookchains : public IRehldsHookchains { CRehldsHookRegistry_SV_Frame m_SV_Frame; CRehldsHookRegistry_SV_ShouldSendConsistencyList m_SV_ShouldSendConsistencyList; CRehldsHookRegistry_GetEntityInit m_GetEntityInit; + CRehldsHookRegistry_SV_EmitPings m_SV_EmitPings; + 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(); @@ -296,6 +356,18 @@ class CRehldsHookchains : public IRehldsHookchains { EXT_FUNC virtual IRehldsHookRegistry_SV_Frame* SV_Frame(); EXT_FUNC virtual IRehldsHookRegistry_SV_ShouldSendConsistencyList* SV_ShouldSendConsistencyList(); EXT_FUNC virtual IRehldsHookRegistry_GetEntityInit* GetEntityInit(); + EXT_FUNC virtual IRehldsHookRegistry_SV_EmitPings* SV_EmitPings(); + 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 0a1f0952e..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 10 +#define VERSION_MINOR 13 #define VERSION_MAINTENANCE 0