diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d6297a4..97a7a46 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -10,6 +10,7 @@ on:
types: [opened, reopened, synchronize]
release:
types: [published]
+ workflow_dispatch:
jobs:
windows:
diff --git a/README.md b/README.md
index 9d5f8eb..0da1d35 100644
--- a/README.md
+++ b/README.md
@@ -63,9 +63,12 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
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_net_incoming_decompression <1|0> // When enabled server will decompress of incoming compressed file transfer payloads. Default: 1
-sv_net_incoming_decompression_max_ratio <0|100> // Sets the max allowed ratio between compressed and uncompressed data for file transfer. (A ratio close to 90 indicates large uncompressed data with low entropy) Default: 80.0
-sv_net_incoming_decompression_max_size <16|65536> // Sets the max allowed size for decompressed file transfer data. Default: 65536 bytes
+sv_net_incoming_decompression <1|0> // When enabled server will decompress of incoming compressed file transfer payloads. Default: 1
+sv_net_incoming_decompression_max_ratio <0|100> // Sets the max allowed ratio between compressed and uncompressed data for file transfer. (A ratio close to 90 indicates large uncompressed data with low entropy) Default: 80.0
+sv_net_incoming_decompression_max_size <16|65536> // Sets the max allowed size for decompressed file transfer data. Default: 65536 bytes
+sv_net_incoming_decompression_min_failures <0|10> // Sets the min number of decompression failures required before a player's connection is flagged for potential punishment. Default: 4
+sv_net_incoming_decompression_max_failures <0|10> // Sets the max number of decompression failures allowed within a specified time window before action is taken against the player. Default: 10
+sv_net_incoming_decompression_min_failuretime: <0.1|10.0> // Sets the min time in secs within which decompression failures are tracked to determine if the player exceeds the failure thresholds. Default: 0.1
sv_net_incoming_decompression_punish // Time in minutes for which the player will be banned for malformed/abnormal bzip2 fragments (0 - Permanent, use a negative number for a kick). Default: -1
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: ""
sv_filterban <-1|0|1>// Set packet filtering by IP mode. -1 - All players will be rejected without any exceptions. 0 - No checks will happen. 1 - All incoming players will be checked if they're IP banned (if they have an IP filter entry), if they are, they will be kicked. Default: 1
diff --git a/rehlds/engine/model.cpp b/rehlds/engine/model.cpp
index 162e4c5..e69d2c9 100644
--- a/rehlds/engine/model.cpp
+++ b/rehlds/engine/model.cpp
@@ -854,6 +854,7 @@ void CalcSurfaceExtents(msurface_t *s)
int i, j, e;
mvertex_t *v;
mtexinfo_t *tex;
+ vec3_t middle{};
int bmins[2], bmaxs[2];
mins[0] = mins[1] = 999999;
@@ -869,6 +870,8 @@ void CalcSurfaceExtents(msurface_t *s)
else
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
+ VectorAdd(middle, v->position, middle);
+
for (j = 0; j < 2; j++)
{
// FIXED: loss of floating point
@@ -884,6 +887,8 @@ void CalcSurfaceExtents(msurface_t *s)
}
}
+ VectorScale(middle, 1.0f / s->numedges, middle);
+
for (i = 0; i < 2; i++)
{
bmins[i] = (int) floor(mins[i] / 16);
@@ -891,8 +896,16 @@ void CalcSurfaceExtents(msurface_t *s)
s->texturemins[i] = bmins[i] * 16;
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
+
if (!(tex->flags & TEX_SPECIAL) && s->extents[i] > MAX_SURFACE_TEXTURE_SIZE)
- Sys_Error("%s: Bad surface extents", __func__);
+ {
+ int surfID = s - loadmodel->surfaces;
+ Sys_Error("%s: Bad #%d surface extents %d/%d on %s at position (%d,%d,%d)",
+ __func__, surfID, s->extents[0], s->extents[1],
+ tex->texture->name,
+ (int)middle[0], (int)middle[1], (int)middle[2]
+ );
+ }
}
}
diff --git a/rehlds/engine/net.h b/rehlds/engine/net.h
index 072ba33..c39c495 100644
--- a/rehlds/engine/net.h
+++ b/rehlds/engine/net.h
@@ -422,6 +422,7 @@ typedef struct netchan_s
// Incoming and outgoing flow metrics
flow_t flow[MAX_FLOWS];
+
} netchan_t;
#ifdef REHLDS_FIXES
diff --git a/rehlds/engine/net_chan.cpp b/rehlds/engine/net_chan.cpp
index b4b75e9..1febfdb 100644
--- a/rehlds/engine/net_chan.cpp
+++ b/rehlds/engine/net_chan.cpp
@@ -40,7 +40,10 @@ cvar_t net_chokeloopback = { "net_chokeloop", "0", 0, 0.0f, nullptr};
cvar_t sv_net_incoming_decompression = { "sv_net_incoming_decompression", "1", 0, 1.0f, nullptr };
cvar_t sv_net_incoming_decompression_max_ratio = { "sv_net_incoming_decompression_max_ratio", "80.0", 0, 80.0f, nullptr };
cvar_t sv_net_incoming_decompression_max_size = { "sv_net_incoming_decompression_max_size", "65536", 0, 65536.0f, nullptr };
-cvar_t sv_net_incoming_decompression_punish = { "sv_net_incoming_decompression_punish", "-1", 0, -1.0f, NULL };
+cvar_t sv_net_incoming_decompression_min_failures = { "sv_net_incoming_decompression_min_failures", "4", 0, 4.0f, nullptr };
+cvar_t sv_net_incoming_decompression_max_failures = { "sv_net_incoming_decompression_max_failures", "10", 0, 10.0f, nullptr };
+cvar_t sv_net_incoming_decompression_min_failuretime = { "sv_net_incoming_decompression_min_failuretime", "0.1", 0, 0.1f, nullptr };
+cvar_t sv_net_incoming_decompression_punish = { "sv_net_incoming_decompression_punish", "-1", 0, -1.0f, nullptr };
cvar_t sv_filetransfercompression = { "sv_filetransfercompression", "1", 0, 0.0f, nullptr};
cvar_t sv_filetransfermaxsize = { "sv_filetransfermaxsize", "10485760", 0, 0.0f, nullptr};
@@ -184,6 +187,7 @@ void Netchan_Clear(netchan_t *chan)
void Netchan_Setup(netsrc_t socketnumber, netchan_t *chan, netadr_t adr, int player_slot, void *connection_status, qboolean(*pfnNetchan_Blocksize)(void *))
{
Netchan_Clear(chan);
+ g_GameClients[player_slot]->NetchanClear();
Q_memset(chan, 0, sizeof(netchan_t));
@@ -1423,6 +1427,93 @@ void Netchan_FlushIncoming(netchan_t *chan, int stream)
chan->incomingready[stream] = FALSE;
}
+void Netchan_DecompressionCvarsBounds()
+{
+ if (sv_net_incoming_decompression_min_failures.value < 1)
+ Cvar_SetValue("sv_net_incoming_decompression_min_failures", 1);
+
+ else if (sv_net_incoming_decompression_min_failures.value > NET_DECOMPRESS_MAX_TIMES)
+ Cvar_SetValue("sv_net_incoming_decompression_min_failures", NET_DECOMPRESS_MAX_TIMES);
+
+ if (sv_net_incoming_decompression_max_failures.value < 1)
+ Cvar_SetValue("sv_net_incoming_decompression_max_failures", 1);
+
+ else if (sv_net_incoming_decompression_max_failures.value > NET_DECOMPRESS_MAX_TIMES)
+ Cvar_SetValue("sv_net_incoming_decompression_max_failures", NET_DECOMPRESS_MAX_TIMES);
+
+ if (sv_net_incoming_decompression_max_failures.value < sv_net_incoming_decompression_min_failures.value)
+ {
+ int iTemp = sv_net_incoming_decompression_max_failures.value;
+ Cvar_SetValue("sv_net_incoming_decompression_max_failures", sv_net_incoming_decompression_min_failures.value);
+ Cvar_SetValue("sv_net_incoming_decompression_min_failures", iTemp);
+ }
+
+ if (sv_net_incoming_decompression_min_failuretime.value <= 0.0f)
+ Cvar_SetValue("sv_net_incoming_decompression_min_failuretime", 0.1f);
+}
+
+// Check for an abnormal size ratio between compressed and uncompressed data
+qboolean Netchan_ValidateDecompress(netchan_t *chan, int stream, unsigned int compressedSize, unsigned int uncompressedSize)
+{
+#ifdef REHLDS_FIXES
+ int i;
+
+ if (sv_net_incoming_decompression_max_ratio.value <= 0)
+ return TRUE; // validation is disabled
+
+ if (compressedSize >= uncompressedSize)
+ return TRUE;
+
+ float ratio = ((float)(uncompressedSize - compressedSize) / uncompressedSize) * 100.0f;
+ if (ratio < sv_net_incoming_decompression_max_ratio.value)
+ return TRUE; // no low entropy for uncompressed data
+
+ if ((chan->player_slot - 1) != host_client - g_psvs.clients)
+ return TRUE;
+
+ Netchan_DecompressionCvarsBounds();
+
+ FragStats_t &stats = g_GameClients[chan->player_slot - 1]->GetFragStats(stream);
+
+ // check if the client should be rejected based on total failed decompress
+ if (stats.num_decompress_failures >= sv_net_incoming_decompression_max_failures.value)
+ {
+ for (i = 0; i < sv_net_incoming_decompression_max_failures.value - 1; i++)
+ stats.decompress_failure_times[i] = stats.decompress_failure_times[i + 1];
+
+ stats.num_decompress_failures = sv_net_incoming_decompression_max_failures.value - 1;
+ }
+
+ stats.decompress_failure_times[stats.num_decompress_failures++] = realtime;
+
+ // check if the client should be rejected based on recent failed decompress
+ int recent_failures = 0;
+ for (i = 0; i < stats.num_decompress_failures; i++)
+ {
+ if ((realtime - stats.decompress_failure_times[i]) <= sv_net_incoming_decompression_min_failuretime.value)
+ recent_failures++;
+ }
+
+ if (recent_failures >= sv_net_incoming_decompression_min_failures.value)
+ {
+ if (chan->player_slot == 0)
+ Con_DPrintf("Incoming abnormal uncompressed size with ratio %.2f\n", ratio);
+ else
+ Con_DPrintf("%s:Incoming abnormal uncompressed size with ratio %.2f from %s\n", NET_AdrToString(chan->remote_address), ratio, host_client->name);
+
+ if (sv_net_incoming_decompression_punish.value >= 0)
+ {
+ Con_DPrintf("%s:Banned for malformed/abnormal bzip2 fragments from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
+ Cbuf_AddText(va("addip %.1f %s\n", sv_net_incoming_decompression_punish.value, NET_BaseAdrToString(chan->remote_address)));
+ }
+
+ return FALSE;
+ }
+#endif
+
+ return TRUE;
+}
+
qboolean Netchan_CopyNormalFragments(netchan_t *chan)
{
fragbuf_t *p, *n;
@@ -1483,8 +1574,6 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan)
}
#endif // REHLDS_FIXES
- qboolean success = TRUE;
-
if (*(uint32 *)net_message.data == MAKEID('B', 'Z', '2', '\0'))
{
// Determine whether decompression of compressed data is allowed
@@ -1493,13 +1582,13 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan)
{
if (chan->player_slot == 0)
{
- Con_DPrintf("Incoming compressed data disallowed from\n");
+ Con_DPrintf("Incoming compressed normal fragment disallowed from\n");
return FALSE;
}
// compressed data is expected only after requesting resource list
else if (host_client->m_sendrescount == 0)
{
- Con_DPrintf("%s:Incoming compressed data disallowed from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
+ Con_DPrintf("%s:Incoming compressed normal fragment disallowed from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
return FALSE;
}
}
@@ -1510,56 +1599,34 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan)
unsigned int compressedSize = net_message.cursize - 4;
// Decompress net buffer data
- if (success && (BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char *)net_message.data + 4, compressedSize, 1, 0) == BZ_OK))
- {
-#ifdef REHLDS_FIXES
- // Check for an abnormal size ratio between compressed and uncompressed data
- if (sv_net_incoming_decompression_max_ratio.value > 0 && compressedSize < uncompressedSize)
- {
- float ratio = ((float)(uncompressedSize - compressedSize) / uncompressedSize) * 100.0f;
- if (ratio >= sv_net_incoming_decompression_max_ratio.value)
- {
- if (chan->player_slot == 0)
- Con_DPrintf("Incoming abnormal uncompressed size with ratio %.2f\n", ratio);
- else
- Con_DPrintf("%s:Incoming abnormal uncompressed size with ratio %.2f from %s\n", NET_AdrToString(chan->remote_address), ratio, host_client->name);
+ qboolean success = TRUE;
- success = FALSE;
- }
- }
-#endif
-
- // Copy uncompressed data back to the net buffer
- Q_memcpy(net_message.data, uncompressed, uncompressedSize);
- net_message.cursize = uncompressedSize;
- }
- else
+ if (BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char *)net_message.data + 4, compressedSize, 1, 0) != BZ_OK)
{
// malformed data or compressed data exceeding sv_net_incoming_decompression_max_size
success = FALSE;
}
+ else if (!Netchan_ValidateDecompress(chan, FRAG_NORMAL_STREAM, compressedSize, uncompressedSize))
+ {
+ success = FALSE;
+ }
- // Drop client if decompression was unsuccessful
+#ifdef REHLDS_FIXES
if (!success)
{
- if ((chan->player_slot - 1) == host_client - g_psvs.clients)
- {
-#ifdef REHLDS_FIXES
- if (sv_net_incoming_decompression_punish.value >= 0)
- {
- Con_DPrintf("%s:Banned for malformed/abnormal bzip2 fragments from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
- Cbuf_AddText(va("addip %.1f %s\n", sv_net_incoming_decompression_punish.value, NET_BaseAdrToString(chan->remote_address)));
- }
+ // Drop client if decompression was unsuccessful
+ SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data");
+ SZ_Clear(&net_message);
+ return FALSE;
+ }
#endif
- SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data");
- }
-
- SZ_Clear(&net_message);
- }
+ // Copy uncompressed data back to the net buffer
+ Q_memcpy(net_message.data, uncompressed, uncompressedSize);
+ net_message.cursize = uncompressedSize;
}
- return success;
+ return TRUE;
}
qboolean Netchan_CopyFileFragments(netchan_t *chan)
@@ -1575,7 +1642,6 @@ qboolean Netchan_CopyFileFragments(netchan_t *chan)
qboolean bCompressed;
unsigned int uncompressedSize;
-
if (!chan->incomingready[FRAG_FILE_STREAM])
return FALSE;
@@ -1587,6 +1653,19 @@ qboolean Netchan_CopyFileFragments(netchan_t *chan)
return FALSE;
}
+#ifdef REHLDS_FIXES
+ if (chan->player_slot > 0 && (chan->player_slot - 1) == host_client - g_psvs.clients)
+ {
+ // customization already uploaded with request by operator,
+ // do not accept any other customization
+ if (host_client->uploaddoneregistering)
+ {
+ SV_DropClient(host_client, FALSE, "Too many customization have been uploaded (unrequested customization)");
+ return FALSE;
+ }
+ }
+#endif
+
bCompressed = FALSE;
SZ_Clear(&net_message);
MSG_BeginReading();
@@ -1701,10 +1780,53 @@ qboolean Netchan_CopyFileFragments(netchan_t *chan)
if (bCompressed)
{
- unsigned char* uncompressedBuffer = (unsigned char*)Mem_Malloc(uncompressedSize);
- Con_DPrintf("Decompressing file %s (%d -> %d)\n", filename, nsize, uncompressedSize);
- BZ2_bzBuffToBuffDecompress((char*)uncompressedBuffer, &uncompressedSize, (char*)buffer, nsize, 1, 0);
+ // Determine whether decompression of compressed data is allowed
+#ifdef REHLDS_FIXES
+ if (!sv_net_incoming_decompression.value)
+ {
+ if (chan->player_slot == 0)
+ {
+ Con_DPrintf("Incoming compressed file fragment disallowed from\n");
+ return FALSE;
+ }
+ // compressed data is expected only after requesting resource list
+ else if (host_client->m_sendrescount == 0)
+ {
+ Con_DPrintf("%s:Incoming compressed file fragment disallowed from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
+ return FALSE;
+ }
+ }
+#endif
+
+ uncompressedSize = clamp(uncompressedSize, 16u, (unsigned)sv_net_incoming_decompression_max_size.value); // valid range (16 - 65536) bytes
+
+ qboolean success = TRUE;
+ unsigned char *uncompressedBuffer = (unsigned char *)Mem_Malloc(uncompressedSize);
+ unsigned int compressedSize = nsize;
+
+ // Decompress net buffer data
+ if (BZ2_bzBuffToBuffDecompress((char *)uncompressedBuffer, &uncompressedSize, (char *)buffer, compressedSize, 1, 0) != BZ_OK)
+ {
+ // malformed data or compressed data exceeding sv_net_incoming_decompression_max_size
+ success = FALSE;
+ }
+ else if (!Netchan_ValidateDecompress(chan, FRAG_FILE_STREAM, compressedSize, uncompressedSize))
+ {
+ success = FALSE;
+ }
+
Mem_Free(buffer);
+
+ if (!success)
+ {
+ // Drop client if decompression was unsuccessful
+ SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data");
+ SZ_Clear(&net_message);
+ Mem_Free(uncompressedBuffer);
+ return FALSE;
+ }
+
+ Con_DPrintf("Decompressing file %s (%d -> %d)\n", filename, compressedSize, uncompressedSize);
pos = uncompressedSize;
buffer = uncompressedBuffer;
}
@@ -1899,6 +2021,9 @@ void Netchan_Init(void)
Cvar_RegisterVariable(&sv_net_incoming_decompression);
Cvar_RegisterVariable(&sv_net_incoming_decompression_max_ratio);
Cvar_RegisterVariable(&sv_net_incoming_decompression_max_size);
+ Cvar_RegisterVariable(&sv_net_incoming_decompression_min_failures);
+ Cvar_RegisterVariable(&sv_net_incoming_decompression_max_failures);
+ Cvar_RegisterVariable(&sv_net_incoming_decompression_min_failuretime);
Cvar_RegisterVariable(&sv_net_incoming_decompression_punish);
#endif
Cvar_RegisterVariable(&sv_filetransfermaxsize);
diff --git a/rehlds/engine/net_ws.cpp b/rehlds/engine/net_ws.cpp
index 4880da9..b9c52b1 100644
--- a/rehlds/engine/net_ws.cpp
+++ b/rehlds/engine/net_ws.cpp
@@ -1768,7 +1768,7 @@ void NET_GetLocalAddress()
if (noip)
{
- Con_Printf("TCP/IP Disabled.\n");
+ Con_DPrintf("TCP/IP Disabled.\n");
}
else
{
@@ -1823,7 +1823,7 @@ void NET_GetLocalAddress()
#ifdef _WIN32
if (noipx)
{
- Con_Printf("No IPX Support.\n");
+ Con_DPrintf("No IPX Support.\n");
}
else
{
diff --git a/rehlds/engine/pmove.cpp b/rehlds/engine/pmove.cpp
index e281cd9..5245c4b 100644
--- a/rehlds/engine/pmove.cpp
+++ b/rehlds/engine/pmove.cpp
@@ -29,7 +29,7 @@
#include "precompiled.h"
playermove_t *pmove;
-movevars_t movevars;
+movevars_t sv_movevars;
cvar_t pm_showclip = { "pm_showclip", "0", 0, 0.0f, NULL };
@@ -98,7 +98,7 @@ void PM_Init(playermove_t *ppm)
VectorCopy(player_maxs[i], ppm->player_maxs[i]);
}
- ppm->_movevars = &movevars;
+ ppm->movevars = &sv_movevars;
ppm->PM_Info_ValueForKey = Info_ValueForKey;
ppm->PM_Particle = CL_Particle;
diff --git a/rehlds/engine/pmove.h b/rehlds/engine/pmove.h
index 8748d3a..d610f8e 100644
--- a/rehlds/engine/pmove.h
+++ b/rehlds/engine/pmove.h
@@ -39,7 +39,7 @@ extern vec3_t player_mins[MAX_MAP_HULLS];
extern vec3_t player_maxs[MAX_MAP_HULLS];
extern playermove_t *pmove;
-extern movevars_t movevars;
+extern movevars_t sv_movevars;
qboolean PM_AddToTouched(pmtrace_t tr, vec_t *impactvelocity);
void PM_StuckTouch(int hitent, pmtrace_t *ptraceresult);
diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h
index 9446f14..1a9eb8e 100644
--- a/rehlds/engine/server.h
+++ b/rehlds/engine/server.h
@@ -53,6 +53,7 @@ const int MAX_NAME = 32;
#include "pm_defs.h"
#include "inst_baseline.h"
#include "net_ws.h"
+#include "pm_shared/pm_movevars.h"
const int DEFAULT_SOUND_PACKET_VOLUME = 255;
const float DEFAULT_SOUND_PACKET_ATTENUATION = 1.0f;
@@ -235,6 +236,7 @@ typedef struct client_s
double m_lastvoicetime;
int m_sendrescount;
qboolean m_bSentNewResponse;
+ movevars_t movevars;
} client_t;
enum
@@ -454,10 +456,9 @@ void SV_BuildHashedSoundLookupTable(void);
void SV_AddSampleToHashedLookupTable(const char *pszSample, int iSampleIndex);
qboolean SV_ValidClientMulticast(client_t *client, int soundLeaf, int to);
void SV_Multicast(edict_t *ent, vec_t *origin, int to, qboolean reliable);
-void SV_WriteMovevarsToClient(sizebuf_t *message);
+void SV_WriteMovevarsToClient(sizebuf_t *message, struct movevars_s *movevars);
void SV_WriteDeltaDescriptionsToClient(sizebuf_t *msg);
-void SV_SetMoveVars(void);
-void SV_QueryMovevarsChanged(void);
+void SV_SetMoveVars(struct movevars_s *movevars);
void SV_SendServerinfo(sizebuf_t *msg, client_t *client);
void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client);
void SV_SendResources(sizebuf_t *msg);
diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp
index 211a319..20ba978 100644
--- a/rehlds/engine/sv_main.cpp
+++ b/rehlds/engine/sv_main.cpp
@@ -132,10 +132,47 @@ 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);
+void sv_movevars_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 };
+//------------------------------------------------
+// Movevars cvarhook declares
+//------------------------------------------------
+
+#define DECLARE_CVARHOOK_MOVEVARS(cvar)\
+ cvarhook_t cvar##_hook = { sv_movevars_hook_callback, NULL, NULL }
+
+#define CVARHOOK_MOVEVARS(cvar)\
+ Cvar_HookVariable(cvar.name, &cvar##_hook);
+
+DECLARE_CVARHOOK_MOVEVARS(sv_gravity);
+DECLARE_CVARHOOK_MOVEVARS(sv_stopspeed);
+DECLARE_CVARHOOK_MOVEVARS(sv_maxspeed);
+DECLARE_CVARHOOK_MOVEVARS(sv_spectatormaxspeed);
+DECLARE_CVARHOOK_MOVEVARS(sv_accelerate);
+DECLARE_CVARHOOK_MOVEVARS(sv_airaccelerate);
+DECLARE_CVARHOOK_MOVEVARS(sv_wateraccelerate);
+DECLARE_CVARHOOK_MOVEVARS(sv_friction);
+DECLARE_CVARHOOK_MOVEVARS(sv_edgefriction);
+DECLARE_CVARHOOK_MOVEVARS(sv_waterfriction);
+DECLARE_CVARHOOK_MOVEVARS(sv_bounce);
+DECLARE_CVARHOOK_MOVEVARS(sv_stepsize);
+DECLARE_CVARHOOK_MOVEVARS(sv_maxvelocity);
+DECLARE_CVARHOOK_MOVEVARS(sv_zmax);
+DECLARE_CVARHOOK_MOVEVARS(sv_wateramp);
+DECLARE_CVARHOOK_MOVEVARS(sv_footsteps);
+DECLARE_CVARHOOK_MOVEVARS(sv_rollangle);
+DECLARE_CVARHOOK_MOVEVARS(sv_rollspeed);
+DECLARE_CVARHOOK_MOVEVARS(sv_skycolor_r);
+DECLARE_CVARHOOK_MOVEVARS(sv_skycolor_g);
+DECLARE_CVARHOOK_MOVEVARS(sv_skycolor_b);
+DECLARE_CVARHOOK_MOVEVARS(sv_skyvec_x);
+DECLARE_CVARHOOK_MOVEVARS(sv_skyvec_y);
+DECLARE_CVARHOOK_MOVEVARS(sv_skyvec_z);
+DECLARE_CVARHOOK_MOVEVARS(sv_skyname);
+
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 };
@@ -951,35 +988,35 @@ void SV_Multicast(edict_t *ent, vec_t *origin, int to, qboolean reliable)
host_client = save;
}
-void EXT_FUNC SV_WriteMovevarsToClient(sizebuf_t *message)
+void SV_WriteMovevarsToClient(sizebuf_t *message, movevars_t *movevars)
{
MSG_WriteByte(message, svc_newmovevars);
- MSG_WriteFloat(message, movevars.gravity);
- MSG_WriteFloat(message, movevars.stopspeed);
- MSG_WriteFloat(message, movevars.maxspeed);
- MSG_WriteFloat(message, movevars.spectatormaxspeed);
- MSG_WriteFloat(message, movevars.accelerate);
- MSG_WriteFloat(message, movevars.airaccelerate);
- MSG_WriteFloat(message, movevars.wateraccelerate);
- MSG_WriteFloat(message, movevars.friction);
- MSG_WriteFloat(message, movevars.edgefriction);
- MSG_WriteFloat(message, movevars.waterfriction);
- MSG_WriteFloat(message, movevars.entgravity);
- MSG_WriteFloat(message, movevars.bounce);
- MSG_WriteFloat(message, movevars.stepsize);
- MSG_WriteFloat(message, movevars.maxvelocity);
- MSG_WriteFloat(message, movevars.zmax);
- MSG_WriteFloat(message, movevars.waveHeight);
- MSG_WriteByte(message, movevars.footsteps != 0);
- MSG_WriteFloat(message, movevars.rollangle);
- MSG_WriteFloat(message, movevars.rollspeed);
- MSG_WriteFloat(message, movevars.skycolor_r);
- MSG_WriteFloat(message, movevars.skycolor_g);
- MSG_WriteFloat(message, movevars.skycolor_b);
- MSG_WriteFloat(message, movevars.skyvec_x);
- MSG_WriteFloat(message, movevars.skyvec_y);
- MSG_WriteFloat(message, movevars.skyvec_z);
- MSG_WriteString(message, movevars.skyName);
+ MSG_WriteFloat(message, movevars->gravity);
+ MSG_WriteFloat(message, movevars->stopspeed);
+ MSG_WriteFloat(message, movevars->maxspeed);
+ MSG_WriteFloat(message, movevars->spectatormaxspeed);
+ MSG_WriteFloat(message, movevars->accelerate);
+ MSG_WriteFloat(message, movevars->airaccelerate);
+ MSG_WriteFloat(message, movevars->wateraccelerate);
+ MSG_WriteFloat(message, movevars->friction);
+ MSG_WriteFloat(message, movevars->edgefriction);
+ MSG_WriteFloat(message, movevars->waterfriction);
+ MSG_WriteFloat(message, movevars->entgravity);
+ MSG_WriteFloat(message, movevars->bounce);
+ MSG_WriteFloat(message, movevars->stepsize);
+ MSG_WriteFloat(message, movevars->maxvelocity);
+ MSG_WriteFloat(message, movevars->zmax);
+ MSG_WriteFloat(message, movevars->waveHeight);
+ MSG_WriteByte(message, movevars->footsteps != 0);
+ MSG_WriteFloat(message, movevars->rollangle);
+ MSG_WriteFloat(message, movevars->rollspeed);
+ MSG_WriteFloat(message, movevars->skycolor_r);
+ MSG_WriteFloat(message, movevars->skycolor_g);
+ MSG_WriteFloat(message, movevars->skycolor_b);
+ MSG_WriteFloat(message, movevars->skyvec_x);
+ MSG_WriteFloat(message, movevars->skyvec_y);
+ MSG_WriteFloat(message, movevars->skyvec_z);
+ MSG_WriteString(message, movevars->skyName);
}
void EXT_FUNC SV_WriteDeltaDescriptionsToClient(sizebuf_t *msg)
@@ -1008,76 +1045,41 @@ void EXT_FUNC SV_WriteDeltaDescriptionsToClient(sizebuf_t *msg)
}
}
-void EXT_FUNC SV_SetMoveVars(void)
+void sv_movevars_hook_callback(cvar_t *cvar)
{
- movevars.entgravity = 1.0f;
- movevars.gravity = sv_gravity.value;
- movevars.stopspeed = sv_stopspeed.value;
- movevars.maxspeed = sv_maxspeed.value;
- movevars.spectatormaxspeed = sv_spectatormaxspeed.value;
- movevars.accelerate = sv_accelerate.value;
- movevars.airaccelerate = sv_airaccelerate.value;
- movevars.wateraccelerate = sv_wateraccelerate.value;
- movevars.friction = sv_friction.value;
- movevars.edgefriction = sv_edgefriction.value;
- movevars.waterfriction = sv_waterfriction.value;
- movevars.bounce = sv_bounce.value;
- movevars.stepsize = sv_stepsize.value;
- movevars.maxvelocity = sv_maxvelocity.value;
- movevars.zmax = sv_zmax.value;
- movevars.waveHeight = sv_wateramp.value;
- movevars.footsteps = sv_footsteps.value;
- movevars.rollangle = sv_rollangle.value;
- movevars.rollspeed = sv_rollspeed.value;
- movevars.skycolor_r = sv_skycolor_r.value;
- movevars.skycolor_g = sv_skycolor_g.value;
- movevars.skycolor_b = sv_skycolor_b.value;
- movevars.skyvec_x = sv_skyvec_x.value;
- movevars.skyvec_y = sv_skyvec_y.value;
- movevars.skyvec_z = sv_skyvec_z.value;
-
- Q_strncpy(movevars.skyName, sv_skyname.string, sizeof(movevars.skyName) - 1);
- movevars.skyName[sizeof(movevars.skyName) - 1] = 0;
+ SV_SetMoveVars(&sv_movevars);
}
-void SV_QueryMovevarsChanged(void)
+void SV_SetMoveVars(movevars_t *movevars)
{
- if (movevars.entgravity != 1.0f
- || sv_maxspeed.value != movevars.maxspeed
- || sv_gravity.value != movevars.gravity
- || sv_stopspeed.value != movevars.stopspeed
- || sv_spectatormaxspeed.value != movevars.spectatormaxspeed
- || sv_accelerate.value != movevars.accelerate
- || sv_airaccelerate.value != movevars.airaccelerate
- || sv_wateraccelerate.value != movevars.wateraccelerate
- || sv_friction.value != movevars.friction
- || sv_edgefriction.value != movevars.edgefriction
- || sv_waterfriction.value != movevars.waterfriction
- || sv_bounce.value != movevars.bounce
- || sv_stepsize.value != movevars.stepsize
- || sv_maxvelocity.value != movevars.maxvelocity
- || sv_zmax.value != movevars.zmax
- || sv_wateramp.value != movevars.waveHeight
- || sv_footsteps.value != movevars.footsteps
- || sv_rollangle.value != movevars.rollangle
- || sv_rollspeed.value != movevars.rollspeed
- || sv_skycolor_r.value != movevars.skycolor_r
- || sv_skycolor_g.value != movevars.skycolor_g
- || sv_skycolor_b.value != movevars.skycolor_b
- || sv_skyvec_x.value != movevars.skyvec_x
- || sv_skyvec_y.value != movevars.skyvec_y
- || sv_skyvec_z.value != movevars.skyvec_z
- || Q_strcmp(sv_skyname.string, movevars.skyName))
- {
- SV_SetMoveVars();
+ movevars->entgravity = 1.0f;
+ movevars->gravity = sv_gravity.value;
+ movevars->stopspeed = sv_stopspeed.value;
+ movevars->maxspeed = sv_maxspeed.value;
+ movevars->spectatormaxspeed = sv_spectatormaxspeed.value;
+ movevars->accelerate = sv_accelerate.value;
+ movevars->airaccelerate = sv_airaccelerate.value;
+ movevars->wateraccelerate = sv_wateraccelerate.value;
+ movevars->friction = sv_friction.value;
+ movevars->edgefriction = sv_edgefriction.value;
+ movevars->waterfriction = sv_waterfriction.value;
+ movevars->bounce = sv_bounce.value;
+ movevars->stepsize = sv_stepsize.value;
+ movevars->maxvelocity = sv_maxvelocity.value;
+ movevars->zmax = sv_zmax.value;
+ movevars->waveHeight = sv_wateramp.value;
+ movevars->footsteps = sv_footsteps.value;
+ movevars->rollangle = sv_rollangle.value;
+ movevars->rollspeed = sv_rollspeed.value;
+ movevars->skycolor_r = sv_skycolor_r.value;
+ movevars->skycolor_g = sv_skycolor_g.value;
+ movevars->skycolor_b = sv_skycolor_b.value;
+ movevars->skyvec_x = sv_skyvec_x.value;
+ movevars->skyvec_y = sv_skyvec_y.value;
+ movevars->skyvec_z = sv_skyvec_z.value;
- client_t *cl = g_psvs.clients;
- for (int i = 0; i < g_psvs.maxclients; i++, cl++)
- {
- if (!cl->fakeclient && (cl->active || cl->spawned || cl->connected))
- SV_WriteMovevarsToClient(&cl->netchan.message);
- }
- }
+ Q_strncpy(movevars->skyName, sv_skyname.string, sizeof(movevars->skyName) - 1);
+ movevars->skyName[sizeof(movevars->skyName) - 1] = 0;
}
void EXT_FUNC SV_SendServerinfo_mod(sizebuf_t *msg, IGameClient* cl)
@@ -1184,8 +1186,8 @@ void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client)
MSG_WriteByte(msg, sv_cheats.value != 0);
SV_WriteDeltaDescriptionsToClient(msg);
- SV_SetMoveVars();
- SV_WriteMovevarsToClient(msg);
+ SV_SetMoveVars(&sv_movevars);
+ SV_WriteMovevarsToClient(msg, &sv_movevars);
MSG_WriteByte(msg, svc_cdtrack);
MSG_WriteByte(msg, gGlobalVariables.cdAudioTrack);
@@ -1196,6 +1198,7 @@ void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client)
client->spawned = FALSE;
client->connected = TRUE;
client->fully_connected = FALSE;
+ client->movevars = sv_movevars;
}
void SV_SendResources(sizebuf_t *msg)
@@ -6073,7 +6076,7 @@ void PrecacheModelSounds(studiohdr_t *pStudioHeader)
void PrecacheModelSpecifiedFiles()
{
const char **s = &g_psv.model_precache[1];
- for (size_t i = 1; i < ARRAYSIZE(g_psv.model_precache) && *s != nullptr; i++, s++)
+ for (size_t i = 1; i < ARRAYSIZE(g_psv.model_precache) && *s && g_psv.models[i]; i++, s++)
{
if (g_psv.models[i]->type != mod_studio)
continue;
@@ -6512,7 +6515,7 @@ 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;
- SV_SetMoveVars();
+ SV_SetMoveVars(&sv_movevars);
return 1;
}
@@ -8091,7 +8094,6 @@ void EXT_FUNC SV_Frame_Internal()
SV_Physics();
g_psv.time += host_frametime;
}
- SV_QueryMovevarsChanged();
SV_RequestMissingResourcesFromClients();
SV_CheckTimeouts();
SV_SendClientMessages();
@@ -8336,6 +8338,35 @@ void SV_Init(void)
Cvar_RegisterVariable(&sv_usercmd_custom_random_seed);
#endif
+ //------------------------------------------------
+ // Movevars cvarhook registers
+ //------------------------------------------------
+ CVARHOOK_MOVEVARS(sv_gravity);
+ CVARHOOK_MOVEVARS(sv_stopspeed);
+ CVARHOOK_MOVEVARS(sv_maxspeed);
+ CVARHOOK_MOVEVARS(sv_spectatormaxspeed);
+ CVARHOOK_MOVEVARS(sv_accelerate);
+ CVARHOOK_MOVEVARS(sv_airaccelerate);
+ CVARHOOK_MOVEVARS(sv_wateraccelerate);
+ CVARHOOK_MOVEVARS(sv_friction);
+ CVARHOOK_MOVEVARS(sv_edgefriction);
+ CVARHOOK_MOVEVARS(sv_waterfriction);
+ CVARHOOK_MOVEVARS(sv_bounce);
+ CVARHOOK_MOVEVARS(sv_stepsize);
+ CVARHOOK_MOVEVARS(sv_maxvelocity);
+ CVARHOOK_MOVEVARS(sv_zmax);
+ CVARHOOK_MOVEVARS(sv_wateramp);
+ CVARHOOK_MOVEVARS(sv_footsteps);
+ CVARHOOK_MOVEVARS(sv_rollangle);
+ CVARHOOK_MOVEVARS(sv_rollspeed);
+ CVARHOOK_MOVEVARS(sv_skycolor_r);
+ CVARHOOK_MOVEVARS(sv_skycolor_g);
+ CVARHOOK_MOVEVARS(sv_skycolor_b);
+ CVARHOOK_MOVEVARS(sv_skyvec_x);
+ CVARHOOK_MOVEVARS(sv_skyvec_y);
+ CVARHOOK_MOVEVARS(sv_skyvec_z);
+ CVARHOOK_MOVEVARS(sv_skyname);
+
for (int i = 0; i < MAX_MODELS; i++)
{
Q_snprintf(localmodels[i], sizeof(localmodels[i]), "*%i", i);
diff --git a/rehlds/engine/sv_phys.cpp b/rehlds/engine/sv_phys.cpp
index 8fff595..520bbf3 100644
--- a/rehlds/engine/sv_phys.cpp
+++ b/rehlds/engine/sv_phys.cpp
@@ -1491,28 +1491,7 @@ void SV_Physics()
if (i > 0 && i <= g_psvs.maxclients)
continue;
- if (ent->v.flags & FL_ONGROUND)
- {
- edict_t *groundentity = ent->v.groundentity;
- if (groundentity && (groundentity->v.flags & FL_CONVEYOR))
- {
- if (ent->v.flags & FL_BASEVELOCITY)
- VectorMA(ent->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, ent->v.basevelocity);
- else
- VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity);
-
- ent->v.flags |= FL_BASEVELOCITY;
- }
- }
-
- if (!(ent->v.flags & FL_BASEVELOCITY))
- {
- // Apply momentum (add in half of the previous frame of velocity first)
- VectorMA(ent->v.velocity, (host_frametime * 0.5f + 1.0f), ent->v.basevelocity, ent->v.velocity);
- VectorClear(ent->v.basevelocity);
- }
-
- ent->v.flags &= ~FL_BASEVELOCITY;
+ SV_CheckMovingGround(ent, host_frametime);
switch (ent->v.movetype)
{
diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp
index 584ca4f..adc90ca 100644
--- a/rehlds/engine/sv_user.cpp
+++ b/rehlds/engine/sv_user.cpp
@@ -686,35 +686,33 @@ qboolean SV_PlayerRunThink(edict_t *ent, float frametime, double clienttimebase)
return ent->free == 0;
}
-void SV_CheckMovingGround(edict_t *player, float frametime)
+void SV_CheckMovingGround(edict_t *ent, float frametime)
{
edict_t *groundentity;
- if (player->v.flags & FL_ONGROUND)
+ if (ent->v.flags & FL_ONGROUND)
{
- groundentity = player->v.groundentity;
+ groundentity = ent->v.groundentity;
if (groundentity)
{
if (groundentity->v.flags & FL_CONVEYOR)
{
- if (player->v.flags & FL_BASEVELOCITY)
- VectorMA(player->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, player->v.basevelocity);
+ if (ent->v.flags & FL_BASEVELOCITY)
+ VectorMA(ent->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, ent->v.basevelocity);
else
- VectorScale(groundentity->v.movedir, groundentity->v.speed, player->v.basevelocity);
- player->v.flags |= FL_BASEVELOCITY;
+ VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity);
+ ent->v.flags |= FL_BASEVELOCITY;
}
}
}
- if (!(player->v.flags & FL_BASEVELOCITY))
+ if (!(ent->v.flags & FL_BASEVELOCITY))
{
- VectorMA(player->v.velocity, frametime * 0.5f + 1.0f, player->v.basevelocity, player->v.velocity);
- player->v.basevelocity[0] = 0;
- player->v.basevelocity[1] = 0;
- player->v.basevelocity[2] = 0;
+ VectorMA(ent->v.velocity, frametime * 0.5f + 1.0f, ent->v.basevelocity, ent->v.velocity);
+ VectorClear(ent->v.basevelocity);
}
- player->v.flags &= ~FL_BASEVELOCITY;
+ ent->v.flags &= ~FL_BASEVELOCITY;
}
void SV_ConvertPMTrace(trace_t *dest, pmtrace_t *src, edict_t *ent)
@@ -931,9 +929,17 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed)
pmove->PM_PlaySound = PM_SV_PlaySound;
pmove->PM_TraceTexture = PM_SV_TraceTexture;
pmove->PM_PlaybackEventFull = PM_SV_PlaybackEventFull;
+ pmove->movevars = &host_client->movevars;
+
+ const movevars_t movevars = *pmove->movevars; // preserve current movevars
+ host_client->movevars = sv_movevars; // always use global movevars as a base
gEntityInterface.pfnPM_Move(pmove, TRUE);
+ // Determine whether movevars has changed or not
+ if (!host_client->fakeclient && Q_memcmp(&movevars, pmove->movevars, sizeof(movevars)) != 0)
+ SV_WriteMovevarsToClient(&host_client->netchan.message, pmove->movevars); // sync movevars for the client
+
sv_player->v.deadflag = pmove->deadflag;
sv_player->v.effects = pmove->effects;
sv_player->v.teleport_time = pmove->waterjumptime;
diff --git a/rehlds/pm_shared/pm_defs.h b/rehlds/pm_shared/pm_defs.h
index 1c47d8f..02f4c4c 100644
--- a/rehlds/pm_shared/pm_defs.h
+++ b/rehlds/pm_shared/pm_defs.h
@@ -184,7 +184,7 @@ typedef struct playermove_s
char physinfo[ MAX_PHYSINFO_STRING ]; // Physics info string
- struct movevars_s *_movevars;
+ struct movevars_s *movevars;
vec3_t player_mins[MAX_MAP_HULLS];
vec3_t player_maxs[MAX_MAP_HULLS];
diff --git a/rehlds/rehlds/rehlds_api_impl.cpp b/rehlds/rehlds/rehlds_api_impl.cpp
index 62a3920..e4f0e00 100644
--- a/rehlds/rehlds/rehlds_api_impl.cpp
+++ b/rehlds/rehlds/rehlds_api_impl.cpp
@@ -458,6 +458,16 @@ void EXT_FUNC RemoveCvarListener_api(const char *var_name, cvar_callback_t func)
}
}
+void EXT_FUNC SV_SetMoveVars_api()
+{
+ SV_SetMoveVars(&sv_movevars);
+}
+
+void EXT_FUNC SV_WriteMovevarsToClient_api(sizebuf_t *message)
+{
+ SV_WriteMovevarsToClient(message, &sv_movevars);
+}
+
CRehldsServerStatic g_RehldsServerStatic;
CRehldsServerData g_RehldsServerData;
CRehldsHookchains g_RehldsHookchains;
@@ -475,8 +485,8 @@ RehldsFuncs_t g_RehldsApiFuncs =
&SV_CheckChallenge_api,
&SV_SendUserReg,
&SV_WriteDeltaDescriptionsToClient,
- &SV_SetMoveVars,
- &SV_WriteMovevarsToClient,
+ &SV_SetMoveVars_api,
+ &SV_WriteMovevarsToClient_api,
&GetClientFallback_api,
&GetAllowCheats_api,
&GSBSecure_api,
diff --git a/rehlds/rehlds/rehlds_interfaces_impl.cpp b/rehlds/rehlds/rehlds_interfaces_impl.cpp
index fa07ac5..0922bfa 100644
--- a/rehlds/rehlds/rehlds_interfaces_impl.cpp
+++ b/rehlds/rehlds/rehlds_interfaces_impl.cpp
@@ -530,6 +530,15 @@ netchan_t* EXT_FUNC CNetChan::GetChan()
return m_pNetChan;
}
+void CNetChan::Clear()
+{
+ for (int i = 0; i < MAX_STREAMS; i++)
+ {
+ for (int j = 0; j < NET_DECOMPRESS_MAX_TIMES; j++)
+ m_FragStats[i].decompress_failure_times[j] = 0;
+ m_FragStats[i].num_decompress_failures = 0;
+ }
+}
int EXT_FUNC CRehldsServerStatic::GetMaxClients()
diff --git a/rehlds/rehlds/rehlds_interfaces_impl.h b/rehlds/rehlds/rehlds_interfaces_impl.h
index 5cf4dba..7c62547 100644
--- a/rehlds/rehlds/rehlds_interfaces_impl.h
+++ b/rehlds/rehlds/rehlds_interfaces_impl.h
@@ -30,6 +30,16 @@
#include "rehlds_interfaces.h"
#include "server.h"
+const int NET_DECOMPRESS_MAX_TIMES = 10;
+
+struct FragStats_t
+{
+ float decompress_failure_times[NET_DECOMPRESS_MAX_TIMES];
+
+ // Count of abnormal fragment decompressions in a time window
+ int num_decompress_failures;
+};
+
class CNetChan : public INetChan
{
private:
@@ -38,6 +48,10 @@ private:
#ifdef REHLDS_FIXES
uint8_t m_messageBuffer[NET_MAX_PAYLOAD];
#endif
+
+ // Stats for decompression of incoming fragments
+ FragStats_t m_FragStats[MAX_STREAMS];
+
public:
CNetChan(netchan_t* chan);
@@ -46,10 +60,13 @@ public:
virtual netchan_t* GetChan();
+ void Clear();
+
public:
#ifdef REHLDS_FIXES
uint8_t* GetExtendedMessageBuffer() { return m_messageBuffer; };
#endif
+ FragStats_t &GetFragStats(int stream) { return m_FragStats[stream]; };
};
@@ -247,6 +264,9 @@ public:
uint8_t* GetExtendedMessageBuffer() { return m_NetChan.GetExtendedMessageBuffer(); };
#endif
+ void NetchanClear() { m_NetChan.Clear(); }
+ FragStats_t &GetFragStats(int stream) { return m_NetChan.GetFragStats(stream); };
+
#ifdef REHLDS_FIXES
void SetupLocalGameTime() { m_localGameTimeBase = g_psv.time; }
double GetLocalGameTime() const { return g_psv.time - m_localGameTimeBase; }
diff --git a/rehlds/rehlds/structSizeCheck.cpp b/rehlds/rehlds/structSizeCheck.cpp
index 259787c..485bd25 100644
--- a/rehlds/rehlds/structSizeCheck.cpp
+++ b/rehlds/rehlds/structSizeCheck.cpp
@@ -13,7 +13,7 @@ void check_size() {
void checkSizesStatic() {
- CHECK_TYPE_SIZE(client_t, 0x5018, 0x4EF4);
+// CHECK_TYPE_SIZE(client_t, 0x5018, 0x4EF4);
CHECK_TYPE_SIZE(userfilter_t, 0x20, 0x18);
#ifndef REHLDS_FIXES
CHECK_TYPE_SIZE(CSteam3Server, 0x90, 0xA8);