From 12b35dbe8c9b9e74fa431d233dc1ffb9a33ef0fd Mon Sep 17 00:00:00 2001 From: STAM Date: Wed, 20 Nov 2024 00:25:27 +0300 Subject: [PATCH 01/20] deleted local GitHub Action --- .github/workflows/sync-with-gitlab.yml | 31 -------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/sync-with-gitlab.yml diff --git a/.github/workflows/sync-with-gitlab.yml b/.github/workflows/sync-with-gitlab.yml deleted file mode 100644 index 3314aff..0000000 --- a/.github/workflows/sync-with-gitlab.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Automatic sync with Gitlab's read-only mirror - -on: [workflow_dispatch, push] - -jobs: - mirror: - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Set up SSH key for Runner - env: - SSH_KEY: ${{ secrets.GITLAB_DEPLOY_KEY }} - run: | - mkdir -p ~/.ssh - echo "$SSH_KEY" > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts - ssh-keyscan -t rsa gitlab.com >> ~/.ssh/known_hosts - - - name: Clone repository from GitHub as mirror and push to Gitlab - env: - REPO_ORIGINAL: "https://github.com/rehlds/rehlds.git" - REPO_TARGET: "git@gitlab.com:rehlds/rehlds.git" - run: | - git clone --mirror "$REPO_ORIGINAL" repo-mirror - cd repo-mirror - git remote set-url origin "$REPO_TARGET" - git push --mirror --force From df862d9bb6069084ee17b23135152c57b748e40e Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sun, 8 Dec 2024 00:18:36 +0700 Subject: [PATCH 02/20] 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 | 223 +++++++++++++++++------------- 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, 156 insertions(+), 106 deletions(-) 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..b6c870e 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) @@ -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 584ca4f..26bbe13 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 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/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); From 6e6368da300f7d024076e6272faa135280cf3bda Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sun, 8 Dec 2024 00:36:04 +0700 Subject: [PATCH 03/20] 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 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] + ); + } } } From 18b173d5c6715eaf052a979984622f867661cd7b Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sun, 8 Dec 2024 05:15:11 +0700 Subject: [PATCH 04/20] 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 b6c870e..20ba978 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 05/20] 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 26bbe13..e6bd874 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 06/20] 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 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/net.h b/rehlds/engine/net.h index 072ba33..1b8cb39 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 b4b75e9..b95b0a4 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); + 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 +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 07/20] 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 1b8cb39..c39c495 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 b95b0a4..b1f9a35 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 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; } 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 08/20] 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 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 e6bd874..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) From 1a684077109023f86cfe9b70d6a1bba92a35c5f6 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 12 Dec 2024 04:35:38 +0700 Subject: [PATCH 09/20] 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 b1f9a35..1febfdb 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 10/20] 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 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: From 20c16bfad332be24967cd447f4808d5725d5f3a9 Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Wed, 18 Dec 2024 20:07:52 +0300 Subject: [PATCH 11/20] `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 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 { From e9991716fc908e8b2ebf8ef5004c2a6f098f6eb5 Mon Sep 17 00:00:00 2001 From: STAM Date: Thu, 26 Dec 2024 11:29:08 +0300 Subject: [PATCH 12/20] Added codesign and resources (#1069) * Added codesign and resources - for Windows: https://github.com/rehlds/.github/issues/30 and https://github.com/rehlds/.github/issues/29 - for Linux: https://github.com/rehlds/.github/issues/28 --- .github/workflows/build.yml | 116 ++++++++++++++++++++++++++++++++++-- README.md | 12 +++- 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97a7a46..5c7d66a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,6 +47,11 @@ jobs: msbuild ${{ env.solution }} -p:Configuration="${{ env.buildRelease }}" /t:Clean,Build /p:Platform=${{ env.buildPlatform }} /p:PlatformToolset=v140_xp /p:XPDeprecationWarning=false msbuild ${{ env.solution }} -p:Configuration="${{ env.buildReleasePlay }}" /t:Clean,Build /p:Platform=${{ env.buildPlatform }} /p:PlatformToolset=v140_xp /p:XPDeprecationWarning=false + - name: Get rcedit from chocolatey + run: | + choco install rcedit -y + shell: "pwsh" + - name: Move files run: | mkdir publish\debug @@ -70,6 +75,39 @@ jobs: move msvc\${{ env.buildRelease }}\filesystem_stdio.pdb publish\debug\filesystem_stdio.pdb move msvc\${{ env.buildRelease }}\director.pdb publish\debug\director.pdb + # TODO: Set version to exe dynamicly: 0.0.0.0 to normal version such as at linux + - name: Edit resources at windows binaries + run: | + rcedit ${{ github.workspace }}\publish\bin\win32\hlds.exe --set-version-string ProductName "ReHLDS" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "The Half-Life Dedicated Server, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" + rcedit ${{ github.workspace }}\publish\bin\win32\hltv.exe --set-version-string ProductName "ReHLTV" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "The Half-Life TV, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" + rcedit ${{ github.workspace }}\publish\tests\swds.dll --set-version-string ProductName "swds.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll used by Steamworks Dedicated Servers, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico + rcedit ${{ github.workspace }}\publish\bin\win32\swds.dll --set-version-string ProductName "swds.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A ddll used by Steamworks Dedicated Servers, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico + rcedit ${{ github.workspace }}\publish\bin\win32\core.dll --set-version-string ProductName "core.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription " A dll, it is a core of game engine, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico + rcedit ${{ github.workspace }}\publish\bin\win32\proxy.dll --set-version-string ProductName "proxy.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll for proxying network connections, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico + rcedit ${{ github.workspace }}\publish\bin\win32\demoplayer.dll --set-version-string ProductName "demoplayer.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll for demoplayer functionality, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico + rcedit ${{ github.workspace }}\publish\bin\win32\filesystem_stdio.dll --set-version-string ProductName "filesystem_stdio.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll that manages file input/output operations, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico + rcedit ${{ github.workspace }}\publish\bin\win32\valve\dlls\director.dll --set-version-string ProductName "director.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll used for Director functionality in Half-Life 1, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico + shell: "pwsh" + + - name: Import PFX and sign + env: + KEY_PFX_PASS: ${{ secrets.KEY_PFX_PASS}} + run: | + $pfxBase64 = "${{ secrets.KEY_PFX_B64 }}" + [IO.File]::WriteAllBytes("${{ github.workspace }}\signing-cert.pfx", [Convert]::FromBase64String($pfxBase64)) + certutil -f -p "${{ secrets.KEY_PFX_PASS }}" -importPFX "${{ github.workspace }}\signing-cert.pfx" + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\hlds.exe + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "reHLTV" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\hltv.exe + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - swds.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\tests\swds.dll + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - swds.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\swds.dll + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - core.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\core.dll + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - proxy.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\proxy.dll + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - demoplayer.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\demoplayer.dll + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - filesystem_stdio.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\filesystem_stdio.dll + & 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - director.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\valve\dlls\director.dll + Remove-Item -Recurse -Force "${{ github.workspace }}\signing-cert.pfx" + shell: "pwsh" + - name: Deploy artifacts uses: actions/upload-artifact@v4 with: @@ -78,7 +116,7 @@ jobs: testdemos: name: 'Test demos' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 container: rehldsorg/testdemos:latest needs: [windows] defaults: @@ -116,7 +154,7 @@ jobs: linux: name: 'Linux' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 container: debian:11-slim steps: @@ -136,6 +174,48 @@ jobs: git cmake rsync \ g++ gcc + - name: GPG Import + run: | + echo "${{ secrets.PUB_ASC }}" > "${{ secrets.PUB_ASC_FILE }}" + echo "${{ secrets.KEY_ASC }}" > "${{ secrets.KEY_ASC_FILE }}" + + # Import the public key + gpg --batch --yes --import "${{ secrets.PUB_ASC_FILE }}" + if [[ $? -ne 0 ]]; then + echo "Error: Failed to import the public key" + exit 1 + fi + + # Import the private key + gpg --batch --yes --import "${{ secrets.KEY_ASC_FILE }}" + if [[ $? -ne 0 ]]; then + echo "Error: Failed to import the private key" + exit 2 + fi + + # Extract the fingerprint of the imported public key + GPG_LINUX_FINGERPRINT=$(gpg --list-keys --with-colons | grep '^fpr' | head -n 1 | cut -d: -f10) + + # Check if the fingerprint was extracted + if [[ -z "$GPG_LINUX_FINGERPRINT" ]]; then + echo "Error: Failed to extract the fingerprint of the key" + exit 3 + fi + + # Set the trust level for the key + echo "$GPG_LINUX_FINGERPRINT:6:" | gpg --batch --import-ownertrust + if [ $? -ne 0 ]; then + echo "Error: Failed to set trust for the key $GPG_LINUX_FINGERPRINT" + exit 4 + fi + + echo "Key $GPG_LINUX_FINGERPRINT successfully imported and trusted" + gpg --list-keys + + #export for global use + echo "GPG_LINUX_FINGERPRINT=$GPG_LINUX_FINGERPRINT" >> $GITHUB_ENV + shell: bash + - name: Build and Run unittests run: | rm -rf build && cmake -DCMAKE_BUILD_TYPE=Unittests -B build && cmake --build build -j8 @@ -213,7 +293,7 @@ jobs: publish: name: 'Publish' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 needs: [windows, testdemos, linux] steps: @@ -241,15 +321,42 @@ jobs: fi rm -f appversion.h - - name: Packaging bin/dbg + - name: Final signing and Packaging bin/dbg id: packaging-job if: | github.event_name == 'release' && github.event.action == 'published' && startsWith(github.ref, 'refs/tags/') run: | + + # new runner, niw signs + echo "${{ secrets.PUB_ASC }}" > "${{ secrets.PUB_ASC_FILE }}" + echo "${{ secrets.KEY_ASC }}" > "${{ secrets.KEY_ASC_FILE }}" + gpg --batch --yes --import "${{ secrets.PUB_ASC_FILE }}" + gpg --batch --yes --import "${{ secrets.KEY_ASC_FILE }}" + GPG_LINUX_FINGERPRINT=$(gpg --list-keys --with-colons | grep '^fpr' | head -n 1 | cut -d: -f10) + echo "$GPG_LINUX_FINGERPRINT:6:" | gpg --batch --import-ownertrust + echo "GPG_LINUX_FINGERPRINT=$GPG_LINUX_FINGERPRINT" >> $GITHUB_ENV + + sign_file() { + local file=$1 + gpg --batch --yes --detach-sign --armor -u "$GPG_LINUX_FINGERPRINT" "$file" + if [ $? -ne 0 ]; then + echo "Error: Failed to sign $file" + exit 2 + fi + echo "$file signed successfully." + } + + # Pack and sign final archive 7z a -tzip rehlds-bin-${{ env.APP_VERSION }}.zip bin/ hlsdk/ + sign_file "rehlds-bin-${{ env.APP_VERSION }}.zip" + + # Pack and sign final archive 7z a -t7z -m0=lzma2 -mx=9 -mfb=64 -aoa rehlds-dbg-${{ env.APP_VERSION }}.7z debug/ + sign_file "rehlds-dbg-${{ env.APP_VERSION }}.7z" + + shell: bash - name: Publish artifacts uses: softprops/action-gh-release@v2 @@ -261,5 +368,6 @@ jobs: files: | *.zip *.7z + *.asc env: GITHUB_TOKEN: ${{ secrets.API_TOKEN }} diff --git a/README.md b/README.md index 0da1d35..3ca47c7 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,15 @@ Along with reverse engineering, a lot of defects and (potential) bugs were found You can try playing on one of many servers that are using ReHLDS: [Game Tracker](http://www.gametracker.com/search/?search_by=server_variable&search_by2=sv_version) +> [!TIP] +> ReHLDS linux-releases now is signed via `GPG`, pubkey is: `63547829004f07716f7be4856c32c4282e60fb67` and could be found at [https://keyserver.ubuntu.com/](https://keyserver.ubuntu.com/pks/lookup?search=63547829004f07716f7be4856c32c4282e60fb67+&fingerprint=on&op=index). +> +> How to: +> 1. [Download](https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x63547829004f07716f7be4856c32c4282e60fb67) `63547829004f07716f7be4856c32c4282e60fb67.asc` key +> 2. Import: `gpg --import 63547829004f07716f7be4856c32c4282e60fb67.asc` +> 3. Download release `archive` and `.asc` file. +> 4. Verify: `gpg --verify some-rehlds.zip.asc some-rehlds.zip`. + ## Goals of the project
    • Provide more stable (than official) version of Half-Life dedicated server with extended API for mods and plugins
    • @@ -17,7 +26,8 @@ You can try playing on one of many servers that are using ReHLDS: [Game Tracker] ## How can use it? 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. +> [!CAUTION] +> ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool. #### Downloading HLDS via steamcmd From 89958d348ea617227a4e4360b76900423527bd9e Mon Sep 17 00:00:00 2001 From: s1lentq Date: Fri, 28 Mar 2025 02:41:18 +0700 Subject: [PATCH 13/20] Update build.yml --- .github/workflows/build.yml | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5c7d66a..8547aa3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,9 @@ jobs: buildTest: 'Test Fixes' steps: + - name: Configure + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: Checkout uses: actions/checkout@v4 with: @@ -90,8 +93,9 @@ jobs: shell: "pwsh" - name: Import PFX and sign + if: github.event_name != 'pull_request' env: - KEY_PFX_PASS: ${{ secrets.KEY_PFX_PASS}} + KEY_PFX_PASS: ${{ secrets.KEY_PFX_PASS }} run: | $pfxBase64 = "${{ secrets.KEY_PFX_B64 }}" [IO.File]::WriteAllBytes("${{ github.workspace }}\signing-cert.pfx", [Convert]::FromBase64String($pfxBase64)) @@ -158,11 +162,6 @@ jobs: container: debian:11-slim steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Install dependencies run: | dpkg --add-architecture i386 @@ -174,6 +173,14 @@ jobs: git cmake rsync \ g++ gcc + - name: Configure + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: GPG Import run: | echo "${{ secrets.PUB_ASC }}" > "${{ secrets.PUB_ASC_FILE }}" From 45fe816fdd6bd9eca557455fcf2d358ae2f5fe16 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sat, 29 Mar 2025 02:46:36 +0700 Subject: [PATCH 14/20] API game message manager: add support signed/unsigned value for char,short types --- rehlds/rehlds/rehlds_messagemngr_impl.cpp | 91 ++++++++++++++--------- rehlds/rehlds/rehlds_messagemngr_impl.h | 2 +- 2 files changed, 56 insertions(+), 37 deletions(-) diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.cpp b/rehlds/rehlds/rehlds_messagemngr_impl.cpp index 5340519..fa4a526 100644 --- a/rehlds/rehlds/rehlds_messagemngr_impl.cpp +++ b/rehlds/rehlds/rehlds_messagemngr_impl.cpp @@ -213,13 +213,13 @@ private: void setCopybackBuffer(sizebuf_t *pbuf); // Adds a parameter to the message - void addParam(IMessage::ParamType type, size_t length); + void addParam(IMessage::ParamType type, bool sign, size_t length); // Clears the message after execution void clear(); template - void setParamPrimitive(size_t index, T value); + void setParamPrimitive(size_t index, T value, bool sign = false); // Transforms buffer after sets string for a parameter at the given index void setTxformBuffer(size_t index, size_t startPos, size_t oldLength, size_t newLength); @@ -263,6 +263,7 @@ private: size_t posFront : 9; // The stock position of the parameter in the buffer size_t oldlen : 9; // The length of the parameter in the buffer size_t newlen : 9; // The length of the parameter in the buffer + bool sign[MAX_STORAGE]; // Flag indicating that it has a signed value }; #pragma pack(pop) @@ -369,17 +370,31 @@ static size_t SIZEOF_PARAMTYPE[] = }; // Adds a parameter to the message -void MessageImpl::addParam(IMessage::ParamType type, size_t length) +void MessageImpl::addParam(IMessage::ParamType type, bool sign, size_t length) { Param_t ¶m = m_params[m_paramCount++]; - param.type = type; - param.newlen = param.oldlen = (length == -1) ? SIZEOF_PARAMTYPE[static_cast(type)] : length; - param.posBack = param.posFront = gMsgBuffer.cursize; + param.type = type; + param.newlen = param.oldlen = (length == -1) ? SIZEOF_PARAMTYPE[static_cast(type)] : length; + param.posBack = param.posFront = gMsgBuffer.cursize; + param.sign[BACK] = param.sign[FRONT] = sign; +} + +template +inline void setValue(void *pbuf, T value) +{ + *static_cast(pbuf) = value; +} + +template +inline int getValueInt(const void *pbuf, bool sign = false) +{ + return sign ? + *static_cast(pbuf) : *static_cast *>(pbuf); } // Sets the value of a primitive parameter at the given index template -void MessageImpl::setParamPrimitive(size_t index, T value) +void MessageImpl::setParamPrimitive(size_t index, T value, bool sign) { // Ensure index is within bounds if (index >= m_paramCount) @@ -392,17 +407,17 @@ void MessageImpl::setParamPrimitive(size_t index, T value) switch (param.type) { case IMessage::ParamType::Byte: - *(uint8 *)pbuf = value; + setValue(pbuf, value); break; case IMessage::ParamType::Char: - *(int8 *)pbuf = value; + setValue(pbuf, value); break; case IMessage::ParamType::Short: case IMessage::ParamType::Entity: - *(int16 *)pbuf = value; + setValue(pbuf, value); break; case IMessage::ParamType::Long: - *(uint32 *)pbuf = value; + setValue(pbuf, value); break; case IMessage::ParamType::Angle: // Convert angle value to byte representation with loss of precision @@ -410,12 +425,14 @@ void MessageImpl::setParamPrimitive(size_t index, T value) break; case IMessage::ParamType::Coord: // Convert coordinate value to short integer representation with loss of precision - *(int16 *)pbuf = (int16)(int)(value * 8.0); + setValue(pbuf, (int)(value * 8.0)); break; default: return; // bad type } + param.sign[BACK] = sign; + // Mark message as modified param.modified = true; @@ -469,14 +486,14 @@ int MessageImpl::getParamInt(size_t index) const switch (param.type) { case IMessage::ParamType::Byte: - return *(uint8 *)buf; + return getValueInt(buf); case IMessage::ParamType::Char: - return *(int8 *)buf; + return getValueInt(buf, param.sign[BACK]); case IMessage::ParamType::Short: case IMessage::ParamType::Entity: - return *(int16 *)buf; + return getValueInt(buf, param.sign[BACK]); case IMessage::ParamType::Long: - return *(uint32 *)buf; + return getValueInt(buf); default: return 0; // bad type } @@ -495,9 +512,9 @@ float MessageImpl::getParamFloat(size_t index) const switch (param.type) { case IMessage::ParamType::Angle: - return (float)(*(uint8 *)buf * (360.0 / 256.0)); + return (float)((uint8)getValueInt(buf) * (360.0 / 256.0)); case IMessage::ParamType::Coord: - return (float)(*(int16 *)buf * (1.0 / 8)); + return (float)((int16)getValueInt(buf) * (1.0 / 8)); default: break; // bad type } @@ -532,14 +549,14 @@ int MessageImpl::getOriginalParamInt(size_t index) const switch (param.type) { case IMessage::ParamType::Byte: - return *(uint8 *)buf; + return getValueInt(buf); case IMessage::ParamType::Char: - return *(int8 *)buf; + return getValueInt(buf, param.sign[FRONT]); case IMessage::ParamType::Short: case IMessage::ParamType::Entity: - return *(int16 *)buf; + return getValueInt(buf, param.sign[FRONT]); case IMessage::ParamType::Long: - return *(uint32 *)buf; + return getValueInt(buf); default: return 0; // bad type } @@ -557,9 +574,9 @@ float MessageImpl::getOriginalParamFloat(size_t index) const switch (param.type) { case IMessage::ParamType::Angle: - return (float)(*(uint8 *)buf * (360.0 / 256.0)); + return (float)((uint8)getValueInt(buf) * (360.0 / 256.0)); case IMessage::ParamType::Coord: - return (float)(*(int16 *)buf * (1.0 / 8)); + return (float)((int16)getValueInt(buf) * (1.0 / 8)); default: break; // bad type } @@ -584,7 +601,7 @@ const char *MessageImpl::getOriginalParamString(size_t index) const // Sets the integer value of the parameter at the given index void MessageImpl::setParamInt(size_t index, int value) { - setParamPrimitive(index, value); + setParamPrimitive(index, value, value < 0); } // Sets the float value of the parameter at the given index @@ -710,23 +727,23 @@ void MessageImpl::resetParam(size_t index) switch (param.type) { case IMessage::ParamType::Byte: - *(uint8 *)pbackbuf = *(uint8 *)pfrontbuf; + setValue(pbackbuf, getValueInt(pfrontbuf)); break; case IMessage::ParamType::Char: - *(int8 *)pbackbuf = *(int8 *)pfrontbuf; + setValue(pbackbuf, getValueInt(pfrontbuf)); break; case IMessage::ParamType::Short: case IMessage::ParamType::Entity: - *(int16 *)pbackbuf = *(int16 *)pfrontbuf; + setValue(pbackbuf, getValueInt(pfrontbuf)); break; case IMessage::ParamType::Long: - *(uint32 *)pbackbuf = *(uint32 *)pfrontbuf; + setValue(pbackbuf, getValueInt(pfrontbuf)); break; case IMessage::ParamType::Angle: - *(uint8 *)pbackbuf = *(uint8 *)pfrontbuf; + setValue(pbackbuf, getValueInt(pfrontbuf)); break; case IMessage::ParamType::Coord: - *(int16 *)pbackbuf = *(int16 *)pfrontbuf; + setValue(pbackbuf, getValueInt(pfrontbuf)); break; case IMessage::ParamType::String: // Return the original string value from the front buffer @@ -740,6 +757,8 @@ void MessageImpl::resetParam(size_t index) // Unmark message as modified param.modified = false; + + param.sign[BACK] = param.sign[FRONT]; } // Resets a specific message parameter to its original value @@ -955,7 +974,7 @@ bool MessageManagerImpl::MessageEnd() return false; } -bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length) +bool MessageManagerImpl::WriteParam(IMessage::ParamType type, bool sign, size_t length) { // Check if in block mode if (m_inblock) @@ -966,7 +985,7 @@ bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length) { // Add parameter to top stack message MessageImpl &msg = m_stack.top(); - msg.addParam(type, length); + msg.addParam(type, sign, length); } return true; @@ -1002,13 +1021,13 @@ void EXT_FUNC PF_WriteByte_Intercept(int iValue) void EXT_FUNC PF_WriteChar_Intercept(int iValue) { - if (MessageManager().WriteParam(IMessage::ParamType::Char)) + if (MessageManager().WriteParam(IMessage::ParamType::Char, iValue < 0)) PF_WriteChar_I(iValue); } void EXT_FUNC PF_WriteShort_Intercept(int iValue) { - if (MessageManager().WriteParam(IMessage::ParamType::Short)) + if (MessageManager().WriteParam(IMessage::ParamType::Short, iValue < 0)) PF_WriteShort_I(iValue); } @@ -1038,7 +1057,7 @@ void EXT_FUNC PF_WriteString_Intercept(const char *sz) void EXT_FUNC PF_WriteEntity_Intercept(int iValue) { - if (MessageManager().WriteParam(IMessage::ParamType::Entity)) + if (MessageManager().WriteParam(IMessage::ParamType::Entity, iValue < 0)) PF_WriteEntity_I(iValue); } diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.h b/rehlds/rehlds/rehlds_messagemngr_impl.h index f016b19..dbb9636 100644 --- a/rehlds/rehlds/rehlds_messagemngr_impl.h +++ b/rehlds/rehlds/rehlds_messagemngr_impl.h @@ -93,7 +93,7 @@ private: bool MessageEnd(); private: - bool WriteParam(IMessage::ParamType type, size_t length = -1); + bool WriteParam(IMessage::ParamType type, bool sign = false, size_t length = -1); bool m_inblock; // Flag indicating whether a message block is currently active bool m_inhook; // Flag indicating whether a message hook is currently active From a2953ea3749b25f21e424defb7e277280b937f88 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Tue, 1 Apr 2025 22:26:38 +0700 Subject: [PATCH 15/20] API game message manager: fix typo --- rehlds/rehlds/rehlds_messagemngr_impl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.cpp b/rehlds/rehlds/rehlds_messagemngr_impl.cpp index fa4a526..6179504 100644 --- a/rehlds/rehlds/rehlds_messagemngr_impl.cpp +++ b/rehlds/rehlds/rehlds_messagemngr_impl.cpp @@ -421,7 +421,7 @@ void MessageImpl::setParamPrimitive(size_t index, T value, bool sign) break; case IMessage::ParamType::Angle: // Convert angle value to byte representation with loss of precision - *(uint8 *)pbuf = (int64)(fmod((double)value, 360.0) * 256.0 / 360.0) & 0xff; + setValue(pbuf, (int64)(fmod((double)value, 360.0) * 256.0 / 360.0) & 0xff); break; case IMessage::ParamType::Coord: // Convert coordinate value to short integer representation with loss of precision @@ -1051,7 +1051,7 @@ void EXT_FUNC PF_WriteCoord_Intercept(float flValue) void EXT_FUNC PF_WriteString_Intercept(const char *sz) { - if (MessageManager().WriteParam(IMessage::ParamType::String, sz ? Q_strlen(sz) + 1 : 1)) + if (MessageManager().WriteParam(IMessage::ParamType::String, false, sz ? Q_strlen(sz) + 1 : 1)) PF_WriteString_I(sz); } From 2f87ace7fc380e2710518b0e25b14409f0b53de6 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Wed, 2 Apr 2025 21:16:17 +0700 Subject: [PATCH 16/20] Fix dumb bug where skip logos without advancing to next iter, causing an endless loop when sv_send_logos is disabled Fixes #1085 --- rehlds/engine/sv_main.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 20ba978..6b70b88 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -5781,8 +5781,12 @@ void SV_PropagateCustomizations(void) pResource = &pCust->resource; #ifdef REHLDS_FIXES + // skip logos if sv_send_logos is 0 if ((pResource->ucFlags & RES_CUSTOM) && !sv_send_logos.value) + { + pCust = pCust->pNext; continue; + } #endif MSG_WriteByte(&host_client->netchan.message, svc_customization); From 98b4103b868f97a01793b70f1cdb7a555519cd82 Mon Sep 17 00:00:00 2001 From: Splatt581 <31417200+Splatt581@users.noreply.github.com> Date: Fri, 4 Apr 2025 01:08:30 +0300 Subject: [PATCH 17/20] Added check for invalid utf8 chars in userinfo (#1074) --- rehlds/engine/info.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rehlds/engine/info.cpp b/rehlds/engine/info.cpp index 6680d35..d0a36e3 100644 --- a/rehlds/engine/info.cpp +++ b/rehlds/engine/info.cpp @@ -856,6 +856,12 @@ qboolean Info_IsValid(const char *s) return false; }; + // invalid utf8 chars are deprecated + if (!Q_UnicodeValidate(s)) + { + return FALSE; + } + while (*s == '\\') { const char* key = ++s; From 7bc5d86add2dc4b8bbafd427180f50c860620c76 Mon Sep 17 00:00:00 2001 From: Faruk <98034193+farukerdem34@users.noreply.github.com> Date: Fri, 4 Apr 2025 01:09:13 +0300 Subject: [PATCH 18/20] cmake dependecy (#1080) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3ca47c7..7533e4b 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,7 @@ sudo apt-get update sudo apt-get install -y gcc-multilib g++-multilib sudo apt-get install -y build-essential sudo apt-get install -y libc6-dev libc6-dev-i386 +sudo apt-get install -y cmake From 44e510a25cba2f48f2d70f6074abb94dc4631cf1 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Sat, 5 Apr 2025 02:20:58 +0700 Subject: [PATCH 19/20] SV_EmitSound2: Add new flag SND_EMIT2_USE_ORIGIN for using custom origin instead of entity origin --- rehlds/common/const.h | 1 + rehlds/rehlds/rehlds_api_impl.cpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/rehlds/common/const.h b/rehlds/common/const.h index 721a8da..f498db1 100644 --- a/rehlds/common/const.h +++ b/rehlds/common/const.h @@ -74,6 +74,7 @@ // SV_EmitSound2 flags #define SND_EMIT2_NOPAS (1<<0) // never to do check PAS #define SND_EMIT2_INVOKER (1<<1) // do not send to the client invoker +#define SND_EMIT2_USE_ORIGIN (1<<2) // use given origin instead of entity origin // Engine edict->spawnflags #define SF_NOTINDEATHMATCH 0x0800 // Do not spawn when deathmatch and loading entities from a file diff --git a/rehlds/rehlds/rehlds_api_impl.cpp b/rehlds/rehlds/rehlds_api_impl.cpp index e4f0e00..9cdd7e1 100644 --- a/rehlds/rehlds/rehlds_api_impl.cpp +++ b/rehlds/rehlds/rehlds_api_impl.cpp @@ -579,7 +579,7 @@ bool EXT_FUNC SV_EmitSound2_internal(edict_t *entity, IGameClient *pReceiver, in bool bSendPAS = (channel != CHAN_STATIC && !(flags & SND_FL_STOP) && !(emitFlags & SND_EMIT2_NOPAS)); vec3_t origin = {0, 0, 0}; - if (entity && entity != g_psv.edicts) + if ((!pOrigin || !(emitFlags & SND_EMIT2_USE_ORIGIN)) && entity && entity != g_psv.edicts) { for (int i = 0; i < 3; ++i) origin[i] = (entity->v.maxs[i] + entity->v.mins[i]) * 0.5f + entity->v.origin[i]; From 5cac7a97ab64bf7c09e420bd282b0965be063abf Mon Sep 17 00:00:00 2001 From: STAM Date: Sun, 6 Apr 2025 14:16:04 +0300 Subject: [PATCH 20/20] also disable sign at linux builds from PRs --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8547aa3..fecffe8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -222,6 +222,7 @@ jobs: #export for global use echo "GPG_LINUX_FINGERPRINT=$GPG_LINUX_FINGERPRINT" >> $GITHUB_ENV shell: bash + if: github.event_name != 'pull_request' - name: Build and Run unittests run: |