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
-- listipcfgfile // File for permanent ip bans. Default: listip.cfg
-
- syserror_logfile // File for the system error log. Default: sys_error.log
-
- sv_auto_precache_sounds_in_models <1|0> // Automatically precache sounds attached to models. Deault: 0
-
- sv_delayed_spray_upload <1|0> // Upload custom sprays after entering the game instead of when connecting. It increases upload speed. Default: 0
-
- sv_echo_unknown_cmd <1|0> // Echo in the console when trying execute an unknown command. Default: 0
-
- sv_rcon_condebug <1|0> // Print rcon debug in the console. Default: 1
-
- sv_force_ent_intersection <1|0> // In a 3-rd party plugins used to force colliding of SOLID_SLIDEBOX entities. Default: 0
-
- sv_rehlds_force_dlmax <1|0> // Force a client's cl_dlmax cvar to 1024. It avoids an excessive packets fragmentation. Default: 0
-
- sv_rehlds_hull_centering <1|0> // Use center of hull instead of corner. Default: 0
+
- listipcfgfile <filename> // File for permanent ip bans. Default: listip.cfg
+
- syserror_logfile <filename> // File for the system error log. Default: sys_error.log
+
- sv_auto_precache_sounds_in_models <1|0> // Automatically precache sounds attached to models. Deault: 0
+
- sv_delayed_spray_upload <1|0> // Upload custom sprays after entering the game instead of when connecting. It increases upload speed. Default: 0
+
- sv_echo_unknown_cmd <1|0> // Echo in the console when trying execute an unknown command. Default: 0
+
- sv_rcon_condebug <1|0> // Print rcon debug in the console. Default: 1
+
- sv_force_ent_intersection <1|0> // In a 3-rd party plugins used to force colliding of SOLID_SLIDEBOX entities. Default: 0
+
- sv_rehlds_force_dlmax <1|0> // Force a client's cl_dlmax cvar to 1024. It avoids an excessive packets fragmentation. Default: 0
+
- sv_rehlds_hull_centering <1|0> // Use center of hull instead of corner. Default: 0
- sv_rehlds_movecmdrate_max_avg // Max average level of 'move' cmds for ban. Default: 400
- sv_rehlds_movecmdrate_avg_punish // Time in minutes for which the player will be banned (0 - Permanent, use a negative number for a kick). Default: 5
- sv_rehlds_movecmdrate_max_burst // Max burst level of 'move' cmds for ban. Default: 2500
- sv_rehlds_movecmdrate_burst_punish // Time in minutes for which the player will be banned (0 - Permanent, use a negative number for a kick). Default: 5
-
- sv_rehlds_send_mapcycle <1|0> // Send mapcycle.txt in serverinfo message (HLDS behavior, but it is unused on the client). Default: 0
+
- sv_rehlds_send_mapcycle <1|0> // Send mapcycle.txt in serverinfo message (HLDS behavior, but it is unused on the client). Default: 0
- sv_rehlds_stringcmdrate_max_avg // Max average level of 'string' cmds for ban. Default: 80
- sv_rehlds_stringcmdrate_avg_punish // Time in minutes for which the player will be banned (0 - Permanent, use a negative number for a kick). Default: 5
- sv_rehlds_stringcmdrate_max_burst // Max burst level of 'string' cmds for ban. Default: 400
@@ -52,9 +60,10 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
- sv_rehlds_userinfo_transmitted_fields // Userinfo fields only with these keys will be transmitted to clients via network. If not set then all fields will be transmitted (except prefixed with underscore). Each key must be prefixed by backslash, for example "\name\model\*sid\*hltv\bottomcolor\topcolor". See [wiki](https://github.com/dreamstalker/rehlds/wiki/Userinfo-keys) to collect sufficient set of keys for your server. Default: ""
- sv_rehlds_attachedentities_playeranimationspeed_fix // Fixes bug with gait animation speed increase when player has some attached entities (aiments). Can cause animation lags when cl_updaterate is low. Default: 0
- sv_rehlds_maxclients_from_single_ip // Limit number of connections at the same time from single IP address, not confuse to already connected players. Default: 5
-
- sv_rehlds_local_gametime <1|0> // A feature of local gametime which decrease "lags" if you run same map for a long time. Default: 0
+
- sv_rehlds_local_gametime <1|0> // A feature of local gametime which decrease "lags" if you run same map for a long time. Default: 0
- sv_use_entity_file // Use custom entity file for a map. Path to an entity file will be "maps/[map name].ent". 0 - use original entities. 1 - use .ent files from maps directory. 2 - use .ent files from maps directory and create new .ent file if not exist.
- sv_usercmd_custom_random_seed // When enabled server will populate an additional random seed independent of the client. Default: 0
+
- sv_tags <comma-delimited string list of tags> // Sets a string defining the "gametags" for this server, this is optional, but if it is set it allows users/scripts to filter in the matchmaking/server-browser interfaces based on the value. Default: ""
@@ -62,6 +71,9 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
- rescount // Prints the total count of precached resources in the server console
- reslist <sound | model | decal | generic | event> // Separately prints the details of the precached resources for sounds, models, decals, generic and events in server console. Useful for managing resources and dealing with the goldsource precache limits.
+
- rcon_adduser <ipaddress/CIDR> // Add a new IP address or CIDR range to RCON user list (This command adds a new IP address to the RCON user list. The specified IP or CIDR range is granted privileged access to server console. Without any Rcon users, access is allowed to anyone with a valid password)
+- rcon_deluser <ipaddress> {removeAll} // Remove an IP address or CIDR range from RCON user list
+- rcon_users // List all IP addresses and CIDR ranges in RCON user list
## 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
}