From df862d9bb6069084ee17b23135152c57b748e40e Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sun, 8 Dec 2024 00:18:36 +0700 Subject: [PATCH 01/10] Improved movevars sync logic for clients, allowing independent sync of movement props for each client, regardless of global movevars --- rehlds/engine/pmove.cpp | 4 +- rehlds/engine/pmove.h | 2 +- rehlds/engine/server.h | 7 +- rehlds/engine/sv_main.cpp | 233 +++++++++++++++++------------- rehlds/engine/sv_user.cpp | 8 + rehlds/pm_shared/pm_defs.h | 2 +- rehlds/rehlds/rehlds_api_impl.cpp | 14 +- rehlds/rehlds/structSizeCheck.cpp | 2 +- 8 files changed, 161 insertions(+), 111 deletions(-) diff --git a/rehlds/engine/pmove.cpp b/rehlds/engine/pmove.cpp index e281cd9d7..5245c4b93 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 8748d3a54..d610f8e56 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 9446f1461..1a9eb8e19 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 211a319ea..b6c870e02 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) -{ - 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; -} - -void SV_QueryMovevarsChanged(void) -{ - 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(); - - 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); - } - } +void sv_movevars_hook_callback(cvar_t *cvar) +{ + SV_SetMoveVars(&sv_movevars); +} + +void SV_SetMoveVars(movevars_t *movevars) +{ + 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; } 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) @@ -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_user.cpp b/rehlds/engine/sv_user.cpp index 584ca4fd7..26bbe1306 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -931,9 +931,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 (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 1c47d8f66..02f4c4c20 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 62a392070..e4f0e00bc 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/structSizeCheck.cpp b/rehlds/rehlds/structSizeCheck.cpp index 259787c3e..485bd2555 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); From 6e6368da300f7d024076e6272faa135280cf3bda Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sun, 8 Dec 2024 00:36:04 +0700 Subject: [PATCH 02/10] CalcSurfaceExtents: more info in extents error message --- rehlds/engine/model.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/rehlds/engine/model.cpp b/rehlds/engine/model.cpp index 162e4c5ce..e69d2c9fb 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] + ); + } } } From 18b173d5c6715eaf052a979984622f867661cd7b Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sun, 8 Dec 2024 05:15:11 +0700 Subject: [PATCH 03/10] Fix potential crash --- rehlds/engine/sv_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index b6c870e02..20ba978ff 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -6076,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; From e54adb089c816425bac43c7eaf6fc5b898401fb4 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sun, 8 Dec 2024 08:57:51 +0700 Subject: [PATCH 04/10] fix crash due fakeclient --- rehlds/engine/sv_user.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index 26bbe1306..e6bd874fb 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -939,7 +939,7 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) gEntityInterface.pfnPM_Move(pmove, TRUE); // Determine whether movevars has changed or not - if (Q_memcmp(&movevars, pmove->movevars, sizeof(movevars)) != 0) + 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; From 64c684af4a8e6030b9e06a59a774d6efd92db352 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sun, 8 Dec 2024 23:54:20 +0700 Subject: [PATCH 05/10] Added new CVARs for improved handling of decompression failures: - `sv_net_incoming_decompression_min_failures`: Min failures required to flag a player - `sv_net_incoming_decompression_max_failures`: max failures allowed before punishment - `sv_net_incoming_decompression_min_failuretime`: Time window for failure tracking --- README.md | 9 +- rehlds/engine/net.h | 13 +++ rehlds/engine/net_chan.cpp | 223 +++++++++++++++++++++++++++++-------- 3 files changed, 195 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 9d5f8ebf5..0da1d351d 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/net.h b/rehlds/engine/net.h index 072ba3330..1b8cb3927 100644 --- a/rehlds/engine/net.h +++ b/rehlds/engine/net.h @@ -276,6 +276,15 @@ typedef struct flow_s float avgkbytespersec; } flow_t; +const int NET_DECOMPRESS_MAX_TIMES = 10; + +typedef struct frag_decomp_failure_s +{ + float failure_times[NET_DECOMPRESS_MAX_TIMES]; + // Count of abnormal fragment decompressions in a time window + int num_failures; +} frag_decomp_failure_t; + const int FRAGMENT_C2S_MIN_SIZE = 16; const int FRAGMENT_S2C_MIN_SIZE = 256; const int FRAGMENT_S2C_MAX_SIZE = 1024; @@ -422,6 +431,10 @@ typedef struct netchan_s // Incoming and outgoing flow metrics flow_t flow[MAX_FLOWS]; + + // Stats for decompression of incoming fragments + frag_decomp_failure_t frag_decompress[MAX_STREAMS]; + } netchan_t; #ifdef REHLDS_FIXES diff --git a/rehlds/engine/net_chan.cpp b/rehlds/engine/net_chan.cpp index b4b75e9ce..b95b0a41b 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}; @@ -171,6 +174,10 @@ void Netchan_Clear(netchan_t *chan) chan->frag_startpos[i] = 0; chan->frag_length[i] = 0; chan->incomingready[i] = FALSE; + + for (int j = 0; j < NET_DECOMPRESS_MAX_TIMES; j++) + chan->frag_decompress[i].failure_times[j] = 0; + chan->frag_decompress[i].num_failures = 0; } if (chan->tempbuffer) @@ -1423,6 +1430,94 @@ 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; + frag_decomp_failure_t *decomp; + + 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(); + + decomp = &chan->frag_decompress[stream]; + + // check if the client should be rejected based on total failed decompress + if (decomp->num_failures >= sv_net_incoming_decompression_max_failures.value) + { + for (i = 0; i < sv_net_incoming_decompression_max_failures.value - 1; i++) + decomp->failure_times[i] = decomp->failure_times[i + 1]; + + decomp->num_failures = sv_net_incoming_decompression_max_failures.value - 1; + } + + decomp->failure_times[decomp->num_failures++] = realtime; + + // check if the client should be rejected based on recent failed decompress + int recent_failures = 0; + for (i = 0; i < decomp->num_failures; i++) + { + if ((realtime - decomp->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 +1578,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 +1586,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 +1603,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); - - success = FALSE; - } - } -#endif + qboolean success = TRUE; - // 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))); - } -#endif - - SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data"); - } - + // Drop client if decompression was unsuccessful + SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data"); SZ_Clear(&net_message); + return FALSE; } +#endif + + // 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 +1646,6 @@ qboolean Netchan_CopyFileFragments(netchan_t *chan) qboolean bCompressed; unsigned int uncompressedSize; - if (!chan->incomingready[FRAG_FILE_STREAM]) return FALSE; @@ -1587,6 +1657,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 +1784,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_NORMAL_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 +2025,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); From e83b3243018953e9fd619ebf54ecf2109274d17c Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 9 Dec 2024 01:21:00 +0700 Subject: [PATCH 06/10] Fix build --- rehlds/engine/net.h | 12 ------------ rehlds/engine/net_chan.cpp | 20 ++++++++------------ rehlds/rehlds/rehlds_interfaces_impl.cpp | 9 +++++++++ rehlds/rehlds/rehlds_interfaces_impl.h | 20 ++++++++++++++++++++ 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/rehlds/engine/net.h b/rehlds/engine/net.h index 1b8cb3927..c39c495fe 100644 --- a/rehlds/engine/net.h +++ b/rehlds/engine/net.h @@ -276,15 +276,6 @@ typedef struct flow_s float avgkbytespersec; } flow_t; -const int NET_DECOMPRESS_MAX_TIMES = 10; - -typedef struct frag_decomp_failure_s -{ - float failure_times[NET_DECOMPRESS_MAX_TIMES]; - // Count of abnormal fragment decompressions in a time window - int num_failures; -} frag_decomp_failure_t; - const int FRAGMENT_C2S_MIN_SIZE = 16; const int FRAGMENT_S2C_MIN_SIZE = 256; const int FRAGMENT_S2C_MAX_SIZE = 1024; @@ -432,9 +423,6 @@ typedef struct netchan_s // Incoming and outgoing flow metrics flow_t flow[MAX_FLOWS]; - // Stats for decompression of incoming fragments - frag_decomp_failure_t frag_decompress[MAX_STREAMS]; - } netchan_t; #ifdef REHLDS_FIXES diff --git a/rehlds/engine/net_chan.cpp b/rehlds/engine/net_chan.cpp index b95b0a41b..b1f9a3594 100644 --- a/rehlds/engine/net_chan.cpp +++ b/rehlds/engine/net_chan.cpp @@ -174,10 +174,6 @@ void Netchan_Clear(netchan_t *chan) chan->frag_startpos[i] = 0; chan->frag_length[i] = 0; chan->incomingready[i] = FALSE; - - for (int j = 0; j < NET_DECOMPRESS_MAX_TIMES; j++) - chan->frag_decompress[i].failure_times[j] = 0; - chan->frag_decompress[i].num_failures = 0; } if (chan->tempbuffer) @@ -191,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)); @@ -1460,7 +1457,6 @@ qboolean Netchan_ValidateDecompress(netchan_t *chan, int stream, unsigned int co { #ifdef REHLDS_FIXES int i; - frag_decomp_failure_t *decomp; if (sv_net_incoming_decompression_max_ratio.value <= 0) return TRUE; // validation is disabled @@ -1477,24 +1473,24 @@ qboolean Netchan_ValidateDecompress(netchan_t *chan, int stream, unsigned int co Netchan_DecompressionCvarsBounds(); - decomp = &chan->frag_decompress[stream]; + FragStats_t &stats = g_GameClients[chan->player_slot - 1]->GetFragStats(stream); // check if the client should be rejected based on total failed decompress - if (decomp->num_failures >= sv_net_incoming_decompression_max_failures.value) + 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++) - decomp->failure_times[i] = decomp->failure_times[i + 1]; + stats.decompress_failure_times[i] = stats.decompress_failure_times[i + 1]; - decomp->num_failures = sv_net_incoming_decompression_max_failures.value - 1; + stats.num_decompress_failures = sv_net_incoming_decompression_max_failures.value - 1; } - decomp->failure_times[decomp->num_failures++] = realtime; + 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 < decomp->num_failures; i++) + for (i = 0; i < stats.num_decompress_failures; i++) { - if ((realtime - decomp->failure_times[i]) <= sv_net_incoming_decompression_min_failuretime.value) + if ((realtime - stats.decompress_failure_times[i]) <= sv_net_incoming_decompression_min_failuretime.value) recent_failures++; } diff --git a/rehlds/rehlds/rehlds_interfaces_impl.cpp b/rehlds/rehlds/rehlds_interfaces_impl.cpp index fa07ac529..0922bfa9f 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 5cf4dba45..7c625472b 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 @@ class CNetChan : public INetChan #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 @@ class CNetChan : public INetChan 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 @@ class CGameClient : public IGameClient 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; } From 4afb6e3be9a65f2e9581c460bfe5bd46324e00d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Mu=C3=B1oz?= Date: Mon, 9 Dec 2024 09:20:17 -0300 Subject: [PATCH 07/10] Move SV_CheckMovingGround into SV_Physics (#1045) --- rehlds/engine/sv_phys.cpp | 23 +---------------------- rehlds/engine/sv_user.cpp | 24 +++++++++++------------- 2 files changed, 12 insertions(+), 35 deletions(-) diff --git a/rehlds/engine/sv_phys.cpp b/rehlds/engine/sv_phys.cpp index 8fff59515..520bbf3bc 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 e6bd874fb..adc90cad2 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) From 1a684077109023f86cfe9b70d6a1bba92a35c5f6 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 12 Dec 2024 04:35:38 +0700 Subject: [PATCH 08/10] Netchan_CopyFileFragments: fix typo --- rehlds/engine/net_chan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rehlds/engine/net_chan.cpp b/rehlds/engine/net_chan.cpp index b1f9a3594..1febfdbcc 100644 --- a/rehlds/engine/net_chan.cpp +++ b/rehlds/engine/net_chan.cpp @@ -1810,7 +1810,7 @@ qboolean Netchan_CopyFileFragments(netchan_t *chan) // malformed data or compressed data exceeding sv_net_incoming_decompression_max_size success = FALSE; } - else if (!Netchan_ValidateDecompress(chan, FRAG_NORMAL_STREAM, compressedSize, uncompressedSize)) + else if (!Netchan_ValidateDecompress(chan, FRAG_FILE_STREAM, compressedSize, uncompressedSize)) { success = FALSE; } From 28c205de1113b92a360f1dc1fd4f2bbd6225238b Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Sat, 14 Dec 2024 18:07:53 +0300 Subject: [PATCH 09/10] CI: add `workflow_dispatch` event trigger [skip ci] --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d6297a477..97a7a46b2 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: From 20c16bfad332be24967cd447f4808d5725d5f3a9 Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Wed, 18 Dec 2024 20:07:52 +0300 Subject: [PATCH 10/10] `IP` and `IPX` allocation warnings move under `-dev` arg (#1071) --- rehlds/engine/net_ws.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rehlds/engine/net_ws.cpp b/rehlds/engine/net_ws.cpp index 4880da9b7..b9c52b1bd 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 {