diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ddedaf4a3..d76a2c63f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 2f41d254a..15439cde3 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,16 @@ You can try playing on one of many servers that are using ReHLDS: [Game Tracker] ## How can use it? -ReHLDS is fully compatible with latest official HLDS downloaded by steamcmd. All you have to do is to download rehlds binaries and replace original swds.dll/engine_i486.so. For windows you can also copy a swds.pdb file with a debug information. -
Warning! ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool. +ReHLDS is fully compatible with the official pre-anniversary edition of HLDS (engine version <= 8684) downloaded by steamcmd. All you have to do is to download rehlds binaries and replace original swds.dll/engine_i486.so. For windows you can also copy a swds.pdb file with a debug information. + +Warning! ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool. + +#### Downloading HLDS via steamcmd + +``` +app_set_config 90 mod cstrike +app_update 90 -beta steam_legacy validate +``` ## Downloads * [Release builds](https://github.com/dreamstalker/rehlds/releases) @@ -31,20 +39,20 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
Click to expand
@@ -62,6 +71,9 @@ This means that plugins that do binary code analysis (Orpheu for example) probab ## Build instructions 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/cmd.cpp b/rehlds/engine/cmd.cpp index 7dcf76c43..a49df84ae 100644 --- a/rehlds/engine/cmd.cpp +++ b/rehlds/engine/cmd.cpp @@ -235,7 +235,8 @@ void Cbuf_Execute(void) } // execute the command line - Cmd_ExecuteString(line, src_command); + if (line[0]) + Cmd_ExecuteString(line, src_command); if (cmd_wait) { @@ -406,17 +407,12 @@ void Cmd_Exec_f(void) else { char *pszDataPtr = configContents; - while (true) + while (pszDataPtr && *pszDataPtr) { Cbuf_Execute(); // TODO: This doesn't obey the rule to first execute commands from the file, and then the others in the buffer pszDataPtr = COM_ParseLine(pszDataPtr); // TODO: COM_ParseLine can be const char* - - if (com_token[0] == 0) - { - break; - } - - Cbuf_InsertTextLines(com_token); + if (com_token[0]) + Cbuf_InsertTextLines(com_token); } } diff --git a/rehlds/engine/common.cpp b/rehlds/engine/common.cpp index badf9931b..3ce8aa47a 100644 --- a/rehlds/engine/common.cpp +++ b/rehlds/engine/common.cpp @@ -586,9 +586,9 @@ void MSG_WriteBitData(void *src, int length) void MSG_WriteBitAngle(float fAngle, int numbits) { - if (numbits >= 32) + if (numbits > 22) { - Sys_Error("%s: Can't write bit angle with 32 bits precision\n", __func__); + Sys_Error("%s: Can't write bit angle with more than 22 bits precision\n", __func__); } uint32 shift = (1 << numbits); @@ -1978,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; diff --git a/rehlds/engine/common.h b/rehlds/engine/common.h index 1a7fc081a..94d940480 100644 --- a/rehlds/engine/common.h +++ b/rehlds/engine/common.h @@ -187,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/filter.h b/rehlds/engine/filter.h index b2a080be6..84c7afe37 100644 --- a/rehlds/engine/filter.h +++ b/rehlds/engine/filter.h @@ -42,9 +42,7 @@ typedef struct ipfilter_s } compare; float banEndTime; float banTime; -#ifdef REHLDS_FIXES int cidr; -#endif // REHLDS_FIXES } ipfilter_t; typedef struct userfilter_s diff --git a/rehlds/engine/host.cpp b/rehlds/engine/host.cpp index 3416efdf7..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); @@ -355,7 +362,7 @@ void SV_ClientPrintf(const char *fmt, ...) { va_list va; char string[1024]; - + va_start(va, fmt); Q_vsnprintf(string, ARRAYSIZE(string) - 1, fmt, va); va_end(va); @@ -367,7 +374,7 @@ void SV_ClientPrintf(const char *fmt, ...) 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); @@ -1190,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(); @@ -1275,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_cmd.cpp b/rehlds/engine/host_cmd.cpp index d76815cb8..79996ab33 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) { @@ -602,7 +610,6 @@ void Host_Status_f(void) Host_Status_Printf(conprint, log, "players : %i active (%i max)\n\n", nClients, g_psvs.maxclients); Host_Status_Printf(conprint, log, "# name userid uniqueid frag time ping loss adr\n"); - int count = 1; client = g_psvs.clients; for (j = 0; j < g_psvs.maxclients; j++, client++) { @@ -626,7 +633,7 @@ void Host_Status_f(void) val = SV_GetClientIDString(client); else val = "BOT"; - Host_Status_Printf(conprint, log, "#%2i %8s %i %s", count++, va("\"%s\"", client->name), client->userid, val); + Host_Status_Printf(conprint, log, "#%2i %8s %i %s", j + 1, va("\"%s\"", client->name), client->userid, val); if (client->proxy) { const char *userInfo = Info_ValueForKey(client->userinfo, "hspecs"); @@ -716,7 +723,6 @@ void Host_Status_Formatted_f(void) Host_Status_Printf(conprint, log, "players : %i active (%i max)\n\n", nClients, g_psvs.maxclients); Host_Status_Printf(conprint, log, "%-2.2s\t%-9.9s\t%-7.7s\t%-20.20s\t%-4.4s\t%-8.8s\t%-4.4s\t%-4.4s\t%-21.21s\n","# ","name","userid ","uniqueid ","frag","time ","ping","loss","adr"); - int count = 1; char *szRemoteAddr; client = g_psvs.clients; for (j = 0; j < g_psvs.maxclients; j++, client++) @@ -747,12 +753,117 @@ void Host_Status_Formatted_f(void) #endif // REHLDS_FIXES szIDString = SV_GetClientIDString(client); Host_Status_Printf(conprint, log, "%-2.2s\t%-9.9s\t%-7.7s\t%-20.20s\t%-4.4s\t%-8.8s\t%-4.4s\t%-4.4s\t%-21.21s\n", - va("%-2i", count++),va("\"%s\"", client->name),va("%-7i", client->userid),szIDString, + va("%-2i", j + 1),va("\"%s\"", client->name),va("%-7i", client->userid),szIDString, va("%-4i", (int)client->edict->v.frags),sz,va("%-4i", SV_CalcPing(client)),va("%-4i", (int)client->packet_loss),szRemoteAddr); } 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 +3249,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/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 e1d956a7e..dfd284683 100644 --- a/rehlds/engine/pr_cmds.cpp +++ b/rehlds/engine/pr_cmds.cpp @@ -1447,6 +1447,9 @@ int EXT_FUNC PF_precache_model_I_internal(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)) @@ -1545,7 +1548,7 @@ int EXT_FUNC PF_precache_generic_I_internal(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); @@ -1799,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/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 4bdd7efb6..c59332e16 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; @@ -366,6 +366,7 @@ extern cvar_t sv_visiblemaxplayers; extern cvar_t sv_downloadurl; extern cvar_t sv_allow_dlfile; extern cvar_t sv_version; +extern cvar_t sv_tags; #ifdef REHLDS_FIXES extern cvar_t sv_echo_unknown_cmd; extern cvar_t sv_auto_precache_sounds_in_models; @@ -587,6 +588,9 @@ void SV_ClearEntities(void); int RegUserMsg(const char *pszName, int iSize); qboolean StringToFilter(const char *s, ipfilter_t *f); USERID_t *SV_StringToUserID(const char *str); +bool CanBeWrittenWithoutCIDR(const ipfilter_t &f); +void FilterToString(const ipfilter_t &f, char *s); +bool IsFilterIncludesAnotherFilter(const ipfilter_t &f, const ipfilter_t &f2); void SV_BanId_f(void); void Host_Kick_f(void); void SV_RemoveId_f(void); diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 83a3e3cf7..870a5fabb 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 }; @@ -187,6 +193,8 @@ cvar_t sv_version = { "sv_version", "", FCVAR_SERVER, 0.0f, NULL }; cvar_t sv_version = {"sv_version", "", 0, 0.0f, NULL}; #endif +cvar_t sv_tags = { "sv_tags", "", 0, 0.0f, NULL }; + cvar_t sv_rcon_minfailures = { "sv_rcon_minfailures", "5", 0, 0.0f, NULL }; cvar_t sv_rcon_maxfailures = { "sv_rcon_maxfailures", "10", 0, 0.0f, NULL }; cvar_t sv_rcon_minfailuretime = { "sv_rcon_minfailuretime", "30", 0, 0.0f, NULL }; @@ -680,22 +688,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; } @@ -707,7 +715,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; } } @@ -1112,8 +1120,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); @@ -1163,7 +1181,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(); @@ -3258,6 +3276,159 @@ void SV_ResetRcon_f(void) Q_memset(g_rgRconFailures, 0, sizeof(g_rgRconFailures)); } +const int MAX_RCON_USERS = 128; +ipfilter_t rconusers[MAX_RCON_USERS]; +int numrconusers = 0; + +qboolean SV_CheckRconAllowed(const netadr_t *adr) +{ + if (numrconusers <= 0) + return TRUE; // Rcon user list empty so assume allowed it for all + + for (int i = numrconusers - 1; i >= 0; i--) + { + ipfilter_t *curFilter = &rconusers[i]; + if (curFilter->compare.u32 == 0xFFFFFFFF || (*(uint32*)adr->ip & curFilter->mask) == curFilter->compare.u32) + return TRUE; + } + + return FALSE; +} + +void SV_RconAddUser_f(void) +{ + if (Cmd_Argc() != 2) + { + Con_Printf("Usage: rcon_adduser \n" + "ipaddress A.B.C.D/24 is equivalent to A.B.C.0 and A.B.C\n"); + return; + } + + ipfilter_t tempFilter; + if (!StringToFilter(Cmd_Argv(1), &tempFilter)) + { + Con_Printf("Invalid IP address!\nUsage: rcon_adduser \n"); + return; + } + + int i = 0; + for (; i < numrconusers; i++) + { + if (rconusers[i].mask == tempFilter.mask && rconusers[i].compare.u32 == tempFilter.compare.u32) + { + rconusers[i].cidr = tempFilter.cidr; + return; + } + } + + if (numrconusers >= MAX_RCON_USERS) + { + Con_Printf("IP rcon users is full\n"); + return; + } + + numrconusers++; + rconusers[i].compare = tempFilter.compare; + rconusers[i].mask = tempFilter.mask; + rconusers[i].cidr = tempFilter.cidr; +} + +void SV_RconDelUser_f(void) +{ + int argCount = Cmd_Argc(); + if (argCount != 2 && argCount != 3) + { + Con_Printf("Usage: rcon_deluser {removeAll}\n" + "removeip {removeAll}\n" + "Use removeAll to delete all Rcon ip users which ipaddress or ipaddress/CIDR includes\n"); + + return; + } + + ipfilter_t f; + + if (!StringToFilter(Cmd_Argv(1), &f)) + { + Con_Printf("Invalid IP address\n" + "Usage: rcon_deluser {removeAll}\n" + " rcon_deluser {removeAll}\n" + "Use removeAll to delete all Rcon ip users which ipaddress or ipaddress/CIDR includes\n"); + return; + } + + bool found = false; + for (int i = 0; i < numrconusers; i++) + { + if ((argCount == 2 && rconusers[i].mask == f.mask && rconusers[i].compare.u32 == f.compare.u32) || + (argCount == 3 && IsFilterIncludesAnotherFilter(f, rconusers[i]))) + { + if (i + 1 < numrconusers) + Q_memmove(&rconusers[i], &rconusers[i + 1], (numrconusers - (i + 1)) * sizeof(ipfilter_t)); + numrconusers--; + rconusers[numrconusers].banTime = 0.0f; + rconusers[numrconusers].banEndTime = 0.0f; + rconusers[numrconusers].compare.u32 = 0; + rconusers[numrconusers].mask = 0; + found = true; + --i; + + if (argCount == 2) + break; + } + } + + if (found) + Con_Printf("Rcon user IP removed.\n"); + + else + { + Con_Printf("rcon_deluser: couldn't find %s.\n", Cmd_Argv(1)); + } +} + +void SV_RconUsers_f(void) +{ + if (numrconusers <= 0) + { + Con_Printf("Rcon user IP list: empty\n"); + return; + } + + bool isNew = Cmd_Argc() == 2; + bool searchByFilter = isNew && isdigit(Cmd_Argv(1)[0]); + ipfilter_t filter; + + if (searchByFilter) + { + if (!StringToFilter(Cmd_Argv(1), &filter)) + return; + + Con_Printf("Rcon user IP list for %s:\n", Cmd_Argv(1)); + } + else + { + Con_Printf("Rcon user IP list:\n"); + } + + for (int i = 0; i < numrconusers; i++) + { + uint8 *b = rconusers[i].compare.octets; + if (isNew) + { + if (!searchByFilter || IsFilterIncludesAnotherFilter(filter, rconusers[i])) + { + char strFilter[32]; + FilterToString(rconusers[i], strFilter); + Con_Printf("%-18s\n", strFilter); + } + } + else if (CanBeWrittenWithoutCIDR(rconusers[i])) + { + Con_Printf("%3i.%3i.%3i.%3i\n", b[0], b[1], b[2], b[3]); + } + } +} + void SV_AddFailedRcon(netadr_t *adr) { int i; @@ -3386,10 +3557,21 @@ qboolean SV_CheckRconFailure(netadr_t *adr) return FALSE; } +#define RCON_RESULT_SUCCESS 0 // allow the rcon +#define RCON_RESULT_BADPASSWORD 1 // reject it, bad password +#define RCON_RESULT_BADCHALLENGE 2 // bad challenge +#define RCON_RESULT_BANNING 3 // decline it, banning for rcon hacking attempts +#define RCON_RESULT_NOSETPASSWORD 4 // rcon password is not set +#define RCON_RESULT_NOPRIVILEGE 5 // user attempt with valid password but is not privileged + int SV_Rcon_Validate(void) { - if (Cmd_Argc() < 3 || Q_strlen(rcon_password.string) == 0) - return 1; + if (Cmd_Argc() < 3) + return RCON_RESULT_BADPASSWORD; + + // Must have a password set to allow any rconning + if (Q_strlen(rcon_password.string) == 0) + return RCON_RESULT_NOSETPASSWORD; if (sv_rcon_banpenalty.value < 0.0f) Cvar_SetValue("sv_rcon_banpenalty", 0.0); @@ -3398,78 +3580,105 @@ int SV_Rcon_Validate(void) { Con_Printf("Banning %s for rcon hacking attempts\n", NET_AdrToString(net_from)); Cbuf_AddText(va("addip %i %s\n", (int)sv_rcon_banpenalty.value, NET_BaseAdrToString(net_from))); - return 3; + return RCON_RESULT_BANNING; } if (!SV_CheckChallenge(&net_from, Q_atoi(Cmd_Argv(1)))) - return 2; + return RCON_RESULT_BADCHALLENGE; // The client is spoofing... + // If the pw does not match, then disallow command if (Q_strcmp(Cmd_Argv(2), rcon_password.string)) { SV_AddFailedRcon(&net_from); - return 1; + return RCON_RESULT_BADPASSWORD; } - return 0; + + if (!SV_CheckRconAllowed(&net_from)) + { + Con_Printf("Banning %s for rcon attempts without privileged\n", NET_AdrToString(net_from)); + Cbuf_AddText(va("addip %i %s\n", (int)sv_rcon_banpenalty.value, NET_BaseAdrToString(net_from))); + return RCON_RESULT_NOPRIVILEGE; + } + + // Otherwise it's ok + return RCON_RESULT_SUCCESS; } +// A client issued an rcom command +// Shift down the remaining args and redirect all Con_Printf void SV_Rcon(netadr_t *net_from_) { - char remaining[512]; - char rcon_buff[1024]; + int invalid; + char remaining[1024]; + char rcon_buff[512]; + int len; - int invalid = SV_Rcon_Validate(); - int len = net_message.cursize - Q_strlen("rcon"); - if (len <= 0 || len >= sizeof(remaining)) + // Verify this user has access rights + invalid = SV_Rcon_Validate(); + + len = net_message.cursize - Q_strlen("rcon"); + if (len <= 0 || len >= sizeof(rcon_buff)) return; - Q_memcpy(remaining, &net_message.data[Q_strlen("rcon")], len); - remaining[len] = 0; + Q_memcpy(rcon_buff, &net_message.data[Q_strlen("rcon")], len); + rcon_buff[len] = 0; #ifdef REHLDS_FIXES if (sv_rcon_condebug.value > 0.0f) #endif { - if (invalid) + if (invalid != RCON_RESULT_SUCCESS) { - Con_Printf("Bad Rcon from %s:\n%s\n", NET_AdrToString(*net_from_), remaining); - Log_Printf("Bad Rcon: \"%s\" from \"%s\"\n", remaining, NET_AdrToString(*net_from_)); + Con_Printf("Bad Rcon from %s:\n%s\n", NET_AdrToString(*net_from_), rcon_buff); + Log_Printf("Bad Rcon: \"%s\" from \"%s\"\n", rcon_buff, NET_AdrToString(*net_from_)); } else { - Con_Printf("Rcon from %s:\n%s\n", NET_AdrToString(*net_from_), remaining); - Log_Printf("Rcon: \"%s\" from \"%s\"\n", remaining, NET_AdrToString(*net_from_)); + Con_Printf("Rcon from %s:\n%s\n", NET_AdrToString(*net_from_), rcon_buff); + Log_Printf("Rcon: \"%s\" from \"%s\"\n", rcon_buff, NET_AdrToString(*net_from_)); } } SV_BeginRedirect(RD_PACKET, net_from_); - if (invalid) + switch (invalid) + { + case RCON_RESULT_SUCCESS: { - if (invalid == 2) - Con_Printf("Bad rcon_password.\n"); - else if (Q_strlen(rcon_password.string) == 0) - Con_Printf("Bad rcon_password.\nNo password set for this server.\n"); + char *data; + data = COM_Parse(rcon_buff); + data = COM_Parse(data); + data = COM_Parse(data); + + if (data) + { + Q_strncpy(remaining, data, sizeof(remaining) - 1); + remaining[sizeof(remaining) - 1] = 0; + + Cmd_ExecuteString(remaining, src_command); + } else - Con_Printf("Bad rcon_password.\n"); + { + Con_Printf("Empty rcon\n"); + } - SV_EndRedirect(); - return; + break; } - char *data = COM_Parse(COM_Parse(COM_Parse(remaining))); - if (!data) - { - Con_Printf("Empty rcon\n"); - -#ifdef REHLDS_FIXES - //missing SV_EndRedirect() - SV_EndRedirect(); -#endif // REHLDS_FIXES - return; + case RCON_RESULT_BANNING: + case RCON_RESULT_BADPASSWORD: + Con_Printf("Bad rcon_password.\n"); + break; + case RCON_RESULT_NOPRIVILEGE: + Con_Printf("Bad rcon_password.\nNo privilege.\n"); + break; + case RCON_RESULT_NOSETPASSWORD: + Con_Printf("Bad rcon_password.\nNo password set for this server.\n"); + break; + case RCON_RESULT_BADCHALLENGE: + Con_Printf("Bad rcon_password.\nBad challenge.\n"); + break; } - Q_strncpy(rcon_buff, data, sizeof(rcon_buff) - 1); - rcon_buff[sizeof(rcon_buff) - 1] = 0; - Cmd_ExecuteString(rcon_buff, src_command); SV_EndRedirect(); } @@ -4682,16 +4891,38 @@ void SV_WriteEntitiesToClient(client_t *client, sizebuf_t *msg) auto &entityState = curPack->entities[i]; if (entityState.number > MAX_CLIENTS) { - if (sv_rehlds_attachedentities_playeranimationspeed_fix.string[0] == '1' - && entityState.movetype == MOVETYPE_FOLLOW - && 1 <= entityState.aiment && entityState.aiment <= MAX_CLIENTS) + if (entityState.movetype == MOVETYPE_FOLLOW) { - attachedEntCount[entityState.aiment]++; + if (entityState.aiment > 0 && entityState.aiment < g_psv.num_edicts) + { + if (sv_rehlds_attachedentities_playeranimationspeed_fix.string[0] == '1' && + entityState.aiment <= MAX_CLIENTS) + { + attachedEntCount[entityState.aiment]++; + } + + // Prevent crash "Cache_UnlinkLRU: NULL link" on client-side + // if aiment with sprite model will be to render as a studio model + edict_t *ent = &g_psv.edicts[entityState.aiment]; + if (ent->v.modelindex >= 0 && ent->v.modelindex < MAX_MODELS + && (!g_psv.models[ent->v.modelindex] + || g_psv.models[ent->v.modelindex]->type != mod_studio)) + { + entityState.aiment = 0; + entityState.movetype = MOVETYPE_NONE; + } + } + else + { + entityState.aiment = 0; + entityState.movetype = MOVETYPE_NONE; + } } // Prevent spam "Non-sprite set to glow!" in console on client-side if (entityState.rendermode == kRenderGlow && (entityState.modelindex >= 0 && entityState.modelindex < MAX_MODELS) + && g_psv.models[entityState.modelindex] && g_psv.models[entityState.modelindex]->type != mod_sprite) { entityState.rendermode = kRenderNormal; @@ -6247,7 +6478,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; @@ -6341,7 +6571,6 @@ int EXT_FUNC RegUserMsg(const char *pszName, int iSize) return pNewMsg->iMsg; } -#ifdef REHLDS_FIXES uint32_t CIDRToMask(int cidr) { return htonl(0xFFFFFFFFull << (32 - cidr)); @@ -6491,44 +6720,6 @@ qboolean StringToFilter(const char *s, ipfilter_t *f) return true; } -#else // REHLDS_FIXES -qboolean StringToFilter(const char *s, ipfilter_t *f) -{ - char num[128]; - unsigned char b[4] = { 0, 0, 0, 0 }; - unsigned char m[4] = { 0, 0, 0, 0 }; - - const char* cc = s; - int i = 0; - while (1) - { - if (*cc < '0' || *cc > '9') - break; - - int j = 0; - while (*cc >= '0' && *cc <= '9') - num[j++] = *(cc++); - - num[j] = 0; - b[i] = Q_atoi(num); - if (b[i]) - m[i] = -1; - - if (*cc) - { - ++cc; - ++i; - if (i < 4) - continue; - } - f->mask = *(uint32 *)m; - f->compare.u32 = *(uint32 *)b; - return TRUE; - } - Con_Printf("Bad filter address: %s\n", cc); - return FALSE; -} -#endif // REHLDS_FIXES USERID_t *SV_StringToUserID(const char *str) { @@ -6566,6 +6757,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]; @@ -7930,6 +8157,11 @@ void SV_Init(void) Cmd_AddCommand("listid", SV_ListId_f); Cmd_AddCommand("writeid", SV_WriteId_f); Cmd_AddCommand("resetrcon", SV_ResetRcon_f); +#ifdef REHLDS_FIXES + Cmd_AddCommand("rcon_adduser", SV_RconAddUser_f); + Cmd_AddCommand("rcon_deluser", SV_RconDelUser_f); + Cmd_AddCommand("rcon_users", SV_RconUsers_f); +#endif Cmd_AddCommand("logaddress", SV_SetLogAddress_f); Cmd_AddCommand("logaddress_add", SV_AddLogAddress_f); Cmd_AddCommand("logaddress_del", SV_DelLogAddress_f); @@ -7974,6 +8206,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); @@ -8004,6 +8239,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); @@ -8015,6 +8251,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); @@ -8039,6 +8276,7 @@ void SV_Init(void) Cvar_RegisterVariable(&sv_version); Cvar_RegisterVariable(&sv_allow_dlfile); #ifdef REHLDS_FIXES + Cvar_RegisterVariable(&sv_tags); Cvar_RegisterVariable(&sv_force_ent_intersection); Cvar_RegisterVariable(&sv_echo_unknown_cmd); Cvar_RegisterVariable(&sv_auto_precache_sounds_in_models); diff --git a/rehlds/engine/sv_steam3.cpp b/rehlds/engine/sv_steam3.cpp index ad288fa57..c35b29343 100644 --- a/rehlds/engine/sv_steam3.cpp +++ b/rehlds/engine/sv_steam3.cpp @@ -232,6 +232,10 @@ CSteam3Server::CSteam3Server() : m_CallbackLogonFailure(this, &CSteam3Server::OnLogonFailure), m_SteamIDGS(1, 0, k_EUniverseInvalid, k_EAccountTypeInvalid) { +#ifdef REHLDS_FIXES + m_GameTagsData[0] = '\0'; +#endif + m_bHasActivePlayers = false; m_bWantToBeSecure = false; m_bLanOnly = false; @@ -499,6 +503,21 @@ void CSteam3Server::RunFrame() } } +void CSteam3Server::UpdateGameTags() +{ +#ifdef REHLDS_FIXES + if (!m_GameTagsData[0] && !sv_tags.string[0]) + return; + + if (m_GameTagsData[0] && !Q_stricmp(m_GameTagsData, sv_tags.string)) + return; + + Q_strlcpy(m_GameTagsData, sv_tags.string); + Q_strlwr(m_GameTagsData); + CRehldsPlatformHolder::get()->SteamGameServer()->SetGameTags(m_GameTagsData); +#endif +} + void CSteam3Server::SendUpdatedServerDetails() { int botCount = 0; @@ -521,6 +540,8 @@ void CSteam3Server::SendUpdatedServerDetails() CRehldsPlatformHolder::get()->SteamGameServer()->SetBotPlayerCount(botCount); CRehldsPlatformHolder::get()->SteamGameServer()->SetServerName(Cvar_VariableString("hostname")); CRehldsPlatformHolder::get()->SteamGameServer()->SetMapName(g_psv.name); + + UpdateGameTags(); } void CSteam3Client::Shutdown() diff --git a/rehlds/engine/sv_steam3.h b/rehlds/engine/sv_steam3.h index 1aa2e51aa..3238dc87b 100644 --- a/rehlds/engine/sv_steam3.h +++ b/rehlds/engine/sv_steam3.h @@ -54,6 +54,8 @@ class CSteam3 bool InitModule(); }; +#define MAX_STEAM_TAGS_LENGTH 128 // Steam doesn't send tags string more than 128 bytes + class CSteam3Server: public CSteam3 { public: @@ -71,6 +73,10 @@ class CSteam3Server: public CSteam3 bool m_bLanOnly; CSteamID m_SteamIDGS; +#ifdef REHLDS_FIXES + char m_GameTagsData[MAX_STEAM_TAGS_LENGTH]; +#endif + public: NOBODY void SetServerType(); @@ -96,6 +102,7 @@ class CSteam3Server: public CSteam3 void NotifyOfLevelChange(bool bForce); void RunFrame(); void SendUpdatedServerDetails(); + void UpdateGameTags(); }; class CSteam3Client: public CSteam3 diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index 88242cdea..414e7704a 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 @@ -525,7 +525,6 @@ void SV_AddLinksToPM_(areanode_t *node, float *pmove_mins, float *pmove_maxs) edict_t *check; int e; physent_t *ve; - int i; link_t *next; float *fmax; float *fmin; @@ -588,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 293119d22..08431a490 100644 --- a/rehlds/engine/sys_dll.cpp +++ b/rehlds/engine/sys_dll.cpp @@ -1150,46 +1150,47 @@ void EXT_FUNC EngineFprintf(void *pfile, const char *szFmt, ...) void EXT_FUNC AlertMessage(ALERT_TYPE atype, const char *szFmt, ...) { + char szOut[2048]; va_list argptr; - static char szOut[1024]; - va_start(argptr, szFmt); if (atype == at_logged && g_psvs.maxclients > 1) { + va_start(argptr, szFmt); Q_vsnprintf(szOut, sizeof(szOut), szFmt, argptr); + va_end(argptr); + Log_Printf("%s", szOut); + return; } - else if (developer.value != 0.0f) + + if (!developer.value) + return; + + if (atype == at_aiconsole && developer.value < 2) + return; + + va_start(argptr, szFmt); + Q_vsnprintf(szOut, sizeof(szOut), szFmt, argptr); + va_end(argptr); + + switch (atype) { - switch (atype) - { - case at_notice: - Q_strcpy(szOut, "NOTE: "); - break; - case at_console: - szOut[0] = 0; - break; - case at_aiconsole: - if (developer.value < 2.0f) - return; - szOut[0] = 0; - break; - case at_warning: - Q_strcpy(szOut, "WARNING: "); - break; - case at_error: - Q_strcpy(szOut, "ERROR: "); - break; - case at_logged: - break; - default: - break; - } - int iLen = Q_strlen(szOut); - Q_vsnprintf(&szOut[iLen], sizeof(szOut) - iLen, szFmt, argptr); + case at_notice: + Con_Printf("NOTE: %s", szOut); + break; + case at_console: + case at_aiconsole: Con_Printf("%s", szOut); + break; + case at_warning: + Con_Printf("WARNING: %s", szOut); + break; + case at_error: + Con_Printf("ERROR: %s", szOut); + break; + default: + break; } - va_end(argptr); } NOXREF void Sys_SplitPath(const char *path, char *drive, char *dir, char *fname, char *ext) @@ -1326,7 +1327,7 @@ void 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); } diff --git a/rehlds/engine/textures.cpp b/rehlds/engine/textures.cpp index 17b65b3ad..4c72dfd55 100644 --- a/rehlds/engine/textures.cpp +++ b/rehlds/engine/textures.cpp @@ -109,10 +109,10 @@ qboolean TEX_InitFromWad(char *path) #endif // REHLDS_FIXES texfile = FS_Open(wadPath, "rb"); - texfiles[nTexFiles++] = texfile; if (!texfile) Sys_Error("%s: couldn't open %s\n", __func__, wadPath); + texfiles[nTexFiles++] = texfile; Con_DPrintf("Using WAD File: %s\n", wadPath); SafeRead(texfile, &header, 12); if (Q_strncmp(header.identification, "WAD2", 4) && Q_strncmp(header.identification, "WAD3", 4)) @@ -147,8 +147,9 @@ void TEX_CleanupWadInfo(void) for (int i = 0; i < nTexFiles; i++) { - FS_Close(texfiles[i]); - texfiles[i] = 0; + if (texfiles[i]) + FS_Close(texfiles[i]); + texfiles[i] = NULL; } nTexLumps = 0; 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/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/rehlds/rehlds_api_impl.cpp b/rehlds/rehlds/rehlds_api_impl.cpp index d18622036..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() { diff --git a/rehlds/rehlds/structSizeCheck.cpp b/rehlds/rehlds/structSizeCheck.cpp index 18cb3db2a..259787c3e 100644 --- a/rehlds/rehlds/structSizeCheck.cpp +++ b/rehlds/rehlds/structSizeCheck.cpp @@ -15,5 +15,7 @@ void check_size() { void checkSizesStatic() { CHECK_TYPE_SIZE(client_t, 0x5018, 0x4EF4); CHECK_TYPE_SIZE(userfilter_t, 0x20, 0x18); +#ifndef REHLDS_FIXES CHECK_TYPE_SIZE(CSteam3Server, 0x90, 0xA8); +#endif }