From 174414db81116b3647e7bb1b906417d6a38ed3a3 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 18 Mar 2024 21:57:57 +0700 Subject: [PATCH 01/31] Draw_ValidateCustomLogo: Minor refactoring & cleanup --- rehlds/engine/decals.cpp | 79 ++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/rehlds/engine/decals.cpp b/rehlds/engine/decals.cpp index 975d7f9..1705b7f 100644 --- a/rehlds/engine/decals.cpp +++ b/rehlds/engine/decals.cpp @@ -717,14 +717,9 @@ NOXREF qboolean Draw_CacheReload(cachewad_t *wad, int i, lumpinfo_t *pLump, cach qboolean Draw_ValidateCustomLogo(cachewad_t *wad, unsigned char *data, lumpinfo_t *lump) { texture_t tex; - miptex_t *mip; - miptex_t tmp; - int pix; - int pixoffset; - int paloffset; - int palettesize; - int nPalleteCount; - int nSize; + miptex_t *mip, tmp; + int i, pix, paloffset, palettesize; + int size; if (wad->cacheExtra != DECAL_EXTRASIZE) { @@ -734,58 +729,54 @@ qboolean Draw_ValidateCustomLogo(cachewad_t *wad, unsigned char *data, lumpinfo_ tex = *(texture_t *)data; mip = (miptex_t *)(data + wad->cacheExtra); - tmp = *mip; + // Copy mip texture data + tmp = *mip; tex.width = LittleLong(tmp.width); tex.height = LittleLong(tmp.height); - tex.anim_max = 0; - tex.anim_min = 0; - tex.anim_total = 0; - tex.alternate_anims = NULL; - tex.anim_next = NULL; + tex.anim_total = tex.anim_min = tex.anim_max = 0; + tex.alternate_anims = tex.anim_next = NULL; - if (!tex.width || tex.width > 256 || tex.height > 256) - { - Con_Printf("%s: Bad wad dimensions %s\n", __func__, wad->name); - return FALSE; - } - - for (int i = 0; i < MIPLEVELS; i++) + for (i = 0; i < MIPLEVELS; i++) tex.offsets[i] = wad->cacheExtra + LittleLong(tmp.offsets[i]); + if (tex.width <= 0 || tex.height <= 0 || + // Check if texture dimensions exceed limits + tex.width > 256 || tex.height > 256) + { + Con_Printf("%s: Bad cached wad tex size %ux%u on %s\n", __func__, tex.width, tex.height, wad->name); + return FALSE; + } + pix = tex.width * tex.height; - pixoffset = pix + (pix >> 2) + (pix >> 4) + (pix >> 6); + size = pix + (pix >> 2) + (pix >> 4) + (pix >> 6); -#ifdef REHLDS_FIXES - // Ensure that pixoffset won't be exceed the pre allocated buffer - // This can happen when there are no color palettes in payload - if ((pixoffset + sizeof(texture_t)) >= (unsigned)(wad->cacheExtra + lump->size)) + if ((unsigned)(size + sizeof(miptex_t)) >= (unsigned)(lump->size + wad->cacheExtra)) { - Con_Printf("%s: Bad wad payload size %s\n", __func__, wad->name); - return FALSE; + Con_Printf("%s: Bad cached wad size %i/%i on %s\n", __func__, size + sizeof(miptex_t), lump->size + wad->cacheExtra, wad->name); } -#endif - paloffset = (pix >> 2) + tmp.offsets[0] + pix; - palettesize = (pix >> 4) + paloffset; + paloffset = size + sizeof(lumpinfo_t) + sizeof(miptex_t); + palettesize = *(u_short *)(data + paloffset); // Get palette size - if ((tmp.offsets[0] + pix != tmp.offsets[1]) - || paloffset != tmp.offsets[2] - || palettesize != tmp.offsets[3]) + for (i = 0; i < 3; i++) { - Con_Printf("%s: Bad cached wad %s\n", __func__, wad->name); + // Check if offsets are valid for mip levels + if (pix + tmp.offsets[i] != tmp.offsets[i + 1]) + { + Con_Printf("%s: Bad cached wad %s\n", __func__, wad->name); + return FALSE; + } + pix >>= 2; + } + + if (palettesize > 256) + { + Con_Printf("%s: Bad cached wad palette size %i on %s\n", __func__, palettesize, wad->name); return FALSE; } - nPalleteCount = *(u_short *)(data + pixoffset + sizeof(texture_t)); - if (nPalleteCount > 256) - { - Con_Printf("%s: Bad cached wad palette size %i on %s\n", __func__, nPalleteCount, wad->name); - return FALSE; - } - - nSize = pixoffset + LittleLong(tmp.offsets[0]) + 3 * nPalleteCount + 2; - if (nSize > lump->disksize) + if ((palettesize + 2 * (palettesize + 1) + size + LittleLong(tmp.offsets[0])) > lump->disksize) { Con_Printf("%s: Bad cached wad %s\n", __func__, wad->name); return FALSE; From 516bb936271b1cf8523d6f97a421370128a7c964 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 18 Mar 2024 21:59:18 +0700 Subject: [PATCH 02/31] HPAK_ResourceForHash: Remove message with missing custom.hpk --- rehlds/engine/hashpak.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rehlds/engine/hashpak.cpp b/rehlds/engine/hashpak.cpp index 3e76012..2befee6 100644 --- a/rehlds/engine/hashpak.cpp +++ b/rehlds/engine/hashpak.cpp @@ -620,7 +620,9 @@ qboolean HPAK_ResourceForHash(char *pakname, unsigned char *hash, struct resourc fp = FS_Open(name, "rb"); if (!fp) { +#ifndef REHLDS_FIXES Con_Printf("ERROR: couldn't open %s.\n", name); +#endif return FALSE; } FS_Read(&header, sizeof(hash_pack_header_t), 1, fp); From 59ed3f6867b1b3cba69cd7650887841da8e76d42 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 18 Mar 2024 22:04:02 +0700 Subject: [PATCH 03/31] SV_ParseResourceList: Do not uploading according to sv_allowupload cvar --- rehlds/engine/sv_upld.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rehlds/engine/sv_upld.cpp b/rehlds/engine/sv_upld.cpp index e28a0e3..82b8315 100644 --- a/rehlds/engine/sv_upld.cpp +++ b/rehlds/engine/sv_upld.cpp @@ -509,8 +509,13 @@ void SV_ParseResourceList(client_t *pSenderClient) } } - host_client->uploading = TRUE; - host_client->uploaddoneregistering = FALSE; +#ifdef REHLDS_FIXES + if (sv_allow_upload.value != 0.0f) +#endif //REHLDS_FIXES + { + host_client->uploading = TRUE; + host_client->uploaddoneregistering = FALSE; - SV_BatchUploadRequest(host_client); + SV_BatchUploadRequest(host_client); + } } From f26ad71aba6a596602245c64af71cd196196859f Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 18 Mar 2024 22:09:18 +0700 Subject: [PATCH 04/31] Do not send customizations list on duplicate or missing resource --- rehlds/engine/sv_upld.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/rehlds/engine/sv_upld.cpp b/rehlds/engine/sv_upld.cpp index 82b8315..1cd93f6 100644 --- a/rehlds/engine/sv_upld.cpp +++ b/rehlds/engine/sv_upld.cpp @@ -138,6 +138,9 @@ void SV_CreateCustomizationList(client_t *pHost) { pCust->nUserData2 = nLumps; gEntityInterface.pfnPlayerCustomization(pHost->edict, pCust); +#ifdef REHLDS_FIXES + SV_Customization(pHost, pResource, TRUE); +#endif } else { @@ -205,10 +208,6 @@ void SV_RegisterResources(void) pHost->uploading = FALSE; #ifdef REHLDS_FIXES SV_CreateCustomizationList(pHost); // FIXED: Call this function only once. It was crazy to call it for each resource available. - for (pResource = pHost->resourcesonhand.pNext; pResource != &pHost->resourcesonhand; pResource = pResource->pNext) - { - SV_Customization(pHost, pResource, TRUE); - } #else // REHLDS_FIXES for (pResource = pHost->resourcesonhand.pNext; pResource != &pHost->resourcesonhand; pResource = pResource->pNext) { From 462fe55fb832209270118b6def6034f8eec6efbf Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 18 Mar 2024 22:12:01 +0700 Subject: [PATCH 05/31] SV_CreateCustomizationList: spew logs in only dev mode --- rehlds/engine/sv_upld.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rehlds/engine/sv_upld.cpp b/rehlds/engine/sv_upld.cpp index 1cd93f6..aced9ed 100644 --- a/rehlds/engine/sv_upld.cpp +++ b/rehlds/engine/sv_upld.cpp @@ -145,9 +145,9 @@ void SV_CreateCustomizationList(client_t *pHost) else { if (sv_allow_upload.value == 0.0f) - Con_Printf("Ignoring custom decal from %s\n", pHost->name); + Con_DPrintf("Ignoring custom decal from %s\n", pHost->name); else - Con_Printf("Ignoring invalid custom decal from %s\n", pHost->name); + Con_DPrintf("Ignoring invalid custom decal from %s\n", pHost->name); } } } From ec47e4d97834c35f8667684f15a372c85505ce55 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 18 Mar 2024 22:17:23 +0700 Subject: [PATCH 06/31] Do not propagate custom logos according to sv_send_logos cvar --- rehlds/engine/sv_main.cpp | 6 ++++++ rehlds/engine/sv_upld.cpp | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 870a5fa..8f99c0f 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -5748,6 +5748,12 @@ void SV_PropagateCustomizations(void) if (pCust->bInUse) { pResource = &pCust->resource; + +#ifdef REHLDS_FIXES + if ((pResource->ucFlags & RES_CUSTOM) && !sv_send_logos.value) + continue; +#endif + MSG_WriteByte(&host_client->netchan.message, svc_customization); MSG_WriteByte(&host_client->netchan.message, i); MSG_WriteByte(&host_client->netchan.message, pResource->type); diff --git a/rehlds/engine/sv_upld.cpp b/rehlds/engine/sv_upld.cpp index aced9ed..425e439 100644 --- a/rehlds/engine/sv_upld.cpp +++ b/rehlds/engine/sv_upld.cpp @@ -160,6 +160,11 @@ void SV_Customization(client_t *pPlayer, resource_t *pResource, qboolean bSkipPl int nPlayerNumber; client_t *pHost; +#ifdef REHLDS_FIXES + if ((pResource->ucFlags & RES_CUSTOM) && !sv_send_logos.value) + return; +#endif + // Get originating player id for (i = 0, pHost = g_psvs.clients; i < g_psvs.maxclients; i++, pHost++) { From 3c282b435c7d0508d72104d26a0c9171f7feea71 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Tue, 26 Mar 2024 20:41:53 +0700 Subject: [PATCH 07/31] Draw_ValidateCustomLogo: Fixed incorrect offset to palette size --- rehlds/engine/decals.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rehlds/engine/decals.cpp b/rehlds/engine/decals.cpp index 1705b7f..19959d3 100644 --- a/rehlds/engine/decals.cpp +++ b/rehlds/engine/decals.cpp @@ -756,8 +756,8 @@ qboolean Draw_ValidateCustomLogo(cachewad_t *wad, unsigned char *data, lumpinfo_ Con_Printf("%s: Bad cached wad size %i/%i on %s\n", __func__, size + sizeof(miptex_t), lump->size + wad->cacheExtra, wad->name); } - paloffset = size + sizeof(lumpinfo_t) + sizeof(miptex_t); - palettesize = *(u_short *)(data + paloffset); // Get palette size + paloffset = size + sizeof(miptex_t); + palettesize = *(u_short *)(data + wad->cacheExtra + paloffset); // Get palette size for (i = 0; i < 3; i++) { From b870de768b3260b4c4b500c0a143caff1b697a9c Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Sat, 6 Apr 2024 22:09:47 +0300 Subject: [PATCH 08/31] fix: README badges [skip ci] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 15439cd..d6a0b38 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ReHLDS [![C/C++ CI](https://github.com/dreamstalker/rehlds/actions/workflows/build.yml/badge.svg)](https://github.com/dreamstalker/rehlds/actions/workflows/build.yml) [![Download](https://camo.githubusercontent.com/7ab483250adb4037b26e9575331218ee51108190d0938b7836d32f1209ccf907/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f72656c656173652f647265616d7374616c6b65722f7265686c64732e737667)](https://github.com/dreamstalker/rehlds/releases/latest) [![Downloads](https://camo.githubusercontent.com/d37654956d99bb9fb7a348fdac39b214d6ae14a7cfb9f96bf873c6b46cdf9ef6/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f646f776e6c6f6164732f647265616d7374616c6b65722f7265686c64732f746f74616c3f636f6c6f723d696d706f7274616e74)]() [![Percentage of issues still open](http://isitmaintained.com/badge/open/dreamstalker/rehlds.svg)](http://isitmaintained.com/project/dreamstalker/rehlds "Percentage of issues still open") [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ReHLDS +# ReHLDS [![C/C++ CI](https://github.com/dreamstalker/rehlds/actions/workflows/build.yml/badge.svg)](https://github.com/dreamstalker/rehlds/actions/workflows/build.yml) [![GitHub release (by tag)](https://img.shields.io/github/downloads/dreamstalker/rehlds/latest/total)](https://github.com/dreamstalker/rehlds/releases/latest) ![GitHub all releases](https://img.shields.io/github/downloads/dreamstalker/rehlds/total) [![Percentage of issues still open](http://isitmaintained.com/badge/open/dreamstalker/rehlds.svg)](http://isitmaintained.com/project/dreamstalker/rehlds "Percentage of issues still open") [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ReHLDS Reverse-engineered (and bugfixed) HLDS ## What is this? From 61ee4f926938b1894bedfd9f139d45db48d89903 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Wed, 10 Apr 2024 12:30:59 +0700 Subject: [PATCH 09/31] Netchan_CreateFileFragments: Fixed a hang connection on verifying resource stage, when precached file exists but is absolutely empty --- rehlds/engine/net_chan.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rehlds/engine/net_chan.cpp b/rehlds/engine/net_chan.cpp index 94149bc..5e51a8c 100644 --- a/rehlds/engine/net_chan.cpp +++ b/rehlds/engine/net_chan.cpp @@ -1203,7 +1203,9 @@ int Netchan_CreateFileFragments(qboolean server, netchan_t *chan, const char *fi if (!FS_FileExists(filename)) return FALSE; - if (FS_FileSize(filename) > sv_filetransfermaxsize.value) + + unsigned int nSize = FS_FileSize(filename); + if (nSize == 0 || nSize > sv_filetransfermaxsize.value) return FALSE; auto wait = (fragbufwaiting_t *)Mem_ZeroMalloc(sizeof(fragbufwaiting_t)); From 0d1bdbab671752aa579d7b4e10f4cf04d69a49a4 Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Sun, 14 Apr 2024 01:04:03 +0700 Subject: [PATCH 10/31] Implemented reduction of impact caused by zip-bomb exploit (#994) * Implemented reduction of impact caused by zip-bomb exploit Added network security CVars: - sv_net_incoming_decompression (0-1) Enables or disables incoming data decompression - sv_net_incoming_decompression_max_ratio (0.0 - 100.0) Sets max allowed ratio between compressed and decompressed data. (A ratio close to 90 indicates large uncompressed data with low entropy) - sv_net_incoming_decompression_max_size (16-65536) Adjusts max size of output data after decompression. Added CVar sv_net_incoming_decompression_punish for ban * Fix missing reg CVar sv_net_incoming_decompression_punish --- README.md | 4 ++ rehlds/engine/net_chan.cpp | 99 +++++++++++++++++++++++++++++++++----- 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index d6a0b38..b824c15 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,10 @@ 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_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: "" diff --git a/rehlds/engine/net_chan.cpp b/rehlds/engine/net_chan.cpp index 5e51a8c..71fc0f6 100644 --- a/rehlds/engine/net_chan.cpp +++ b/rehlds/engine/net_chan.cpp @@ -36,6 +36,12 @@ cvar_t net_showpackets = { "net_showpackets", "0", 0, 0.0f, nullptr}; cvar_t net_showdrop = { "net_showdrop", "0", 0, 0.0f, nullptr}; cvar_t net_drawslider = { "net_drawslider", "0", 0, 0.0f, nullptr}; 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", "75.0", 0, 75.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_filetransfercompression = { "sv_filetransfercompression", "1", 0, 0.0f, nullptr}; cvar_t sv_filetransfermaxsize = { "sv_filetransfermaxsize", "10485760", 0, 0.0f, nullptr}; @@ -1433,6 +1439,9 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan) p = chan->incomingbufs[FRAG_NORMAL_STREAM]; + chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr; + chan->incomingready[FRAG_NORMAL_STREAM] = FALSE; + SZ_Clear(&net_message); MSG_BeginReading(); @@ -1470,27 +1479,87 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan) } SZ_Clear(&net_message); - - chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr; - chan->incomingready[FRAG_NORMAL_STREAM] = FALSE; - return FALSE; } #endif // REHLDS_FIXES + qboolean success = TRUE; + if (*(uint32 *)net_message.data == MAKEID('B', 'Z', '2', '\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 data 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); + return FALSE; + } + } +#endif + char uncompressed[65536]; - unsigned int uncompressedSize = 65536; - BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char*)net_message.data + 4, net_message.cursize - 4, 1, 0); - Q_memcpy(net_message.data, uncompressed, uncompressedSize); - net_message.cursize = uncompressedSize; + unsigned int uncompressedSize = clamp((int)sv_net_incoming_decompression_max_size.value, 16, 65536); // valid range (16 - 65536) bytes + 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 + + // Copy uncompressed data back to the net buffer + Q_memcpy(net_message.data, uncompressed, uncompressedSize); + net_message.cursize = uncompressedSize; + } + else + { + // malformed data or compressed data exceeding sv_net_incoming_decompression_max_size + success = FALSE; + } + + // Drop client if decompression was unsuccessful + 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"); + } + + SZ_Clear(&net_message); + } } - chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr; - chan->incomingready[FRAG_NORMAL_STREAM] = FALSE; - - return TRUE; + return success; } qboolean Netchan_CopyFileFragments(netchan_t *chan) @@ -1826,6 +1895,12 @@ void Netchan_Init(void) Cvar_RegisterVariable(&net_chokeloopback); Cvar_RegisterVariable(&net_drawslider); Cvar_RegisterVariable(&sv_filetransfercompression); +#ifdef REHLDS_FIXES + 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_punish); +#endif Cvar_RegisterVariable(&sv_filetransfermaxsize); } From 5b72d06bfb1d09a147711536b8d878e959494094 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Fri, 10 May 2024 17:38:09 +0700 Subject: [PATCH 11/31] Fix default value for sv_net_incoming_decompression_max_ratio --- 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 71fc0f6..b4b75e9 100644 --- a/rehlds/engine/net_chan.cpp +++ b/rehlds/engine/net_chan.cpp @@ -38,7 +38,7 @@ cvar_t net_drawslider = { "net_drawslider", "0", 0, 0.0f, nullptr}; 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", "75.0", 0, 75.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 }; From 3c4acbb046196f2ee05c2d8f1a7eef1740f1276b Mon Sep 17 00:00:00 2001 From: s1lentq Date: Fri, 10 May 2024 17:42:05 +0700 Subject: [PATCH 12/31] Fix warning --- rehlds/engine/sv_upld.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rehlds/engine/sv_upld.cpp b/rehlds/engine/sv_upld.cpp index 425e439..eced090 100644 --- a/rehlds/engine/sv_upld.cpp +++ b/rehlds/engine/sv_upld.cpp @@ -207,14 +207,13 @@ void SV_Customization(client_t *pPlayer, resource_t *pResource, qboolean bSkipPl // Creates customizations list for the current player and sends resources to other players. void SV_RegisterResources(void) { - resource_t *pResource; client_t *pHost = host_client; pHost->uploading = FALSE; #ifdef REHLDS_FIXES SV_CreateCustomizationList(pHost); // FIXED: Call this function only once. It was crazy to call it for each resource available. #else // REHLDS_FIXES - for (pResource = pHost->resourcesonhand.pNext; pResource != &pHost->resourcesonhand; pResource = pResource->pNext) + for (resource_t *pResource = pHost->resourcesonhand.pNext; pResource != &pHost->resourcesonhand; pResource = pResource->pNext) { SV_CreateCustomizationList(pHost); SV_Customization(pHost, pResource, TRUE); From 47ffe4b257e388fc0cc24d6c2bb2230c961fbfde Mon Sep 17 00:00:00 2001 From: s1lentq Date: Fri, 10 May 2024 17:43:24 +0700 Subject: [PATCH 13/31] Fix rehlsdk --- rehlds/engine/server.h | 7 ------- rehlds/public/rehlds/maintypes.h | 8 ++++++++ rehlds/public/rehlds/rehlds_interfaces.h | 1 + 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h index c59332e..cbf31e8 100644 --- a/rehlds/engine/server.h +++ b/rehlds/engine/server.h @@ -95,13 +95,6 @@ typedef enum redirect_e RD_PACKET = 2, } redirect_t; -typedef enum server_state_e -{ - ss_dead = 0, - ss_loading = 1, - ss_active = 2, -} server_state_t; - typedef struct server_s { qboolean active; diff --git a/rehlds/public/rehlds/maintypes.h b/rehlds/public/rehlds/maintypes.h index b211e2d..158a354 100644 --- a/rehlds/public/rehlds/maintypes.h +++ b/rehlds/public/rehlds/maintypes.h @@ -67,4 +67,12 @@ typedef enum sv_delta_s sv_packet_delta, } sv_delta_t; +// From engine/server.h +typedef enum server_state_e +{ + ss_dead = 0, + ss_loading = 1, + ss_active = 2, +} server_state_t; + #endif // MAINTYPES_H diff --git a/rehlds/public/rehlds/rehlds_interfaces.h b/rehlds/public/rehlds/rehlds_interfaces.h index da3768c..438838c 100644 --- a/rehlds/public/rehlds/rehlds_interfaces.h +++ b/rehlds/public/rehlds/rehlds_interfaces.h @@ -36,6 +36,7 @@ class IGameClient; #include "common_rehlds.h" #include "userid_rehlds.h" +#include "FileSystem.h" #ifdef REHLDS_SELF #include "server.h" From 693b51c8839847270048be40b62fb1d37f9954ae Mon Sep 17 00:00:00 2001 From: s1lentq Date: Fri, 10 May 2024 18:04:43 +0700 Subject: [PATCH 14/31] Improved behavior of sv_filterban 0. Fixes #1027 --- README.md | 1 + rehlds/engine/sv_main.cpp | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b824c15..9098f12 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
  • 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_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/sv_main.cpp b/rehlds/engine/sv_main.cpp index 8f99c0f..dc2c517 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -3836,13 +3836,20 @@ void SV_ProcessFile(client_t *cl, char *filename) qboolean SV_FilterPacket(void) { + // sv_filterban filtering 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 + + qboolean bNegativeFilter = (sv_filterban.value == 1) ? TRUE : FALSE; + for (int i = numipfilters - 1; i >= 0; i--) { ipfilter_t* curFilter = &ipfilters[i]; if (curFilter->compare.u32 == 0xFFFFFFFF || curFilter->banEndTime == 0.0f || curFilter->banEndTime > realtime) { if ((*(uint32*)net_from.ip & curFilter->mask) == curFilter->compare.u32) - return (int)sv_filterban.value; + return bNegativeFilter; } else { @@ -3852,7 +3859,8 @@ qboolean SV_FilterPacket(void) --numipfilters; } } - return sv_filterban.value == 0.0f; + + return !bNegativeFilter; } void SV_SendBan(void) From 82a3d1d08412d53a0f48f01283ea36d38dd905c8 Mon Sep 17 00:00:00 2001 From: Adrian Cirstea <54354938+ShadowsAdi@users.noreply.github.com> Date: Fri, 10 May 2024 14:15:22 +0300 Subject: [PATCH 15/31] Add `SV_SendResources` hook (#1024) * Implement `SV_AddResources` hook --- rehlds/engine/server.h | 1 + rehlds/engine/sv_main.cpp | 5 +++++ rehlds/public/rehlds/rehlds_api.h | 7 ++++++- rehlds/rehlds/rehlds_api_impl.cpp | 4 ++++ rehlds/rehlds/rehlds_api_impl.h | 6 ++++++ rehlds/version/version.h | 2 +- 6 files changed, 23 insertions(+), 2 deletions(-) diff --git a/rehlds/engine/server.h b/rehlds/engine/server.h index cbf31e8..9446f14 100644 --- a/rehlds/engine/server.h +++ b/rehlds/engine/server.h @@ -461,6 +461,7 @@ void SV_QueryMovevarsChanged(void); 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); +void SV_SendResources_internal(sizebuf_t *msg); void SV_WriteClientdataToMessage(client_t *client, sizebuf_t *msg); void SV_WriteSpawn(sizebuf_t *msg); void SV_SendUserReg(sizebuf_t *msg); diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index dc2c517..c015f49 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -1199,6 +1199,11 @@ void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client) } void SV_SendResources(sizebuf_t *msg) +{ + g_RehldsHookchains.m_SV_SendResources.callChain(SV_SendResources_internal, msg); +} + +void EXT_FUNC SV_SendResources_internal(sizebuf_t *msg) { unsigned char nullbuffer[32]; Q_memset(nullbuffer, 0, sizeof(nullbuffer)); diff --git a/rehlds/public/rehlds/rehlds_api.h b/rehlds/public/rehlds/rehlds_api.h index bc83581..b82f332 100644 --- a/rehlds/public/rehlds/rehlds_api.h +++ b/rehlds/public/rehlds/rehlds_api.h @@ -37,7 +37,7 @@ #include "pr_dlls.h" #define REHLDS_API_VERSION_MAJOR 3 -#define REHLDS_API_VERSION_MINOR 13 +#define REHLDS_API_VERSION_MINOR 14 //Steam_NotifyClientConnect hook typedef IHookChain IRehldsHook_Steam_NotifyClientConnect; @@ -259,6 +259,10 @@ typedef IVoidHookChainRegistry IRehldsHookRegistry_SV_ClientPrintf typedef IHookChain IRehldsHook_SV_AllowPhysent; typedef IHookChainRegistry IRehldsHookRegistry_SV_AllowPhysent; +//SV_SendResources hook +typedef IVoidHookChain IRehldsHook_SV_SendResources; +typedef IVoidHookChainRegistry IRehldsHookRegistry_SV_SendResources; + class IRehldsHookchains { public: virtual ~IRehldsHookchains() { } @@ -318,6 +322,7 @@ public: virtual IRehldsHookRegistry_SV_AddResource* SV_AddResource() = 0; virtual IRehldsHookRegistry_SV_ClientPrintf* SV_ClientPrintf() = 0; virtual IRehldsHookRegistry_SV_AllowPhysent* SV_AllowPhysent() = 0; + virtual IRehldsHookRegistry_SV_SendResources* SV_SendResources() = 0; }; struct RehldsFuncs_t { diff --git a/rehlds/rehlds/rehlds_api_impl.cpp b/rehlds/rehlds/rehlds_api_impl.cpp index 8d7f9da..2b4ede0 100644 --- a/rehlds/rehlds/rehlds_api_impl.cpp +++ b/rehlds/rehlds/rehlds_api_impl.cpp @@ -885,6 +885,10 @@ IRehldsHookRegistry_SV_AllowPhysent* CRehldsHookchains::SV_AllowPhysent() { return &m_SV_AllowPhysent; } +IRehldsHookRegistry_SV_SendResources* CRehldsHookchains::SV_SendResources() { + return &m_SV_SendResources; +} + int EXT_FUNC CRehldsApi::GetMajorVersion() { return REHLDS_API_VERSION_MAJOR; diff --git a/rehlds/rehlds/rehlds_api_impl.h b/rehlds/rehlds/rehlds_api_impl.h index 699bd16..6aece98 100644 --- a/rehlds/rehlds/rehlds_api_impl.h +++ b/rehlds/rehlds/rehlds_api_impl.h @@ -254,6 +254,10 @@ typedef IVoidHookChainRegistryImpl CRehldsHookRegistry_SV_ClientPri typedef IHookChainImpl CRehldsHook_SV_AllowPhysent; typedef IHookChainRegistryImpl CRehldsHookRegistry_SV_AllowPhysent; +//SV_SendResources hook +typedef IVoidHookChainImpl CRehldsHook_SV_SendResources; +typedef IVoidHookChainRegistryImpl CRehldsHookRegistry_SV_SendResources; + class CRehldsHookchains : public IRehldsHookchains { public: CRehldsHookRegistry_Steam_NotifyClientConnect m_Steam_NotifyClientConnect; @@ -311,6 +315,7 @@ public: CRehldsHookRegistry_SV_AddResource m_SV_AddResource; CRehldsHookRegistry_SV_ClientPrintf m_SV_ClientPrintf; CRehldsHookRegistry_SV_AllowPhysent m_SV_AllowPhysent; + CRehldsHookRegistry_SV_SendResources m_SV_SendResources; public: EXT_FUNC virtual IRehldsHookRegistry_Steam_NotifyClientConnect* Steam_NotifyClientConnect(); @@ -368,6 +373,7 @@ public: EXT_FUNC virtual IRehldsHookRegistry_SV_AddResource* SV_AddResource(); EXT_FUNC virtual IRehldsHookRegistry_SV_ClientPrintf* SV_ClientPrintf(); EXT_FUNC virtual IRehldsHookRegistry_SV_AllowPhysent* SV_AllowPhysent(); + EXT_FUNC virtual IRehldsHookRegistry_SV_SendResources* SV_SendResources(); }; extern CRehldsHookchains g_RehldsHookchains; diff --git a/rehlds/version/version.h b/rehlds/version/version.h index 55a454b..3bed179 100644 --- a/rehlds/version/version.h +++ b/rehlds/version/version.h @@ -6,5 +6,5 @@ #pragma once #define VERSION_MAJOR 3 -#define VERSION_MINOR 13 +#define VERSION_MINOR 14 #define VERSION_MAINTENANCE 0 From a761efa75df820ebf5fb4899e863ce41fda099bd Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 13 May 2024 18:48:35 +0700 Subject: [PATCH 16/31] Minor cleanup --- rehlds/common/quakedef.h | 3 +++ rehlds/engine/pr_cmds.cpp | 5 +++-- rehlds/engine/sv_main.cpp | 8 +++++++- rehlds/engine/usermsg.h | 5 ++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/rehlds/common/quakedef.h b/rehlds/common/quakedef.h index 21f88e2..17f166d 100644 --- a/rehlds/common/quakedef.h +++ b/rehlds/common/quakedef.h @@ -29,6 +29,9 @@ typedef int BOOL; +// The maximum user messages +#define MAX_USERMESSAGES 256 + // user message #define MAX_USER_MSG_DATA 192 diff --git a/rehlds/engine/pr_cmds.cpp b/rehlds/engine/pr_cmds.cpp index dfd2846..d53a74b 100644 --- a/rehlds/engine/pr_cmds.cpp +++ b/rehlds/engine/pr_cmds.cpp @@ -2124,7 +2124,7 @@ void EXT_FUNC PF_MessageBegin_I(int msg_dest, int msg_type, const float *pOrigin if (msg_type == 0) Sys_Error("%s: Tried to create a message with a bogus message type ( 0 )", __func__); - gMsgStarted = 1; + gMsgStarted = TRUE; gMsgType = msg_type; gMsgEntity = ed; gMsgDest = msg_dest; @@ -2151,7 +2151,7 @@ void EXT_FUNC PF_MessageEnd_I(void) qboolean MsgIsVarLength = 0; if (!gMsgStarted) Sys_Error("%s: called with no active message\n", __func__); - gMsgStarted = 0; + gMsgStarted = FALSE; if (gMsgEntity && (gMsgEntity->v.flags & FL_FAKECLIENT)) return; @@ -2275,6 +2275,7 @@ void EXT_FUNC PF_WriteByte_I(int iValue) { if (!gMsgStarted) Sys_Error("%s: called with no active message\n", __func__); + MSG_WriteByte(&gMsgBuffer, iValue); } diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index c015f49..5dd76f2 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -6568,7 +6568,13 @@ void SV_ClearEntities(void) } int EXT_FUNC RegUserMsg(const char *pszName, int iSize) { - if (giNextUserMsg > 255 || !pszName || Q_strlen(pszName) > 11 || iSize > 192) + if (giNextUserMsg >= MAX_USERMESSAGES) + return 0; + + if (iSize > MAX_USER_MSG_DATA) + return 0; + + if (!pszName || Q_strlen(pszName) >= MAX_USERMESSAGES_LENGTH - 1) return 0; UserMsg *pUserMsgs = sv_gpUserMsgs; diff --git a/rehlds/engine/usermsg.h b/rehlds/engine/usermsg.h index ffff648..b752055 100644 --- a/rehlds/engine/usermsg.h +++ b/rehlds/engine/usermsg.h @@ -31,11 +31,14 @@ #include "maintypes.h" #include "quakedef.h" +// The maximum length of a usermessage name in a network transmission +#define MAX_USERMESSAGES_LENGTH 16 + typedef struct _UserMsg { int iMsg; int iSize; - char szName[16]; + char szName[MAX_USERMESSAGES_LENGTH]; struct _UserMsg *next; pfnUserMsgHook pfn; } UserMsg; From c9f9bbfff98cdb6e8ca45b9ab626f40d0c7bb22b Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 13 May 2024 19:20:19 +0700 Subject: [PATCH 17/31] Implement API interface game message manager --- rehlds/CMakeLists.txt | 1 + rehlds/engine/sys_dll.cpp | 4 + rehlds/msvc/ReHLDS.vcxproj | 3 + rehlds/msvc/ReHLDS.vcxproj.filters | 9 + rehlds/public/rehlds/IMessageManager.h | 223 ++++++++++ rehlds/public/rehlds/rehlds_api.h | 2 + rehlds/rehlds/hookchains_impl.cpp | 4 + rehlds/rehlds/hookchains_impl.h | 12 +- rehlds/rehlds/precompiled.h | 1 + rehlds/rehlds/rehlds_api_impl.cpp | 4 + rehlds/rehlds/rehlds_api_impl.h | 1 + rehlds/rehlds/rehlds_messagemngr_impl.cpp | 508 ++++++++++++++++++++++ rehlds/rehlds/rehlds_messagemngr_impl.h | 277 ++++++++++++ 13 files changed, 1048 insertions(+), 1 deletion(-) create mode 100644 rehlds/public/rehlds/IMessageManager.h create mode 100644 rehlds/rehlds/rehlds_messagemngr_impl.cpp create mode 100644 rehlds/rehlds/rehlds_messagemngr_impl.h diff --git a/rehlds/CMakeLists.txt b/rehlds/CMakeLists.txt index a9b371b..e48e87d 100644 --- a/rehlds/CMakeLists.txt +++ b/rehlds/CMakeLists.txt @@ -182,6 +182,7 @@ set(ENGINE_SRCS rehlds/public_amalgamation.cpp rehlds/rehlds_api_impl.cpp rehlds/rehlds_interfaces_impl.cpp + rehlds/rehlds_messagemngr_impl.cpp rehlds/rehlds_security.cpp ) diff --git a/rehlds/engine/sys_dll.cpp b/rehlds/engine/sys_dll.cpp index 08431a4..ddfcef3 100644 --- a/rehlds/engine/sys_dll.cpp +++ b/rehlds/engine/sys_dll.cpp @@ -1083,6 +1083,10 @@ void LoadThisDll(const char *szDllFilename) goto IgnoreThisDLL; } +#ifdef REHLDS_API + MessageManager().Init(); +#endif + pfnGiveFnptrsToDll(&g_engfuncsExportedToDlls, &gGlobalVariables); if (g_iextdllMac == MAX_EXTENSION_DLL) { diff --git a/rehlds/msvc/ReHLDS.vcxproj b/rehlds/msvc/ReHLDS.vcxproj index dc15355..6b2a50b 100644 --- a/rehlds/msvc/ReHLDS.vcxproj +++ b/rehlds/msvc/ReHLDS.vcxproj @@ -119,6 +119,7 @@ + @@ -386,6 +387,7 @@ + @@ -439,6 +441,7 @@ + diff --git a/rehlds/msvc/ReHLDS.vcxproj.filters b/rehlds/msvc/ReHLDS.vcxproj.filters index a59f265..9d0731a 100644 --- a/rehlds/msvc/ReHLDS.vcxproj.filters +++ b/rehlds/msvc/ReHLDS.vcxproj.filters @@ -341,6 +341,9 @@ testsuite + + rehlds + @@ -1060,5 +1063,11 @@ testsuite + + rehlds + + + public\rehlds + \ No newline at end of file diff --git a/rehlds/public/rehlds/IMessageManager.h b/rehlds/public/rehlds/IMessageManager.h new file mode 100644 index 0000000..cf818db --- /dev/null +++ b/rehlds/public/rehlds/IMessageManager.h @@ -0,0 +1,223 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +*/ + +#pragma once + +/** + * @brief Interface for defining message parameters and behavior for a individual message object + */ +class IMessage +{ +public: + /** + * @brief The parameter types for a message + */ + enum class ParamType : uint8 + { + Byte, + Char, + Short, + Long, + Angle, + Coord, + String, + Entity, + }; + + /** + * @brief Blocking behavior types for messages + */ + enum class BlockType : uint8 + { + Not, // Not a block + Once, // Block once + Set // Set block + }; + + /** + * @brief Message destinations + */ + enum class Dest : uint8 + { + BROADCAST, // Unreliable to all + ONE, // Reliable to one (msg_entity) + ALL, // Reliable to all + INIT, // Write to the init string + PVS, // Ents in PVS of org + PAS, // Ents in PAS of org + PVS_R, // Reliable to PVS + PAS_R, // Reliable to PAS + ONE_UNRELIABLE, // Send to one client, but don't put in reliable stream, put in unreliable datagram + SPEC, // Sends to all spectator proxies + }; + + virtual ~IMessage() {}; + + /** + * @brief Returns the number of parameters in the message + * @return The number of parameters + */ + virtual int getParamCount() const = 0; + + /** + * @brief Returns the type of the parameter at the given index + * @param index The index of the parameter + * @return The type of the parameter + */ + virtual ParamType getParamType(size_t index) const = 0; + + /** + * @brief Returns the integer value of the parameter at the given index + * @param index The index of the parameter + * @return The integer value of the parameter + */ + virtual int getParamInt(size_t index) const = 0; + + /** + * @brief Returns the float value of the parameter at the given index + * @param index The index of the parameter + * @return The float value of the parameter + */ + virtual float getParamFloat(size_t index) const = 0; + + /** + * @brief Returns the string value of the parameter at the given index + * @param index The index of the parameter + * @return The string value of the parameter + */ + virtual const char* getParamString(size_t index) const = 0; + + /** + * @brief Sets the integer value of the parameter at the given index + * @param index The index of the parameter + * @param value The integer value to set + */ + virtual void setParamInt(size_t index, int value) = 0; + + /** + * @brief Sets the float value of the parameter at the given index + * @param index The index of the parameter + * @param value The float value to set + */ + virtual void setParamFloat(size_t index, float value) = 0; + + /** + * @brief Sets the vector value of the parameter at the given index + * @param index The index of the parameter + * @param pos The vector value to set + */ + virtual void setParamVec(size_t index, const float *pos) = 0; + + /** + * @brief Sets the string value of the parameter at the given index + * @param index The index of the parameter + * @param string The string value to set + */ + virtual void setParamString(size_t index, const char *string) = 0; + + /** + * @brief Returns the destination of the message + * @return The destination of the message + */ + virtual Dest getDest() const = 0; + + /** + * @brief Returns the type of the message + * @return The type of the message + */ + virtual int getType() const = 0; + + /** + * @brief Returns the origin of the message + * @return The origin of the message + */ + virtual const float* getOrigin() const = 0; + + /** + * @brief Returns the edict associated with the message + * @return The edict associated with the message + */ + virtual struct edict_s* getEdict() const = 0; + + /** + * @brief Returns whether the message has been modified + * @return True if the message has been modified, false otherwise + */ + virtual bool isModified() const = 0; + + // This must be the last virtual function in class +#ifdef REHLDS_SELF + // Set the copyback buffer for the message + virtual void setCopybackBuffer(struct sizebuf_s *pbuf) = 0; +#endif +}; + +#define MESSAGEMNGR_VERSION_MAJOR 1 +#define MESSAGEMNGR_VERSION_MINOR 0 + +/** + * @brief Interface manages hooks and blocking behavior game messages + */ +class IMessageManager +{ +public: + using hookfunc_t = void (*)(IVoidHookChain *chain, IMessage *msg); + + virtual ~IMessageManager() {}; + + /** + * @brief Returns the major version of the MessageManager + * @return The major version + */ + virtual int getMajorVersion() const = 0; + + /** + * @brief Returns the minor version of the MessageManager + * @return The minor version + */ + virtual int getMinorVersion() const = 0; + + /** + * @brief Returns the blocking behavior for the given message type + * @param msgType The message type + * @return The blocking behavior for the given message type + */ + virtual IMessage::BlockType getMessageBlock(int msgType) const = 0; + + /** + * @brief Sets the blocking behavior for the given message type + * @param msgType The message type + * @param blockType The blocking behavior to set + */ + virtual void setMessageBlock(int msgType, IMessage::BlockType blockType) = 0; + + /** + * @brief Registers a hook function for the given message type + * @param msgType The message type to register the hook for + * @param handler The hook function to register + * @param priority The priority of the hook function (see enum HookChainPriority) + */ + virtual void registerHook(int msgType, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT) = 0; + + /** + * @brief Unregisters a hook function for the given message type + * @param msgType The message type to unregister the hook for + * @param handler The hook function to unregister + */ + virtual void unregisterHook(int msgType, hookfunc_t handler) = 0; +}; diff --git a/rehlds/public/rehlds/rehlds_api.h b/rehlds/public/rehlds/rehlds_api.h index b82f332..743e47d 100644 --- a/rehlds/public/rehlds/rehlds_api.h +++ b/rehlds/public/rehlds/rehlds_api.h @@ -31,6 +31,7 @@ #include "rehlds_interfaces.h" #include "hookchains.h" #include "FlightRecorder.h" +#include "IMessageManager.h" #include "interface.h" #include "model.h" #include "ObjectList.h" @@ -446,6 +447,7 @@ public: virtual IRehldsServerStatic* GetServerStatic() = 0; virtual IRehldsServerData* GetServerData() = 0; virtual IRehldsFlightRecorder* GetFlightRecorder() = 0; + virtual IMessageManager *GetMessageManager() = 0; }; #define VREHLDS_HLDS_API_VERSION "VREHLDS_HLDS_API_VERSION001" diff --git a/rehlds/rehlds/hookchains_impl.cpp b/rehlds/rehlds/hookchains_impl.cpp index 13f6e36..eaa49fc 100644 --- a/rehlds/rehlds/hookchains_impl.cpp +++ b/rehlds/rehlds/hookchains_impl.cpp @@ -90,3 +90,7 @@ void AbstractHookChainRegistry::removeHook(void* hookFunc) { } } } + +int AbstractHookChainRegistry::getCount() const { + return m_NumHooks; +} diff --git a/rehlds/rehlds/hookchains_impl.h b/rehlds/rehlds/hookchains_impl.h index dd2523c..0ab7800 100644 --- a/rehlds/rehlds/hookchains_impl.h +++ b/rehlds/rehlds/hookchains_impl.h @@ -109,10 +109,11 @@ protected: protected: void addHook(void* hookFunc, int priority); - bool findHook(void* hookFunc) const; void removeHook(void* hookFunc); public: + int getCount() const; + bool findHook(void* hookFunc) const; AbstractHookChainRegistry(); }; @@ -132,9 +133,14 @@ public: EXT_FUNC virtual void registerHook(hookfunc_t hook, int priority) { addHook((void*)hook, priority); } + EXT_FUNC virtual void unregisterHook(hookfunc_t hook) { removeHook((void*)hook); } + + bool isEmpty() const { + return getCount() == 0; + } }; template @@ -157,4 +163,8 @@ public: EXT_FUNC virtual void unregisterHook(hookfunc_t hook) { removeHook((void*)hook); } + + bool isEmpty() const { + return getCount() == 0; + } }; diff --git a/rehlds/rehlds/precompiled.h b/rehlds/rehlds/precompiled.h index de99a03..a947fcf 100644 --- a/rehlds/rehlds/precompiled.h +++ b/rehlds/rehlds/precompiled.h @@ -49,6 +49,7 @@ #include "rehlds_api_impl.h" #include "FlightRecorderImpl.h" #include "flight_recorder.h" +#include "rehlds_messagemngr_impl.h" #include "rehlds_security.h" #include "dlls/cdll_dll.h" diff --git a/rehlds/rehlds/rehlds_api_impl.cpp b/rehlds/rehlds/rehlds_api_impl.cpp index 2b4ede0..62a3920 100644 --- a/rehlds/rehlds/rehlds_api_impl.cpp +++ b/rehlds/rehlds/rehlds_api_impl.cpp @@ -917,6 +917,10 @@ IRehldsServerData* EXT_FUNC CRehldsApi::GetServerData() { return &g_RehldsServerData; } +IMessageManager* EXT_FUNC CRehldsApi::GetMessageManager() { + return &MessageManager(); +} + IRehldsFlightRecorder* EXT_FUNC CRehldsApi::GetFlightRecorder() { return g_FlightRecorder; } diff --git a/rehlds/rehlds/rehlds_api_impl.h b/rehlds/rehlds/rehlds_api_impl.h index 6aece98..cda2b5a 100644 --- a/rehlds/rehlds/rehlds_api_impl.h +++ b/rehlds/rehlds/rehlds_api_impl.h @@ -391,6 +391,7 @@ public: virtual IRehldsServerStatic* GetServerStatic(); virtual IRehldsServerData* GetServerData(); virtual IRehldsFlightRecorder* GetFlightRecorder(); + virtual IMessageManager* GetMessageManager(); }; extern sizebuf_t* GetNetMessage_api(); diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.cpp b/rehlds/rehlds/rehlds_messagemngr_impl.cpp new file mode 100644 index 0000000..617ceb1 --- /dev/null +++ b/rehlds/rehlds/rehlds_messagemngr_impl.cpp @@ -0,0 +1,508 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +*/ + +#include "precompiled.h" + +// Constructs a Message object +MessageImpl::MessageImpl() +{ + m_buffer.buffername = "MessageManager/Begin/End"; + m_buffer.data = m_bufferData; + m_buffer.flags = SIZEBUF_ALLOW_OVERFLOW; + m_buffer.cursize = 0; + m_buffer.maxsize = sizeof(m_bufferData); + + m_paramCount = 0; +} + +// Sets the active state of the message with the given parameters +void MessageImpl::setActive(int dest, int type, const float *origin, edict_t *edict) +{ + m_dest = static_cast(dest); + m_type = type; + m_edict = edict; + + // Reset buffer size + m_buffer.flags = SIZEBUF_ALLOW_OVERFLOW; + m_buffer.cursize = 0; + + // Copy origin vector if provided + if (origin) + VectorCopy(origin, m_origin); + else + VectorClear(m_origin); +} + +// Sets the buffer for the message +void MessageImpl::setBuffer(sizebuf_t *pbuf) +{ + // Copy data from the provided buffer to the message buffer + memcpy(m_buffer.data, pbuf->data, pbuf->cursize); + m_buffer.cursize = pbuf->cursize; +} + +// Sets the copyback buffer for the message +void MessageImpl::setCopybackBuffer(sizebuf_t *pbuf) +{ + // Copy data from the message buffer back to the provided buffer + memcpy(pbuf->data, m_buffer.data, m_buffer.cursize); + pbuf->cursize = m_buffer.cursize; +} + +// Clears the message parameters +void MessageImpl::clear() +{ + m_paramCount = 0; +} + +// An array containing fixed sizes for various types of parameters +static size_t SIZEOF_PARAMTYPE[] = +{ + sizeof(uint8), // Byte + sizeof(int8), // Char + sizeof(int16), // Short + sizeof(uint32), // Long + sizeof(uint8), // Angle + sizeof(int16), // Coord + 0, // String + sizeof(int16), // Entity +}; + +// Adds a parameter to the message +void MessageImpl::addParam(IMessage::ParamType type, size_t length) +{ + Param_t ¶m = m_params[m_paramCount++]; + param.type = type; + param.len = (length == -1) ? SIZEOF_PARAMTYPE[static_cast(type)] : length; + param.pos = gMsgBuffer.cursize; +} + +// Sets the value of a primitive parameter at the given index +template +void MessageImpl::setParamPrimitive(size_t index, T value) +{ + // Ensure index is within bounds + if (index < 0 || index >= m_paramCount) + return; + + const Param_t ¶m = m_params[index]; + + void *pbuf = m_buffer.data + param.pos; + + // Set value based on parameter type + switch (param.type) + { + case IMessage::ParamType::Byte: + *(uint8 *)pbuf = value; + break; + case IMessage::ParamType::Char: + *(int8 *)pbuf = value; + break; + case IMessage::ParamType::Short: + case IMessage::ParamType::Entity: + *(int16 *)pbuf = value; + break; + case IMessage::ParamType::Long: + *(uint16 *)pbuf = value; + 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; + break; + case IMessage::ParamType::Coord: + // Convert coordinate value to short integer representation with loss of precision + *(int16 *)pbuf = (int16)(int)(value * 8.0); + break; + default: + return; // bad type + } + + // Mark message as modified + m_modified = true; +} + +// Transforms the buffer after setting a string parameter at the given index +void MessageImpl::setTxformBuffer(size_t index, size_t startPos, size_t oldLength, size_t newLength) +{ + // Calculate the difference in length + int32_t diffLength = newLength - oldLength; + if (diffLength != 0) + { + // Check if the buffer size limit will be exceeded + if (m_buffer.cursize + diffLength > m_buffer.maxsize) + { + Sys_Error( + "%s: Refusing to transform string with %i param of user message of %i bytes, " + "user message size limit is %i bytes\n", __func__, index, gMsgBuffer.cursize + diffLength, gMsgBuffer.maxsize); + } + + // Move the data in the buffer + size_t moveLength = m_buffer.cursize - (startPos + oldLength); + Q_memmove(m_buffer.data + startPos + newLength, m_buffer.data + startPos + oldLength, moveLength); + m_buffer.cursize += diffLength; + + // Update the position of all subsequent parameters + for (size_t i = index + 1; i < m_paramCount; i++) + m_params[i].pos += diffLength; + } +} + +// Returns the integer value of the parameter at the given index +int MessageImpl::getParamInt(size_t index) const +{ + // Ensure index is within bounds + if (index < 0 || index >= m_paramCount) + return 0; + + // Get the parameter value based on its type + void *buf = m_buffer.data + m_params[index].pos; + switch (m_params[index].type) + { + case IMessage::ParamType::Byte: + return *(uint8 *)buf; + case IMessage::ParamType::Char: + return *(int8 *)buf; + case IMessage::ParamType::Short: + case IMessage::ParamType::Entity: + return *(int16 *)buf; + case IMessage::ParamType::Long: + return *(uint16 *)buf; + default: + return 0; // bad type + } +} + +// Returns the float value of the parameter at the given index +float MessageImpl::getParamFloat(size_t index) const +{ + // Ensure index is within bounds + if (index < 0 || index >= m_paramCount) + return 0; + + // Get the parameter value based on its type + const Param_t ¶m = m_params[index]; + void *buf = m_buffer.data + param.pos; + switch (param.type) + { + case IMessage::ParamType::Angle: + return (float)(*(uint8 *)buf * (360.0 / 256.0)); + case IMessage::ParamType::Coord: + return (float)(*(int16 *)buf * (1.0 / 8)); + default: + break; // bad type + } + + return 0; +} + +// Returns the string value of the parameter at the given index +const char *MessageImpl::getParamString(size_t index) const +{ + // Ensure index is within bounds + if (index < 0 || index >= m_paramCount) + return nullptr; + + // Get the parameter value if it is a string + const Param_t ¶m = m_params[index]; + if (param.type == IMessage::ParamType::String) + return (const char *)m_buffer.data + param.pos; + + return nullptr; +} + +// Sets the integer value of the parameter at the given index +void MessageImpl::setParamInt(size_t index, int value) +{ + setParamPrimitive(index, value); +} + +// Sets the float value of the parameter at the given index +void MessageImpl::setParamFloat(size_t index, float value) +{ + setParamPrimitive(index, value); +} + +// Sets the vector value of the parameter at the given index +void MessageImpl::setParamVec(size_t index, const float *pos) +{ + if (!pos) + return; + + // Ensure index is within bounds + if (index < 0 || (index + 3) >= m_paramCount) + return; + + // Get the parameter position in the buffer + const Param_t ¶m = m_params[index]; + + int16 *pbuf = (int16 *)m_buffer.data + param.pos; + + // Set each component of the vector parameter + *(int16 *)pbuf++ = (int16)(pos[0] * 8.0); + *(int16 *)pbuf++ = (int16)(pos[1] * 8.0); + *(int16 *)pbuf++ = (int16)(pos[2] * 8.0); + + // Mark message as modified + m_modified = true; +} + +// Sets the string value of the parameter at the given index +void MessageImpl::setParamString(size_t index, const char *value) +{ + if (!value) + return; + + // Ensure index is within bounds + if (index < 0 || index >= m_paramCount) + return; + + // Calculate the length of the string + size_t length = Q_strlen(value) + 1; + const Param_t ¶m = m_params[index]; + + // Transform buffer to accommodate the new string length + setTxformBuffer(index, param.pos, param.len, length); + + // Copy the string value to the buffer + memcpy(m_buffer.data + param.pos, value, length); + + // Mark message as modified + m_modified = true; +} + +MessageManagerImpl::MessageManagerImpl() +{ + m_inblock = false; + m_inhook = false; +} + +// Register hook function for the game message type +void MessageManagerImpl::registerHook(int msgType, hookfunc_t handler, int priority) +{ + m_hooks[msgType].registerHook(handler, priority); +} + +// Unregister hook function for the game message type +void MessageManagerImpl::unregisterHook(int msgType, hookfunc_t handler) +{ + m_hooks[msgType].unregisterHook(handler); +} + +// Get the block type for the game message type +IMessage::BlockType MessageManagerImpl::getMessageBlock(int msgType) const +{ + return m_blocks[msgType]; +} + +// Set the block type for the game message type +void MessageManagerImpl::setMessageBlock(int msgType, IMessage::BlockType blockType) +{ + m_blocks[msgType] = blockType; +} + +bool MessageManagerImpl::MessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed) +{ + // Check if the message type is blocked + if (m_blocks[msg_type] != IMessage::BlockType::Not) + { + m_inblock = true; + return false; + } + + // Check if there are hooks registered for the message type + m_inhook = m_hooks[msg_type].getCount() > 0; + + if (m_inhook) + { + // Check for stack overflow + if (m_stack.size() >= m_stack.max_size() - 1) + Sys_Error("%s: stack overflow in #%i user message.\nIndicate potential recursive calls...\n", __func__, msg_type); + + // Push a new game message onto the stack + m_stack.push(); + + // Initialize the message + MessageImpl &msg = m_stack.back(); + msg.setActive(msg_dest, msg_type, pOrigin, ed); + } + + return true; +} + +static void EXT_FUNC SendUserMessageData(IMessage *msg) +{ + // Set global variables with message data + gMsgType = msg->getType(); + gMsgEntity = msg->getEdict(); + gMsgDest = static_cast(msg->getDest()); + + gMsgOrigin[0] = msg->getOrigin()[0]; + gMsgOrigin[1] = msg->getOrigin()[1]; + gMsgOrigin[2] = msg->getOrigin()[2]; + + gMsgStarted = TRUE; + + // Copy message data to global buffer and call end of the hookchain + msg->setCopybackBuffer(&gMsgBuffer); + PF_MessageEnd_I(); +} + +bool MessageManagerImpl::MessageEnd() +{ + // Check if in block mode + if (m_inblock) + { + m_inblock = false; + + // Unblock the message type if it was blocked once + if (m_blocks[gMsgType] == IMessage::BlockType::Once) + m_blocks[gMsgType] = IMessage::BlockType::Not; + + return false; + } + + // Check if not in hook + if (!m_inhook) + return true; + + gMsgStarted = FALSE; + + // Get the message from the top of the stack + MessageImpl &msg = m_stack.back(); + + // Set buffer from global buffer and call hookchain + msg.setBuffer(&gMsgBuffer); + m_hooks[msg.getType()].callChain(SendUserMessageData, &msg); + m_inhook = false; + + // Clear the message and pop from the stack + msg.clear(); + m_stack.pop(); + + return false; +} + +bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length) +{ + // Check if in block mode + if (m_inblock) + return false; + + // Check if in hook mode + if (m_inhook) + { + // Add parameter to top stack message + MessageImpl &msg = m_stack.back(); + msg.addParam(type, length); + } + + return true; +} + +// +// Functions intercept to handle messages +// + +void EXT_FUNC PF_MessageBegin_Intercept(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed) +{ + // Set global message type + gMsgType = msg_type; + + // Begin message manager + if (MessageManager().MessageBegin(msg_dest, msg_type, pOrigin, ed)) + PF_MessageBegin_I(msg_dest, msg_type, pOrigin, ed); +} + +void EXT_FUNC PF_MessageEnd_Intercept(void) +{ + // End message manager + if (MessageManager().MessageEnd()) + PF_MessageEnd_I(); // Call original message end function if the manager allows it +} + +void EXT_FUNC PF_WriteByte_Intercept(int iValue) +{ + // Write byte parameter to the message if the manager allows it + if (MessageManager().WriteParam(IMessage::ParamType::Byte)) + PF_WriteByte_I(iValue); +} + +void EXT_FUNC PF_WriteChar_Intercept(int iValue) +{ + if (MessageManager().WriteParam(IMessage::ParamType::Char)) + PF_WriteChar_I(iValue); +} + +void EXT_FUNC PF_WriteShort_Intercept(int iValue) +{ + if (MessageManager().WriteParam(IMessage::ParamType::Short)) + PF_WriteShort_I(iValue); +} + +void EXT_FUNC PF_WriteLong_Intercept(int iValue) +{ + if (MessageManager().WriteParam(IMessage::ParamType::Long)) + PF_WriteLong_I(iValue); +} + +void EXT_FUNC PF_WriteAngle_Intercept(float flValue) +{ + if (MessageManager().WriteParam(IMessage::ParamType::Angle)) + PF_WriteAngle_I(flValue); +} + +void EXT_FUNC PF_WriteCoord_Intercept(float flValue) +{ + if (MessageManager().WriteParam(IMessage::ParamType::Coord)) + PF_WriteCoord_I(flValue); +} + +void EXT_FUNC PF_WriteString_Intercept(const char *sz) +{ + if (MessageManager().WriteParam(IMessage::ParamType::String, sz ? Q_strlen(sz) + 1 : 1)) + PF_WriteString_I(sz); +} + +void EXT_FUNC PF_WriteEntity_Intercept(int iValue) +{ + if (MessageManager().WriteParam(IMessage::ParamType::Entity)) + PF_WriteEntity_I(iValue); +} + +// Initialization function to set up function interception +void MessageManagerImpl::Init() +{ + // Set function callback to intercept functions + g_engfuncsExportedToDlls.pfnMessageBegin = PF_MessageBegin_Intercept; + g_engfuncsExportedToDlls.pfnWriteByte = PF_WriteByte_Intercept; + g_engfuncsExportedToDlls.pfnWriteChar = PF_WriteChar_Intercept; + g_engfuncsExportedToDlls.pfnWriteShort = PF_WriteShort_Intercept; + g_engfuncsExportedToDlls.pfnWriteLong = PF_WriteLong_Intercept; + g_engfuncsExportedToDlls.pfnWriteAngle = PF_WriteAngle_Intercept; + g_engfuncsExportedToDlls.pfnWriteCoord = PF_WriteCoord_Intercept; + g_engfuncsExportedToDlls.pfnWriteString = PF_WriteString_Intercept; + g_engfuncsExportedToDlls.pfnWriteEntity = PF_WriteEntity_Intercept; + g_engfuncsExportedToDlls.pfnMessageEnd = PF_MessageEnd_Intercept; +} + +MessageManagerImpl &MessageManager() +{ + // Instance of the message manager singleton + static MessageManagerImpl instance{}; + return instance; +} diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.h b/rehlds/rehlds/rehlds_messagemngr_impl.h new file mode 100644 index 0000000..b0ad18c --- /dev/null +++ b/rehlds/rehlds/rehlds_messagemngr_impl.h @@ -0,0 +1,277 @@ +/* +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at +* your option) any later version. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +* +*/ + +#pragma once + +#include "IMessageManager.h" +#include "hookchains.h" + +/** + * @brief Implementation interface for defining message parameters and behavior for a game message object + */ +class MessageImpl: public IMessage +{ +public: + MessageImpl(); + ~MessageImpl() = default; + + /** + * @brief Returns the number of parameters in the message + * @return The number of parameters + */ + int getParamCount() const { return m_paramCount; } + + /** + * @brief Returns the type of the parameter at the given index + * @param index The index of the parameter + * @return The type of the parameter + */ + ParamType getParamType(size_t index) const { return m_params[index].type; } + + /** + * @brief Returns the integer value of the parameter at the given index + * @param index The index of the parameter + * @return The integer value of the parameter + */ + int getParamInt(size_t index) const; + + /** + * @brief Returns the float value of the parameter at the given index + * @param index The index of the parameter + * @return The float value of the parameter + */ + float getParamFloat(size_t index) const; + + /** + * @brief Returns the string value of the parameter at the given index + * @param index The index of the parameter + * @return The string value of the parameter + */ + const char *getParamString(size_t index) const; + + /** + * @brief Sets the integer value of the parameter at the given index + * @param index The index of the parameter + * @param value The integer value to set + */ + void setParamInt(size_t index, int value); + + /** + * @brief Sets the float value of the parameter at the given index + * @param index The index of the parameter + * @param value The float value to set + */ + void setParamFloat(size_t index, float value); + + /** + * @brief Sets the vector value of the parameter at the given index + * @param index The index of the parameter + * @param pos The vector value to set + */ + void setParamVec(size_t index, const float *pos); + + /** + * @brief Sets the string value of the parameter at the given index + * @param index The index of the parameter + * @param string The string value to set + */ + void setParamString(size_t index, const char *string); + + /** + * @brief Returns the destination of the message + * @return The destination of the message + */ + Dest getDest() const { return m_dest; } + + /** + * @brief Returns the type of the message + * @return The type of the message + */ + int getType() const { return m_type; } + + /** + * @brief Returns the origin of the message + * @return The origin of the message + */ + const float *getOrigin() const { return m_origin; } + + /** + * @brief Returns the edict associated with the message + * @return The edict associated with the message + */ + edict_t *getEdict() const { return m_edict; } + + /** + * @brief Returns whether the message has been modified + * @return True if the message has been modified, false otherwise + */ + bool isModified() const { return m_modified; } + +private: + + friend class MessageManagerImpl; + + // Sets the active state of the message with the given parameters + void setActive(int dest, int type, const float *origin, edict_t *edict); + + // Sets the buffer for the message + void setBuffer(sizebuf_t *pbuf); + + // Set the copyback buffer for the message + void setCopybackBuffer(sizebuf_t *pbuf); + + // Adds a parameter to the message + void addParam(IMessage::ParamType type, size_t length); + + // Clears the message after execution + void clear(); + + template + void setParamPrimitive(size_t index, T value); + + // 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); + + bool m_modified; // Flag indicating whether the message has been modified + Dest m_dest; // The destination of the message + int m_type; // The type of the message + float m_origin[3]; // The origin of the message + edict_t* m_edict; // The edict associated with the message + + uint8 m_bufferData[512]; // The buffer data for the message 'm_buffer' + sizebuf_t m_buffer; // The buffer for the message + + struct Param_t + { + ParamType type; // The type of the parameter + size_t pos; // The position of the parameter in the buffer + size_t len; // The length of the parameter in the buffer + }; + + static const size_t MAX_PARAMS = 16; // The maximum number of parameters allowed in the message + Param_t m_params[MAX_PARAMS]; // The array of parameters in the message + size_t m_paramCount; // The number of parameters in the message +}; + +/** + * @brief Implementation interface manages hooks and blocking behavior game messages + */ +class MessageManagerImpl: public IMessageManager +{ +public: + + void Init(); + + MessageManagerImpl(); + ~MessageManagerImpl() = default; + + /** + * @brief Returns the major version of the MessageManager + * @return The major version + */ + int getMajorVersion() const { return MESSAGEMNGR_VERSION_MAJOR; } + + /** + * @brief Returns the minor version of the MessageManager + * @return The minor version + */ + int getMinorVersion() const { return MESSAGEMNGR_VERSION_MINOR; } + + /** + * @brief Returns the blocking behavior for the given message type + * @param msgType The message type + * @return The blocking behavior for the given message type + */ + IMessage::BlockType getMessageBlock(int msgType) const; + + /** + * @brief Sets the blocking behavior for the given message type + * @param msgType The message type + * @param blockType The blocking behavior to set + */ + void setMessageBlock(int msgType, IMessage::BlockType blockType); + + /** + * @brief Registers a hook function for the given message type + * @param msgType The message type to register the hook for + * @param handler The hook function to register + * @param priority The priority of the hook function (see enum HookChainPriority) + */ + void registerHook(int msgType, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT); + + /** + * @brief Unregisters a hook function for the given message type + * @param msgType The message type to unregister the hook for + * @param handler The hook function to unregister + */ + void unregisterHook(int msgType, hookfunc_t handler); + +private: + friend void PF_MessageBegin_Intercept(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); + friend void PF_MessageEnd_Intercept(); + friend void PF_WriteByte_Intercept(int iValue); + friend void PF_WriteChar_Intercept(int iValue); + friend void PF_WriteShort_Intercept(int iValue); + friend void PF_WriteLong_Intercept(int iValue); + friend void PF_WriteAngle_Intercept(float flValue); + friend void PF_WriteCoord_Intercept(float flValue); + friend void PF_WriteString_Intercept(const char *sz); + friend void PF_WriteEntity_Intercept(int iValue); + + bool MessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); + bool MessageEnd(); + +private: + bool WriteParam(IMessage::ParamType type, 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 + + /** + * @brief Helper a templated Stack class to manage a stack of Message objects + * @tparam T The type of objects stored in the stack + * @tparam MAX The maximum size of the stack + */ + template + class Stack + { + public: + void push() { _size++; } + void pop () { _size--; } + + size_t size() const { return _size; } + size_t max_size() const { return MAX; } + + const T &back() const { return _data[_size - 1]; } + T &back() { return _data[_size - 1]; } + private: + size_t _size = 0u; + T _data[MAX]{}; + }; + + static const size_t MAX_MSGSTACK = 16; // The maximum size of the message stack, 16 it should be enough + Stack m_stack; + + IVoidHookChainRegistryImpl m_hooks [MAX_USERMESSAGES]{}; // The array of hook chain registries for each message type + IMessage::BlockType m_blocks[MAX_USERMESSAGES]{}; // The array of blocking behaviors for each message type +}; + +/** + * @brief The singleton instance of the MessageManager + */ +extern MessageManagerImpl &MessageManager(); From 0f45ec09fa36534f55035703a2727db546af67b0 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 27 May 2024 05:01:37 +0700 Subject: [PATCH 18/31] Enhanced API interface game message manager Bump major API version Minor refactoring --- rehlds/public/rehlds/IMessageManager.h | 200 +++++++--- rehlds/rehlds/rehlds_messagemngr_impl.cpp | 438 ++++++++++++++++++---- rehlds/rehlds/rehlds_messagemngr_impl.h | 274 +++++++++++--- 3 files changed, 745 insertions(+), 167 deletions(-) diff --git a/rehlds/public/rehlds/IMessageManager.h b/rehlds/public/rehlds/IMessageManager.h index cf818db..d0cabb3 100644 --- a/rehlds/public/rehlds/IMessageManager.h +++ b/rehlds/public/rehlds/IMessageManager.h @@ -44,9 +44,9 @@ public: */ enum class BlockType : uint8 { - Not, // Not a block - Once, // Block once - Set // Set block + Not, // Not a block + Once, // Block once + Set // Set block }; /** @@ -66,99 +66,213 @@ public: SPEC, // Sends to all spectator proxies }; - virtual ~IMessage() {}; + /** + * @brief Data types for message data + */ + enum class DataType : uint8_t + { + Any, // Any part of the message + Dest, // Destination of the message + Index, // Index of the message + Origin, // Origin of the message + Edict, // Pointer to the edict of the recipient client + Param, // Parameter of the message + Max + }; + + virtual ~IMessage() = default; /** * @brief Returns the number of parameters in the message * @return The number of parameters */ - virtual int getParamCount() const = 0; + virtual int getParamCount() const = 0; /** * @brief Returns the type of the parameter at the given index * @param index The index of the parameter * @return The type of the parameter */ - virtual ParamType getParamType(size_t index) const = 0; + virtual ParamType getParamType(size_t index) const = 0; /** * @brief Returns the integer value of the parameter at the given index * @param index The index of the parameter * @return The integer value of the parameter */ - virtual int getParamInt(size_t index) const = 0; + virtual int getParamInt(size_t index) const = 0; /** * @brief Returns the float value of the parameter at the given index * @param index The index of the parameter * @return The float value of the parameter */ - virtual float getParamFloat(size_t index) const = 0; + virtual float getParamFloat(size_t index) const = 0; /** * @brief Returns the string value of the parameter at the given index * @param index The index of the parameter * @return The string value of the parameter */ - virtual const char* getParamString(size_t index) const = 0; + virtual const char* getParamString(size_t index) const = 0; /** * @brief Sets the integer value of the parameter at the given index * @param index The index of the parameter * @param value The integer value to set */ - virtual void setParamInt(size_t index, int value) = 0; + virtual void setParamInt(size_t index, int value) = 0; /** * @brief Sets the float value of the parameter at the given index * @param index The index of the parameter * @param value The float value to set */ - virtual void setParamFloat(size_t index, float value) = 0; + virtual void setParamFloat(size_t index, float value) = 0; /** * @brief Sets the vector value of the parameter at the given index * @param index The index of the parameter * @param pos The vector value to set */ - virtual void setParamVec(size_t index, const float *pos) = 0; + virtual void setParamVec(size_t index, const float *pos) = 0; /** * @brief Sets the string value of the parameter at the given index * @param index The index of the parameter * @param string The string value to set */ - virtual void setParamString(size_t index, const char *string) = 0; + virtual void setParamString(size_t index, const char *string) = 0; /** * @brief Returns the destination of the message * @return The destination of the message */ - virtual Dest getDest() const = 0; + virtual Dest getDest() const = 0; - /** - * @brief Returns the type of the message - * @return The type of the message - */ - virtual int getType() const = 0; + /** + * @brief Returns the index of the message + * @return The index of the message + */ + virtual int getId() const = 0; - /** - * @brief Returns the origin of the message - * @return The origin of the message - */ - virtual const float* getOrigin() const = 0; + /** + * @brief Returns the origin of the message + * @return The origin of the message + */ + virtual const float* getOrigin() const = 0; - /** - * @brief Returns the edict associated with the message - * @return The edict associated with the message - */ + /** + * @brief Returns the edict associated with the message + * @return The edict associated with the message + */ virtual struct edict_s* getEdict() const = 0; - /** - * @brief Returns whether the message has been modified - * @return True if the message has been modified, false otherwise - */ - virtual bool isModified() const = 0; + /** + * @brief Checks if the specified type of message data has been modified + * + * This function allows you to check if any part of the message data, such as its + * destination, type, origin, edict, or any specific parameter, has been modified + * + * @param type The type of the data to check for modification + * This can be one of the following: + * - DataType::Any: Check if any part of the message has been modified + * - DataType::Dest: Check if the destination has been modified + * - DataType::Index: Check if the message ID has been modified + * - DataType::Origin: Check if the origin has been modified + * - DataType::Edict: Check if the edict pointer has been modified + * - DataType::Param: Check if a specific parameter has been modified + * + * @param index The index of the parameter to check for modification (used only when type is DataType::Param) + * Default value is -1, which means the parameter index is not applicable + * + * @return True if the specified data type has been modified, false otherwise + */ + virtual bool isDataModified(DataType type = DataType::Any, size_t index = -1) const = 0; + + /** + * @brief Resets a specific type of message data to its original value + * + * @param type The type of data to reset to its original value + * This can be one of the following: + * - DataType::Any: Reset all modified message data to its original values + * - DataType::Dest: Reset the destination to its original value + * - DataType::Index: Reset the message ID to its original value + * - DataType::Origin: Reset the origin to its original value + * - DataType::Edict: Reset the edict pointer of the recipient client to its original value + * - DataType::Param: Reset a specific parameter to its original value + * + * @param index The index of the parameter to reset (used only when type is DataType::Param) + * Default value is -1, which means the parameter index is not applicable + * + * @return True if the modified data type was reset, false otherwise + */ + virtual bool resetModifiedData(DataType type = DataType::Any, size_t index = -1) = 0; + + /** + * @brief Sets the destination of the message + */ + virtual void setDest(Dest dest) = 0; + + /** + * @brief Sets the index of the message + */ + virtual void setId(int msg_id) = 0; + + /** + * @brief Sets the origin of the message + */ + virtual void setOrigin(const float *origin) = 0; + + /** + * @brief Sets the edict associated with the message + */ + virtual void setEdict(struct edict_s *pEdict) = 0; + + /** + * @brief Returns the original destination of the message before any modifications + * @return The original destination of the message + */ + virtual Dest getOriginalDest() const = 0; + + /** + * @brief Returns the original type of the message before any modifications + * @return The original type of the message + */ + virtual int getOriginalId() const = 0; + + /** + * @brief Returns the original origin of the message before any modifications + * @return The original origin of the message + */ + virtual const float* getOriginalOrigin() const = 0; + + /** + * @brief Returns the original edict associated with the message before any modifications + * @return The original edict associated with the message + */ + virtual struct edict_s* getOriginalEdict() const = 0; + + /** + * @brief Returns the original integer value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original integer value of the parameter + */ + virtual int getOriginalParamInt(size_t index) const = 0; + + /** + * @brief Returns the original float value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original float value of the parameter + */ + virtual float getOriginalParamFloat(size_t index) const = 0; + + /** + * @brief Returns the original string value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original string value of the parameter + */ + virtual const char* getOriginalParamString(size_t index) const = 0; // This must be the last virtual function in class #ifdef REHLDS_SELF @@ -167,7 +281,7 @@ public: #endif }; -#define MESSAGEMNGR_VERSION_MAJOR 1 +#define MESSAGEMNGR_VERSION_MAJOR 2 #define MESSAGEMNGR_VERSION_MINOR 0 /** @@ -178,7 +292,7 @@ class IMessageManager public: using hookfunc_t = void (*)(IVoidHookChain *chain, IMessage *msg); - virtual ~IMessageManager() {}; + virtual ~IMessageManager() = default; /** * @brief Returns the major version of the MessageManager @@ -194,30 +308,30 @@ public: /** * @brief Returns the blocking behavior for the given message type - * @param msgType The message type + * @param msg_id The message type * @return The blocking behavior for the given message type */ - virtual IMessage::BlockType getMessageBlock(int msgType) const = 0; + virtual IMessage::BlockType getMessageBlock(int msg_id) const = 0; /** * @brief Sets the blocking behavior for the given message type - * @param msgType The message type + * @param msg_id The message type * @param blockType The blocking behavior to set */ - virtual void setMessageBlock(int msgType, IMessage::BlockType blockType) = 0; + virtual void setMessageBlock(int msg_id, IMessage::BlockType blockType) = 0; /** * @brief Registers a hook function for the given message type - * @param msgType The message type to register the hook for + * @param msg_id The message type to register the hook for * @param handler The hook function to register * @param priority The priority of the hook function (see enum HookChainPriority) */ - virtual void registerHook(int msgType, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT) = 0; + virtual void registerHook(int msg_id, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT) = 0; /** * @brief Unregisters a hook function for the given message type - * @param msgType The message type to unregister the hook for + * @param msg_id The message type to unregister the hook for * @param handler The hook function to unregister */ - virtual void unregisterHook(int msgType, hookfunc_t handler) = 0; + virtual void unregisterHook(int msg_id, hookfunc_t handler) = 0; }; diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.cpp b/rehlds/rehlds/rehlds_messagemngr_impl.cpp index 617ceb1..dfe2780 100644 --- a/rehlds/rehlds/rehlds_messagemngr_impl.cpp +++ b/rehlds/rehlds/rehlds_messagemngr_impl.cpp @@ -21,53 +21,60 @@ // Constructs a Message object MessageImpl::MessageImpl() { - m_buffer.buffername = "MessageManager/Begin/End"; - m_buffer.data = m_bufferData; - m_buffer.flags = SIZEBUF_ALLOW_OVERFLOW; - m_buffer.cursize = 0; - m_buffer.maxsize = sizeof(m_bufferData); - - m_paramCount = 0; + m_paramCount = 0; + m_modifiedDataBits = 0; } // Sets the active state of the message with the given parameters -void MessageImpl::setActive(int dest, int type, const float *origin, edict_t *edict) +void MessageImpl::setActive(int dest, int id, const float *origin, edict_t *edict) { - m_dest = static_cast(dest); - m_type = type; - m_edict = edict; + // Initialize storage buffers + for (int i = 0; i < MAX_STORAGE; i++) + { + Storage_t &storage = m_Storage[i]; + storage.buf.cursize = 0; + storage.dest = static_cast(dest); + storage.msgid = id; + storage.edict = edict; - // Reset buffer size - m_buffer.flags = SIZEBUF_ALLOW_OVERFLOW; - m_buffer.cursize = 0; + // Copy origin vector if provided + if (origin) + VectorCopy(origin, storage.origin); + else + VectorClear(storage.origin); + } - // Copy origin vector if provided - if (origin) - VectorCopy(origin, m_origin); - else - VectorClear(m_origin); + m_paramCount = 0; + m_modifiedDataBits = 0; } // Sets the buffer for the message void MessageImpl::setBuffer(sizebuf_t *pbuf) { // Copy data from the provided buffer to the message buffer - memcpy(m_buffer.data, pbuf->data, pbuf->cursize); - m_buffer.cursize = pbuf->cursize; + for (int i = 0; i < MAX_STORAGE; i++) + { + Storage_t &storage = m_Storage[i]; + Q_memcpy(storage.buf.data, pbuf->data, pbuf->cursize); + storage.buf.cursize = pbuf->cursize; + } } // Sets the copyback buffer for the message void MessageImpl::setCopybackBuffer(sizebuf_t *pbuf) { + const Storage_t &storage = m_Storage[BACK]; + // Copy data from the message buffer back to the provided buffer - memcpy(pbuf->data, m_buffer.data, m_buffer.cursize); - pbuf->cursize = m_buffer.cursize; + Q_memcpy(pbuf->data, storage.buf.data, storage.buf.cursize); + pbuf->cursize = storage.buf.cursize; } // Clears the message parameters void MessageImpl::clear() { m_paramCount = 0; + m_modifiedDataBits = 0; } // An array containing fixed sizes for various types of parameters @@ -87,9 +94,9 @@ static size_t SIZEOF_PARAMTYPE[] = void MessageImpl::addParam(IMessage::ParamType type, size_t length) { Param_t ¶m = m_params[m_paramCount++]; - param.type = type; - param.len = (length == -1) ? SIZEOF_PARAMTYPE[static_cast(type)] : length; - param.pos = gMsgBuffer.cursize; + param.type = type; + param.newlen = param.oldlen = (length == -1) ? SIZEOF_PARAMTYPE[static_cast(type)] : length; + param.posBack = param.posFront = gMsgBuffer.cursize; } // Sets the value of a primitive parameter at the given index @@ -97,12 +104,11 @@ template void MessageImpl::setParamPrimitive(size_t index, T value) { // Ensure index is within bounds - if (index < 0 || index >= m_paramCount) + if (index >= m_paramCount) return; - const Param_t ¶m = m_params[index]; - - void *pbuf = m_buffer.data + param.pos; + Param_t ¶m = m_params[index]; + void *pbuf = m_Storage[BACK].buf.data + param.posBack; // Set value based on parameter type switch (param.type) @@ -118,7 +124,7 @@ void MessageImpl::setParamPrimitive(size_t index, T value) *(int16 *)pbuf = value; break; case IMessage::ParamType::Long: - *(uint16 *)pbuf = value; + *(uint32 *)pbuf = value; break; case IMessage::ParamType::Angle: // Convert angle value to byte representation with loss of precision @@ -133,7 +139,10 @@ void MessageImpl::setParamPrimitive(size_t index, T value) } // Mark message as modified - m_modified = true; + param.modified = true; + + // Mark the overall status as changed + setModifiedDataBit(DataType::Param); } // Transforms the buffer after setting a string parameter at the given index @@ -143,8 +152,10 @@ void MessageImpl::setTxformBuffer(size_t index, size_t startPos, size_t oldLengt int32_t diffLength = newLength - oldLength; if (diffLength != 0) { + sizebuf_t &buf = m_Storage[BACK].buf; + // Check if the buffer size limit will be exceeded - if (m_buffer.cursize + diffLength > m_buffer.maxsize) + if (buf.cursize + diffLength > buf.maxsize) { Sys_Error( "%s: Refusing to transform string with %i param of user message of %i bytes, " @@ -152,13 +163,18 @@ void MessageImpl::setTxformBuffer(size_t index, size_t startPos, size_t oldLengt } // Move the data in the buffer - size_t moveLength = m_buffer.cursize - (startPos + oldLength); - Q_memmove(m_buffer.data + startPos + newLength, m_buffer.data + startPos + oldLength, moveLength); - m_buffer.cursize += diffLength; + size_t moveLength = buf.cursize - (startPos + oldLength); + if (moveLength > 0) + Q_memmove(buf.data + startPos + newLength, buf.data + startPos + oldLength, moveLength); + + buf.cursize += diffLength; + + if (newLength < oldLength) + Q_memset(buf.data + startPos + newLength + moveLength, 0, oldLength - newLength); // Update the position of all subsequent parameters for (size_t i = index + 1; i < m_paramCount; i++) - m_params[i].pos += diffLength; + m_params[i].posBack += diffLength; } } @@ -166,12 +182,13 @@ void MessageImpl::setTxformBuffer(size_t index, size_t startPos, size_t oldLengt int MessageImpl::getParamInt(size_t index) const { // Ensure index is within bounds - if (index < 0 || index >= m_paramCount) + if (index >= m_paramCount) return 0; // Get the parameter value based on its type - void *buf = m_buffer.data + m_params[index].pos; - switch (m_params[index].type) + const Param_t ¶m = m_params[index]; + const void *buf = m_Storage[BACK].buf.data + param.posBack; + switch (param.type) { case IMessage::ParamType::Byte: return *(uint8 *)buf; @@ -181,7 +198,7 @@ int MessageImpl::getParamInt(size_t index) const case IMessage::ParamType::Entity: return *(int16 *)buf; case IMessage::ParamType::Long: - return *(uint16 *)buf; + return *(uint32 *)buf; default: return 0; // bad type } @@ -191,12 +208,12 @@ int MessageImpl::getParamInt(size_t index) const float MessageImpl::getParamFloat(size_t index) const { // Ensure index is within bounds - if (index < 0 || index >= m_paramCount) + if (index >= m_paramCount) return 0; // Get the parameter value based on its type const Param_t ¶m = m_params[index]; - void *buf = m_buffer.data + param.pos; + const void *buf = m_Storage[BACK].buf.data + param.posBack; switch (param.type) { case IMessage::ParamType::Angle: @@ -214,13 +231,74 @@ float MessageImpl::getParamFloat(size_t index) const const char *MessageImpl::getParamString(size_t index) const { // Ensure index is within bounds - if (index < 0 || index >= m_paramCount) + if (index >= m_paramCount) return nullptr; // Get the parameter value if it is a string const Param_t ¶m = m_params[index]; if (param.type == IMessage::ParamType::String) - return (const char *)m_buffer.data + param.pos; + return (const char *)m_Storage[BACK].buf.data + param.posBack; + + return nullptr; +} + +int MessageImpl::getOriginalParamInt(size_t index) const +{ + // Ensure index is within bounds + if (index >= m_paramCount) + return 0; + + // Get the parameter value based on its type + const Param_t ¶m = m_params[index]; + const void *buf = m_Storage[FRONT].buf.data + param.posFront; + switch (param.type) + { + case IMessage::ParamType::Byte: + return *(uint8 *)buf; + case IMessage::ParamType::Char: + return *(int8 *)buf; + case IMessage::ParamType::Short: + case IMessage::ParamType::Entity: + return *(int16 *)buf; + case IMessage::ParamType::Long: + return *(uint32 *)buf; + default: + return 0; // bad type + } +} + +float MessageImpl::getOriginalParamFloat(size_t index) const +{ + // Ensure index is within bounds + if (index >= m_paramCount) + return 0; + + // Get the parameter value based on its type + const Param_t ¶m = m_params[index]; + const void *buf = m_Storage[FRONT].buf.data + param.posFront; + switch (param.type) + { + case IMessage::ParamType::Angle: + return (float)(*(uint8 *)buf * (360.0 / 256.0)); + case IMessage::ParamType::Coord: + return (float)(*(int16 *)buf * (1.0 / 8)); + default: + break; // bad type + } + + return 0; +} + +const char *MessageImpl::getOriginalParamString(size_t index) const +{ + // Ensure index is within bounds + if (index >= m_paramCount) + return nullptr; + + // Get the parameter value if it is a string + const Param_t ¶m = m_params[index]; + if (param.type == IMessage::ParamType::String) + return (const char *)m_Storage[FRONT].buf.data + param.posFront; return nullptr; } @@ -244,13 +322,13 @@ void MessageImpl::setParamVec(size_t index, const float *pos) return; // Ensure index is within bounds - if (index < 0 || (index + 3) >= m_paramCount) + if ((index + 3) >= m_paramCount) return; // Get the parameter position in the buffer - const Param_t ¶m = m_params[index]; + Param_t ¶m = m_params[index]; - int16 *pbuf = (int16 *)m_buffer.data + param.pos; + int16 *pbuf = (int16 *)m_Storage[BACK].buf.data + param.posBack; // Set each component of the vector parameter *(int16 *)pbuf++ = (int16)(pos[0] * 8.0); @@ -258,7 +336,10 @@ void MessageImpl::setParamVec(size_t index, const float *pos) *(int16 *)pbuf++ = (int16)(pos[2] * 8.0); // Mark message as modified - m_modified = true; + param.modified = true; + + // Mark the overall status as modified + setModifiedDataBit(DataType::Param); } // Sets the string value of the parameter at the given index @@ -268,77 +349,276 @@ void MessageImpl::setParamString(size_t index, const char *value) return; // Ensure index is within bounds - if (index < 0 || index >= m_paramCount) + if (index >= m_paramCount) return; // Calculate the length of the string - size_t length = Q_strlen(value) + 1; - const Param_t ¶m = m_params[index]; + Param_t ¶m = m_params[index]; + + param.newlen = Q_strlen(value) + 1; // Transform buffer to accommodate the new string length - setTxformBuffer(index, param.pos, param.len, length); + setTxformBuffer(index, param.posBack, param.oldlen, param.newlen); // Copy the string value to the buffer - memcpy(m_buffer.data + param.pos, value, length); + Q_memcpy(m_Storage[BACK].buf.data + param.posBack, value, param.newlen); // Mark message as modified - m_modified = true; + param.modified = true; + + // Mark the overall status as modified + setModifiedDataBit(DataType::Param); } -MessageManagerImpl::MessageManagerImpl() +// Sets the destination of the message +void MessageImpl::setDest(Dest dest) +{ + m_Storage[BACK].dest = dest; + setModifiedDataBit(DataType::Dest); +} + +// Sets the type of the message +void MessageImpl::setId(int msg_id) +{ + m_Storage[BACK].msgid = msg_id; + setModifiedDataBit(DataType::Index); +} + +// Sets the origin of the message +void MessageImpl::setOrigin(const float *origin) +{ + // Copy origin vector if provided + if (origin) + VectorCopy(origin, m_Storage[BACK].origin); + else + VectorClear(m_Storage[BACK].origin); + + setModifiedDataBit(DataType::Origin); +} + +//Sets the edict associated with the message +void MessageImpl::setEdict(edict_t *pEdict) +{ + m_Storage[BACK].edict = pEdict; + setModifiedDataBit(DataType::Edict); +} + +bool MessageImpl::isDataModified(DataType type, size_t index) const +{ + if (!isDataTypeModified(type)) + return false; + + if (type == DataType::Param && index != -1) + { + // Ensure index is within bounds + if (index >= m_paramCount) + return false; + + const Param_t ¶m = m_params[index]; + return param.modified; + } + + return true; +} + +void MessageImpl::resetParam(size_t index) +{ + Param_t ¶m = m_params[index]; + + void *pbackbuf = m_Storage[BACK].buf.data + param.posBack; + const void *pfrontbuf = m_Storage[FRONT].buf.data + param.posFront; + + // Set value based on parameter type + switch (param.type) + { + case IMessage::ParamType::Byte: + *(uint8 *)pbackbuf = *(uint8 *)pfrontbuf; + break; + case IMessage::ParamType::Char: + *(int8 *)pbackbuf = *(int8 *)pfrontbuf; + break; + case IMessage::ParamType::Short: + case IMessage::ParamType::Entity: + *(int16 *)pbackbuf = *(int16 *)pfrontbuf; + break; + case IMessage::ParamType::Long: + *(uint32 *)pbackbuf = *(uint32 *)pfrontbuf; + break; + case IMessage::ParamType::Angle: + *(uint8 *)pbackbuf = *(uint8 *)pfrontbuf; + break; + case IMessage::ParamType::Coord: + *(int16 *)pbackbuf = *(int16 *)pfrontbuf; + break; + case IMessage::ParamType::String: + // Return the original string value from the front buffer + setTxformBuffer(index, param.posBack, param.newlen, param.oldlen); + Q_memcpy(pbackbuf, pfrontbuf, param.oldlen); + param.newlen = param.oldlen; + break; + default: + return; // bad type + } + + // Unmark message as modified + param.modified = false; +} + +// Resets a specific message parameter to its original value +bool MessageImpl::resetModifiedData(DataType type, size_t index) +{ + Storage_t &storageBack = m_Storage[BACK]; + const Storage_t &storageFront = m_Storage[FRONT]; + + unsetModifiedDataBit(type); + + switch (type) + { + // Resets all message parameters and storage data to their original values + case DataType::Any: + { + // Update the position of all subsequent parameters + for (size_t i = 0; i < m_paramCount; i++) + { + Param_t ¶m = m_params[i]; + param.posBack = param.posFront; + param.newlen = param.oldlen; + param.modified = false; // Unmark message as modified + } + + // Copy front storage data to back buffer data + Q_memcpy(storageBack.buf.data, storageFront.buf.data, storageFront.buf.maxsize); + + storageBack.dest = storageFront.dest; + storageBack.msgid = storageFront.msgid; + storageBack.edict = storageFront.edict; + + VectorCopy(storageFront.origin, storageBack.origin); + + m_modifiedDataBits = 0; + + break; + } + case DataType::Dest: + storageBack.dest = storageFront.dest; + break; + case DataType::Index: + storageBack.msgid = storageFront.msgid; + break; + case DataType::Origin: + VectorCopy(storageFront.origin, storageBack.origin); + break; + case DataType::Edict: + storageBack.edict = storageFront.edict; + break; + case DataType::Param: + { + // Reset a specific parameter + if (index != -1) + { + // Ensure index is within bounds + if (index < m_paramCount) + resetParam(index); + } + else + { + for (size_t i = 0; i < m_paramCount; i++) + resetParam(i); + } + + // Recalc modified data bits + for (size_t i = 0; i < m_paramCount; i++) + { + const Param_t ¶m = m_params[i]; + if (param.modified) + { + setModifiedDataBit(DataType::Param); + break; + } + } + + break; + } + default: + return false; + } + + // If there was any other modified data, mark Any as overall modified data + if (m_modifiedDataBits != 0) + setModifiedDataBit(DataType::Any); + + return true; +} + +MessageManagerImpl::MessageManagerImpl() : m_stack(m_pool) { m_inblock = false; m_inhook = false; } // Register hook function for the game message type -void MessageManagerImpl::registerHook(int msgType, hookfunc_t handler, int priority) +void MessageManagerImpl::registerHook(int msg_id, hookfunc_t handler, int priority) { - m_hooks[msgType].registerHook(handler, priority); + if (!m_hooks[msg_id]) + m_hooks[msg_id] = new HookRegistry_t; + + if (m_hooks[msg_id]->findHook(handler)) + return; // already registered + + m_hooks[msg_id]->registerHook(handler, priority); } // Unregister hook function for the game message type -void MessageManagerImpl::unregisterHook(int msgType, hookfunc_t handler) +void MessageManagerImpl::unregisterHook(int msg_id, hookfunc_t handler) { - m_hooks[msgType].unregisterHook(handler); + if (!m_hooks[msg_id]) + return; + + m_hooks[msg_id]->unregisterHook(handler); + + if (m_hooks[msg_id]->getCount() == 0) + { + delete m_hooks[msg_id]; + m_hooks[msg_id] = nullptr; + m_pool.clear(); + } } // Get the block type for the game message type -IMessage::BlockType MessageManagerImpl::getMessageBlock(int msgType) const +IMessage::BlockType MessageManagerImpl::getMessageBlock(int msg_id) const { - return m_blocks[msgType]; + return m_blocks[msg_id]; } // Set the block type for the game message type -void MessageManagerImpl::setMessageBlock(int msgType, IMessage::BlockType blockType) +void MessageManagerImpl::setMessageBlock(int msg_id, IMessage::BlockType blockType) { - m_blocks[msgType] = blockType; + m_blocks[msg_id] = blockType; } -bool MessageManagerImpl::MessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed) +bool MessageManagerImpl::MessageBegin(int msg_dest, int msg_id, const float *pOrigin, edict_t *ed) { // Check if the message type is blocked - if (m_blocks[msg_type] != IMessage::BlockType::Not) + if (m_blocks[msg_id] != IMessage::BlockType::Not) { m_inblock = true; return false; } // Check if there are hooks registered for the message type - m_inhook = m_hooks[msg_type].getCount() > 0; + m_inhook = (m_hooks[msg_id] && m_hooks[msg_id]->getCount() > 0); if (m_inhook) { // Check for stack overflow if (m_stack.size() >= m_stack.max_size() - 1) - Sys_Error("%s: stack overflow in #%i user message.\nIndicate potential recursive calls...\n", __func__, msg_type); + Sys_Error("%s: stack overflow in #%i user message.\nIndicate potential recursive calls...\n", __func__, msg_id); // Push a new game message onto the stack - m_stack.push(); + MessageImpl &msg = m_stack.push(); // Initialize the message - MessageImpl &msg = m_stack.back(); - msg.setActive(msg_dest, msg_type, pOrigin, ed); + msg.setActive(msg_dest, msg_id, pOrigin, ed); } return true; @@ -347,7 +627,7 @@ bool MessageManagerImpl::MessageBegin(int msg_dest, int msg_type, const float *p static void EXT_FUNC SendUserMessageData(IMessage *msg) { // Set global variables with message data - gMsgType = msg->getType(); + gMsgType = msg->getId(); gMsgEntity = msg->getEdict(); gMsgDest = static_cast(msg->getDest()); @@ -383,11 +663,11 @@ bool MessageManagerImpl::MessageEnd() gMsgStarted = FALSE; // Get the message from the top of the stack - MessageImpl &msg = m_stack.back(); + MessageImpl &msg = m_stack.top(); // Set buffer from global buffer and call hookchain msg.setBuffer(&gMsgBuffer); - m_hooks[msg.getType()].callChain(SendUserMessageData, &msg); + m_hooks[msg.getId()]->callChain(SendUserMessageData, &msg); m_inhook = false; // Clear the message and pop from the stack @@ -407,7 +687,7 @@ bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length) if (m_inhook) { // Add parameter to top stack message - MessageImpl &msg = m_stack.back(); + MessageImpl &msg = m_stack.top(); msg.addParam(type, length); } @@ -418,14 +698,14 @@ bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length) // Functions intercept to handle messages // -void EXT_FUNC PF_MessageBegin_Intercept(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed) +void EXT_FUNC PF_MessageBegin_Intercept(int msg_dest, int msg_id, const float *pOrigin, edict_t *ed) { // Set global message type - gMsgType = msg_type; + gMsgType = msg_id; // Begin message manager - if (MessageManager().MessageBegin(msg_dest, msg_type, pOrigin, ed)) - PF_MessageBegin_I(msg_dest, msg_type, pOrigin, ed); + if (MessageManager().MessageBegin(msg_dest, msg_id, pOrigin, ed)) + PF_MessageBegin_I(msg_dest, msg_id, pOrigin, ed); } void EXT_FUNC PF_MessageEnd_Intercept(void) diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.h b/rehlds/rehlds/rehlds_messagemngr_impl.h index b0ad18c..549d1bb 100644 --- a/rehlds/rehlds/rehlds_messagemngr_impl.h +++ b/rehlds/rehlds/rehlds_messagemngr_impl.h @@ -21,6 +21,9 @@ #include "IMessageManager.h" #include "hookchains.h" +#include // std::unique_ptr +#include // std::move + /** * @brief Implementation interface for defining message parameters and behavior for a game message object */ @@ -71,6 +74,27 @@ public: */ void setParamInt(size_t index, int value); + /** + * @brief Returns the original integer value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original integer value of the parameter + */ + int getOriginalParamInt(size_t index) const; + + /** + * @brief Returns the original float value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original float value of the parameter + */ + float getOriginalParamFloat(size_t index) const; + + /** + * @brief Returns the original string value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original string value of the parameter + */ + const char* getOriginalParamString(size_t index) const; + /** * @brief Sets the float value of the parameter at the given index * @param index The index of the parameter @@ -92,42 +116,101 @@ public: */ void setParamString(size_t index, const char *string); + /** + * @brief Sets the destination of the message + */ + void setDest(Dest dest); + /** * @brief Returns the destination of the message * @return The destination of the message */ - Dest getDest() const { return m_dest; } + Dest getDest() const { return m_Storage[BACK].dest; } + Dest getOriginalDest() const { return m_Storage[FRONT].dest; } /** - * @brief Returns the type of the message - * @return The type of the message + * @brief Sets the index of the message */ - int getType() const { return m_type; } + void setId(int msg_id); + + /** + * @brief Returns the index of the message + * @return The index of the message + */ + int getId() const { return m_Storage[BACK].msgid; } + int getOriginalId() const { return m_Storage[FRONT].msgid; } + + /** + * @brief Sets the origin of the message + */ + void setOrigin(const float *origin); /** * @brief Returns the origin of the message * @return The origin of the message */ - const float *getOrigin() const { return m_origin; } + const float *getOrigin() const { return m_Storage[BACK].origin; } + const float *getOriginalOrigin() const { return m_Storage[FRONT].origin; } + + /** + * @brief Sets the edict associated with the message + */ + void setEdict(edict_t *pEdict); /** * @brief Returns the edict associated with the message * @return The edict associated with the message */ - edict_t *getEdict() const { return m_edict; } + edict_t *getEdict() const { return m_Storage[BACK].edict; } + edict_t *getOriginalEdict() const { return m_Storage[FRONT].edict; } /** - * @brief Returns whether the message has been modified - * @return True if the message has been modified, false otherwise + * @brief Checks if the specified type of message data has been modified + * + * This function allows you to check if any part of the message data, such as its + * destination, type, origin, edict, or any specific parameter, has been modified + * + * @param type The type of the data to check for modification + * This can be one of the following: + * - DataType::Any: Check if any part of the message has been modified + * - DataType::Dest: Check if the destination has been modified + * - DataType::Index: Check if the message ID has been modified + * - DataType::Origin: Check if the origin has been modified + * - DataType::Edict: Check if the edict pointer has been modified + * - DataType::Param: Check if a specific parameter has been modified + * + * @param index The index of the parameter to check for modification (used only when type is DataType::Param) + * Default value is -1, which means the parameter index is not applicable + * + * @return True if the specified data type has been modified, false otherwise */ - bool isModified() const { return m_modified; } + bool isDataModified(DataType type, size_t index) const; + + /** + * @brief Resets a specific type of message data to its original value + * + * @param type The type of data to reset to its original value + * This can be one of the following: + * - DataType::Any: Reset all modified message data to its original values + * - DataType::Dest: Reset the destination to its original value + * - DataType::Index: Reset the message ID to its original value + * - DataType::Origin: Reset the origin to its original value + * - DataType::Edict: Reset the edict pointer of the recipient client to its original value + * - DataType::Param: Reset a specific parameter to its original value + * + * @param index The index of the parameter to reset (used only when type is DataType::Param) + * Default value is -1, which means the parameter index is not applicable + * + * @return True if the modified data type was reset, false otherwise + */ + bool resetModifiedData(DataType type, size_t index = -1); private: friend class MessageManagerImpl; // Sets the active state of the message with the given parameters - void setActive(int dest, int type, const float *origin, edict_t *edict); + void setActive(int dest, int id, const float *origin, edict_t *edict); // Sets the buffer for the message void setBuffer(sizebuf_t *pbuf); @@ -147,27 +230,78 @@ private: // 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); - bool m_modified; // Flag indicating whether the message has been modified - Dest m_dest; // The destination of the message - int m_type; // The type of the message - float m_origin[3]; // The origin of the message - edict_t* m_edict; // The edict associated with the message - - uint8 m_bufferData[512]; // The buffer data for the message 'm_buffer' - sizebuf_t m_buffer; // The buffer for the message - - struct Param_t + enum { - ParamType type; // The type of the parameter - size_t pos; // The position of the parameter in the buffer - size_t len; // The length of the parameter in the buffer + FRONT, // Original buffer storage data + BACK, // Current modifiable buffer storage data + MAX_STORAGE }; + struct Storage_t + { + // The buffer storage data for the message 'm_buffer' (variable-length message limited to one byte is 256) + uint8_t bufData[256]{}; + + // The buffer for the message + sizebuf_t buf = { "MsgMngr/Begin/End", SIZEBUF_ALLOW_OVERFLOW, bufData, sizeof(bufData), 0 }; + + // The destination of the message + Dest dest{}; + + // The index of the message + int msgid{0}; + + // The origin of the message + float origin[3]{0,0,0}; + + // The edict associated with the message + edict_t *edict{nullptr}; + }; + + Storage_t m_Storage[MAX_STORAGE]; + +#pragma pack(push, 1) + struct Param_t + { + bool modified : 1; // Flag indicating whether the message param has been modified + ParamType type : 3; // The type of the parameter + size_t posBack : 9; // The current position of the parameter in the buffer + 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 + }; +#pragma pack(pop) + static const size_t MAX_PARAMS = 16; // The maximum number of parameters allowed in the message - Param_t m_params[MAX_PARAMS]; // The array of parameters in the message - size_t m_paramCount; // The number of parameters in the message + Param_t m_params[MAX_PARAMS]{}; // The array of parameters in the message + size_t m_paramCount : 4; // The number of parameters in the message + + void resetParam(size_t index); + + void setModifiedDataBit(DataType type); + void unsetModifiedDataBit(DataType type); + bool isDataTypeModified(DataType type) const; + uint8 m_modifiedDataBits : DataType::Max; }; +/** +* Inline methods +*/ +inline void MessageImpl::setModifiedDataBit(DataType type) +{ + m_modifiedDataBits |= ((1 << static_cast(DataType::Any)) | (1 << static_cast(type))); +} + +inline void MessageImpl::unsetModifiedDataBit(DataType type) +{ + m_modifiedDataBits &= ~((1 << static_cast(DataType::Any)) | (1 << static_cast(type))); +} + +inline bool MessageImpl::isDataTypeModified(DataType type) const +{ + return (m_modifiedDataBits & (1 << static_cast(type))) != 0; +} + /** * @brief Implementation interface manages hooks and blocking behavior game messages */ @@ -194,35 +328,35 @@ public: /** * @brief Returns the blocking behavior for the given message type - * @param msgType The message type + * @param msg_id The message type * @return The blocking behavior for the given message type */ - IMessage::BlockType getMessageBlock(int msgType) const; + IMessage::BlockType getMessageBlock(int msg_id) const; /** * @brief Sets the blocking behavior for the given message type - * @param msgType The message type + * @param msg_id The message type * @param blockType The blocking behavior to set */ - void setMessageBlock(int msgType, IMessage::BlockType blockType); + void setMessageBlock(int msg_id, IMessage::BlockType blockType); /** * @brief Registers a hook function for the given message type - * @param msgType The message type to register the hook for + * @param msg_id The message type to register the hook for * @param handler The hook function to register * @param priority The priority of the hook function (see enum HookChainPriority) */ - void registerHook(int msgType, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT); + void registerHook(int msg_id, hookfunc_t handler, int priority = HC_PRIORITY_DEFAULT); /** * @brief Unregisters a hook function for the given message type - * @param msgType The message type to unregister the hook for + * @param msg_id The message type to unregister the hook for * @param handler The hook function to unregister */ - void unregisterHook(int msgType, hookfunc_t handler); + void unregisterHook(int msg_id, hookfunc_t handler); private: - friend void PF_MessageBegin_Intercept(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); + friend void PF_MessageBegin_Intercept(int msg_dest, int msg_id, const float *pOrigin, edict_t *ed); friend void PF_MessageEnd_Intercept(); friend void PF_WriteByte_Intercept(int iValue); friend void PF_WriteChar_Intercept(int iValue); @@ -233,7 +367,7 @@ private: friend void PF_WriteString_Intercept(const char *sz); friend void PF_WriteEntity_Intercept(int iValue); - bool MessageBegin(int msg_dest, int msg_type, const float *pOrigin, edict_t *ed); + bool MessageBegin(int msg_dest, int msg_id, const float *pOrigin, edict_t *ed); bool MessageEnd(); private: @@ -243,31 +377,81 @@ private: bool m_inhook; // Flag indicating whether a message hook is currently active /** - * @brief Helper a templated Stack class to manage a stack of Message objects + * @brief The fixed-size memory pool holds a list of free objects * @tparam T The type of objects stored in the stack * @tparam MAX The maximum size of the stack */ template - class Stack + class MessagePool { public: - void push() { _size++; } - void pop () { _size--; } + std::unique_ptr acquire() + { + if (_size > 0) + return std::move(_freeObjects[--_size]); // reusing + return std::make_unique(); // initialize constructor for new element + } + + void release(std::unique_ptr obj) + { + if (_size < MAX) + _freeObjects[_size++] = std::move(obj); + } + + void clear() + { + while (_size > 0) + _freeObjects[--_size].reset(); + } + + private: + size_t _size{0u}; + std::unique_ptr _freeObjects[MAX]{}; + }; + + /** + * @brief Helper a templated MessageStack class to manage a stack of Message objects with fixed size + * @tparam T The type of objects stored in the stack + * @tparam MAX The maximum size of the stack + */ + template + class MessageStack + { + public: + MessageStack(MessagePool &pool) : _pool(pool) {} + + T &push() + { + std::unique_ptr msg = _pool.acquire(); + _activeObjects[_size++] = std::move(msg); + return *_activeObjects[_size - 1]; + } + + void pop() + { + if (_size > 0) + _pool.release(std::move(_activeObjects[--_size])); + } size_t size() const { return _size; } size_t max_size() const { return MAX; } - const T &back() const { return _data[_size - 1]; } - T &back() { return _data[_size - 1]; } + const T &top() const { return *_activeObjects[_size - 1]; } + T &top() { return *_activeObjects[_size - 1]; } + private: - size_t _size = 0u; - T _data[MAX]{}; + size_t _size{0u}; + std::unique_ptr _activeObjects[MAX]{}; + MessagePool &_pool; }; static const size_t MAX_MSGSTACK = 16; // The maximum size of the message stack, 16 it should be enough - Stack m_stack; + MessagePool m_pool; // A fixed-size memory pool stack for reusable + MessageStack m_stack; + + using HookRegistry_t = IVoidHookChainRegistryImpl; + HookRegistry_t *m_hooks[MAX_USERMESSAGES]{}; - IVoidHookChainRegistryImpl m_hooks [MAX_USERMESSAGES]{}; // The array of hook chain registries for each message type IMessage::BlockType m_blocks[MAX_USERMESSAGES]{}; // The array of blocking behaviors for each message type }; From c1ccc5009a0a6395760e4f4eb5cfddb7353552d2 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Mon, 27 May 2024 05:08:02 +0700 Subject: [PATCH 19/31] Fix warning compiler in linux build --- rehlds/rehlds/rehlds_messagemngr_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.cpp b/rehlds/rehlds/rehlds_messagemngr_impl.cpp index dfe2780..4b381de 100644 --- a/rehlds/rehlds/rehlds_messagemngr_impl.cpp +++ b/rehlds/rehlds/rehlds_messagemngr_impl.cpp @@ -562,7 +562,7 @@ void MessageManagerImpl::registerHook(int msg_id, hookfunc_t handler, int priori if (!m_hooks[msg_id]) m_hooks[msg_id] = new HookRegistry_t; - if (m_hooks[msg_id]->findHook(handler)) + if (m_hooks[msg_id]->findHook((void *)handler)) return; // already registered m_hooks[msg_id]->registerHook(handler, priority); From 9c1e84328ebcdb47ea83c157dd8c7e493dda28a6 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 30 May 2024 19:15:39 +0700 Subject: [PATCH 20/31] Fixed GCC compilation warnings/errors Fixes #1032 --- rehlds/engine/host.cpp | 3 +- rehlds/public/rehlds/IMessageManager.h | 2 +- rehlds/rehlds/rehlds_messagemngr_impl.cpp | 278 +++++++++++++++++++++ rehlds/rehlds/rehlds_messagemngr_impl.h | 282 +--------------------- 4 files changed, 283 insertions(+), 282 deletions(-) diff --git a/rehlds/engine/host.cpp b/rehlds/engine/host.cpp index 673b329..9cd73e6 100644 --- a/rehlds/engine/host.cpp +++ b/rehlds/engine/host.cpp @@ -375,7 +375,8 @@ void EXT_FUNC SV_ClientPrintf_internal(const char *Dest) { char string[1024]; - Q_strlcpy(string, Dest, min(strlen(Dest) + 1, sizeof(string))); + Q_strlcpy(string, Dest); + MSG_WriteByte(&host_client->netchan.message, svc_print); MSG_WriteString(&host_client->netchan.message, string); } diff --git a/rehlds/public/rehlds/IMessageManager.h b/rehlds/public/rehlds/IMessageManager.h index d0cabb3..c93a2ec 100644 --- a/rehlds/public/rehlds/IMessageManager.h +++ b/rehlds/public/rehlds/IMessageManager.h @@ -69,7 +69,7 @@ public: /** * @brief Data types for message data */ - enum class DataType : uint8_t + enum class DataType : uint8 { Any, // Any part of the message Dest, // Destination of the message diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.cpp b/rehlds/rehlds/rehlds_messagemngr_impl.cpp index 4b381de..5340519 100644 --- a/rehlds/rehlds/rehlds_messagemngr_impl.cpp +++ b/rehlds/rehlds/rehlds_messagemngr_impl.cpp @@ -18,6 +18,284 @@ #include "precompiled.h" +/** + * @brief Implementation interface for defining message parameters and behavior for a game message object + */ +class MessageImpl: public IMessage +{ +public: + MessageImpl(); + ~MessageImpl() = default; + + /** + * @brief Returns the number of parameters in the message + * @return The number of parameters + */ + int getParamCount() const { return m_paramCount; } + + /** + * @brief Returns the type of the parameter at the given index + * @param index The index of the parameter + * @return The type of the parameter + */ + ParamType getParamType(size_t index) const { return m_params[index].type; } + + /** + * @brief Returns the integer value of the parameter at the given index + * @param index The index of the parameter + * @return The integer value of the parameter + */ + int getParamInt(size_t index) const; + + /** + * @brief Returns the float value of the parameter at the given index + * @param index The index of the parameter + * @return The float value of the parameter + */ + float getParamFloat(size_t index) const; + + /** + * @brief Returns the string value of the parameter at the given index + * @param index The index of the parameter + * @return The string value of the parameter + */ + const char *getParamString(size_t index) const; + + /** + * @brief Sets the integer value of the parameter at the given index + * @param index The index of the parameter + * @param value The integer value to set + */ + void setParamInt(size_t index, int value); + + /** + * @brief Returns the original integer value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original integer value of the parameter + */ + int getOriginalParamInt(size_t index) const; + + /** + * @brief Returns the original float value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original float value of the parameter + */ + float getOriginalParamFloat(size_t index) const; + + /** + * @brief Returns the original string value of the parameter at the given index before any modifications + * @param index The index of the parameter + * @return The original string value of the parameter + */ + const char* getOriginalParamString(size_t index) const; + + /** + * @brief Sets the float value of the parameter at the given index + * @param index The index of the parameter + * @param value The float value to set + */ + void setParamFloat(size_t index, float value); + + /** + * @brief Sets the vector value of the parameter at the given index + * @param index The index of the parameter + * @param pos The vector value to set + */ + void setParamVec(size_t index, const float *pos); + + /** + * @brief Sets the string value of the parameter at the given index + * @param index The index of the parameter + * @param string The string value to set + */ + void setParamString(size_t index, const char *string); + + /** + * @brief Sets the destination of the message + */ + void setDest(Dest dest); + + /** + * @brief Returns the destination of the message + * @return The destination of the message + */ + Dest getDest() const { return m_Storage[BACK].dest; } + Dest getOriginalDest() const { return m_Storage[FRONT].dest; } + + /** + * @brief Sets the index of the message + */ + void setId(int msg_id); + + /** + * @brief Returns the index of the message + * @return The index of the message + */ + int getId() const { return m_Storage[BACK].msgid; } + int getOriginalId() const { return m_Storage[FRONT].msgid; } + + /** + * @brief Sets the origin of the message + */ + void setOrigin(const float *origin); + + /** + * @brief Returns the origin of the message + * @return The origin of the message + */ + const float *getOrigin() const { return m_Storage[BACK].origin; } + const float *getOriginalOrigin() const { return m_Storage[FRONT].origin; } + + /** + * @brief Sets the edict associated with the message + */ + void setEdict(edict_t *pEdict); + + /** + * @brief Returns the edict associated with the message + * @return The edict associated with the message + */ + edict_t *getEdict() const { return m_Storage[BACK].edict; } + edict_t *getOriginalEdict() const { return m_Storage[FRONT].edict; } + + /** + * @brief Checks if the specified type of message data has been modified + * + * This function allows you to check if any part of the message data, such as its + * destination, type, origin, edict, or any specific parameter, has been modified + * + * @param type The type of the data to check for modification + * This can be one of the following: + * - DataType::Any: Check if any part of the message has been modified + * - DataType::Dest: Check if the destination has been modified + * - DataType::Index: Check if the message ID has been modified + * - DataType::Origin: Check if the origin has been modified + * - DataType::Edict: Check if the edict pointer has been modified + * - DataType::Param: Check if a specific parameter has been modified + * + * @param index The index of the parameter to check for modification (used only when type is DataType::Param) + * Default value is -1, which means the parameter index is not applicable + * + * @return True if the specified data type has been modified, false otherwise + */ + bool isDataModified(DataType type, size_t index) const; + + /** + * @brief Resets a specific type of message data to its original value + * + * @param type The type of data to reset to its original value + * This can be one of the following: + * - DataType::Any: Reset all modified message data to its original values + * - DataType::Dest: Reset the destination to its original value + * - DataType::Index: Reset the message ID to its original value + * - DataType::Origin: Reset the origin to its original value + * - DataType::Edict: Reset the edict pointer of the recipient client to its original value + * - DataType::Param: Reset a specific parameter to its original value + * + * @param index The index of the parameter to reset (used only when type is DataType::Param) + * Default value is -1, which means the parameter index is not applicable + * + * @return True if the modified data type was reset, false otherwise + */ + bool resetModifiedData(DataType type, size_t index = -1); + +private: + + friend class MessageManagerImpl; + + // Sets the active state of the message with the given parameters + void setActive(int dest, int id, const float *origin, edict_t *edict); + + // Sets the buffer for the message + void setBuffer(sizebuf_t *pbuf); + + // Set the copyback buffer for the message + void setCopybackBuffer(sizebuf_t *pbuf); + + // Adds a parameter to the message + void addParam(IMessage::ParamType type, size_t length); + + // Clears the message after execution + void clear(); + + template + void setParamPrimitive(size_t index, T value); + + // 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); + + enum + { + FRONT, // Original buffer storage data + BACK, // Current modifiable buffer storage data + MAX_STORAGE + }; + + struct Storage_t + { + // The buffer storage data for the message 'm_buffer' (variable-length message limited to one byte is 256) + uint8 bufData[256]{}; + + // The buffer for the message + sizebuf_t buf = { "MsgMngr/Begin/End", SIZEBUF_ALLOW_OVERFLOW, bufData, sizeof(bufData), 0 }; + + // The destination of the message + Dest dest{}; + + // The index of the message + int msgid{0}; + + // The origin of the message + float origin[3]{0,0,0}; + + // The edict associated with the message + edict_t *edict{nullptr}; + }; + + Storage_t m_Storage[MAX_STORAGE]; + +#pragma pack(push, 1) + struct Param_t + { + bool modified : 1; // Flag indicating whether the message param has been modified + ParamType type : 3; // The type of the parameter + size_t posBack : 9; // The current position of the parameter in the buffer + 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 + }; +#pragma pack(pop) + + static const size_t MAX_PARAMS = 16; // The maximum number of parameters allowed in the message + Param_t m_params[MAX_PARAMS]{}; // The array of parameters in the message + size_t m_paramCount : 4; // The number of parameters in the message + + void resetParam(size_t index); + + void setModifiedDataBit(DataType type); + void unsetModifiedDataBit(DataType type); + bool isDataTypeModified(DataType type) const; + uint8 m_modifiedDataBits : static_cast(DataType::Max); +}; + +/** +* Inline methods +*/ +inline void MessageImpl::setModifiedDataBit(DataType type) +{ + m_modifiedDataBits |= ((1 << static_cast(DataType::Any)) | (1 << static_cast(type))); +} + +inline void MessageImpl::unsetModifiedDataBit(DataType type) +{ + m_modifiedDataBits &= ~((1 << static_cast(DataType::Any)) | (1 << static_cast(type))); +} + +inline bool MessageImpl::isDataTypeModified(DataType type) const +{ + return (m_modifiedDataBits & (1 << static_cast(type))) != 0; +} + // Constructs a Message object MessageImpl::MessageImpl() { diff --git a/rehlds/rehlds/rehlds_messagemngr_impl.h b/rehlds/rehlds/rehlds_messagemngr_impl.h index 549d1bb..f016b19 100644 --- a/rehlds/rehlds/rehlds_messagemngr_impl.h +++ b/rehlds/rehlds/rehlds_messagemngr_impl.h @@ -24,284 +24,6 @@ #include // std::unique_ptr #include // std::move -/** - * @brief Implementation interface for defining message parameters and behavior for a game message object - */ -class MessageImpl: public IMessage -{ -public: - MessageImpl(); - ~MessageImpl() = default; - - /** - * @brief Returns the number of parameters in the message - * @return The number of parameters - */ - int getParamCount() const { return m_paramCount; } - - /** - * @brief Returns the type of the parameter at the given index - * @param index The index of the parameter - * @return The type of the parameter - */ - ParamType getParamType(size_t index) const { return m_params[index].type; } - - /** - * @brief Returns the integer value of the parameter at the given index - * @param index The index of the parameter - * @return The integer value of the parameter - */ - int getParamInt(size_t index) const; - - /** - * @brief Returns the float value of the parameter at the given index - * @param index The index of the parameter - * @return The float value of the parameter - */ - float getParamFloat(size_t index) const; - - /** - * @brief Returns the string value of the parameter at the given index - * @param index The index of the parameter - * @return The string value of the parameter - */ - const char *getParamString(size_t index) const; - - /** - * @brief Sets the integer value of the parameter at the given index - * @param index The index of the parameter - * @param value The integer value to set - */ - void setParamInt(size_t index, int value); - - /** - * @brief Returns the original integer value of the parameter at the given index before any modifications - * @param index The index of the parameter - * @return The original integer value of the parameter - */ - int getOriginalParamInt(size_t index) const; - - /** - * @brief Returns the original float value of the parameter at the given index before any modifications - * @param index The index of the parameter - * @return The original float value of the parameter - */ - float getOriginalParamFloat(size_t index) const; - - /** - * @brief Returns the original string value of the parameter at the given index before any modifications - * @param index The index of the parameter - * @return The original string value of the parameter - */ - const char* getOriginalParamString(size_t index) const; - - /** - * @brief Sets the float value of the parameter at the given index - * @param index The index of the parameter - * @param value The float value to set - */ - void setParamFloat(size_t index, float value); - - /** - * @brief Sets the vector value of the parameter at the given index - * @param index The index of the parameter - * @param pos The vector value to set - */ - void setParamVec(size_t index, const float *pos); - - /** - * @brief Sets the string value of the parameter at the given index - * @param index The index of the parameter - * @param string The string value to set - */ - void setParamString(size_t index, const char *string); - - /** - * @brief Sets the destination of the message - */ - void setDest(Dest dest); - - /** - * @brief Returns the destination of the message - * @return The destination of the message - */ - Dest getDest() const { return m_Storage[BACK].dest; } - Dest getOriginalDest() const { return m_Storage[FRONT].dest; } - - /** - * @brief Sets the index of the message - */ - void setId(int msg_id); - - /** - * @brief Returns the index of the message - * @return The index of the message - */ - int getId() const { return m_Storage[BACK].msgid; } - int getOriginalId() const { return m_Storage[FRONT].msgid; } - - /** - * @brief Sets the origin of the message - */ - void setOrigin(const float *origin); - - /** - * @brief Returns the origin of the message - * @return The origin of the message - */ - const float *getOrigin() const { return m_Storage[BACK].origin; } - const float *getOriginalOrigin() const { return m_Storage[FRONT].origin; } - - /** - * @brief Sets the edict associated with the message - */ - void setEdict(edict_t *pEdict); - - /** - * @brief Returns the edict associated with the message - * @return The edict associated with the message - */ - edict_t *getEdict() const { return m_Storage[BACK].edict; } - edict_t *getOriginalEdict() const { return m_Storage[FRONT].edict; } - - /** - * @brief Checks if the specified type of message data has been modified - * - * This function allows you to check if any part of the message data, such as its - * destination, type, origin, edict, or any specific parameter, has been modified - * - * @param type The type of the data to check for modification - * This can be one of the following: - * - DataType::Any: Check if any part of the message has been modified - * - DataType::Dest: Check if the destination has been modified - * - DataType::Index: Check if the message ID has been modified - * - DataType::Origin: Check if the origin has been modified - * - DataType::Edict: Check if the edict pointer has been modified - * - DataType::Param: Check if a specific parameter has been modified - * - * @param index The index of the parameter to check for modification (used only when type is DataType::Param) - * Default value is -1, which means the parameter index is not applicable - * - * @return True if the specified data type has been modified, false otherwise - */ - bool isDataModified(DataType type, size_t index) const; - - /** - * @brief Resets a specific type of message data to its original value - * - * @param type The type of data to reset to its original value - * This can be one of the following: - * - DataType::Any: Reset all modified message data to its original values - * - DataType::Dest: Reset the destination to its original value - * - DataType::Index: Reset the message ID to its original value - * - DataType::Origin: Reset the origin to its original value - * - DataType::Edict: Reset the edict pointer of the recipient client to its original value - * - DataType::Param: Reset a specific parameter to its original value - * - * @param index The index of the parameter to reset (used only when type is DataType::Param) - * Default value is -1, which means the parameter index is not applicable - * - * @return True if the modified data type was reset, false otherwise - */ - bool resetModifiedData(DataType type, size_t index = -1); - -private: - - friend class MessageManagerImpl; - - // Sets the active state of the message with the given parameters - void setActive(int dest, int id, const float *origin, edict_t *edict); - - // Sets the buffer for the message - void setBuffer(sizebuf_t *pbuf); - - // Set the copyback buffer for the message - void setCopybackBuffer(sizebuf_t *pbuf); - - // Adds a parameter to the message - void addParam(IMessage::ParamType type, size_t length); - - // Clears the message after execution - void clear(); - - template - void setParamPrimitive(size_t index, T value); - - // 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); - - enum - { - FRONT, // Original buffer storage data - BACK, // Current modifiable buffer storage data - MAX_STORAGE - }; - - struct Storage_t - { - // The buffer storage data for the message 'm_buffer' (variable-length message limited to one byte is 256) - uint8_t bufData[256]{}; - - // The buffer for the message - sizebuf_t buf = { "MsgMngr/Begin/End", SIZEBUF_ALLOW_OVERFLOW, bufData, sizeof(bufData), 0 }; - - // The destination of the message - Dest dest{}; - - // The index of the message - int msgid{0}; - - // The origin of the message - float origin[3]{0,0,0}; - - // The edict associated with the message - edict_t *edict{nullptr}; - }; - - Storage_t m_Storage[MAX_STORAGE]; - -#pragma pack(push, 1) - struct Param_t - { - bool modified : 1; // Flag indicating whether the message param has been modified - ParamType type : 3; // The type of the parameter - size_t posBack : 9; // The current position of the parameter in the buffer - 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 - }; -#pragma pack(pop) - - static const size_t MAX_PARAMS = 16; // The maximum number of parameters allowed in the message - Param_t m_params[MAX_PARAMS]{}; // The array of parameters in the message - size_t m_paramCount : 4; // The number of parameters in the message - - void resetParam(size_t index); - - void setModifiedDataBit(DataType type); - void unsetModifiedDataBit(DataType type); - bool isDataTypeModified(DataType type) const; - uint8 m_modifiedDataBits : DataType::Max; -}; - -/** -* Inline methods -*/ -inline void MessageImpl::setModifiedDataBit(DataType type) -{ - m_modifiedDataBits |= ((1 << static_cast(DataType::Any)) | (1 << static_cast(type))); -} - -inline void MessageImpl::unsetModifiedDataBit(DataType type) -{ - m_modifiedDataBits &= ~((1 << static_cast(DataType::Any)) | (1 << static_cast(type))); -} - -inline bool MessageImpl::isDataTypeModified(DataType type) const -{ - return (m_modifiedDataBits & (1 << static_cast(type))) != 0; -} - /** * @brief Implementation interface manages hooks and blocking behavior game messages */ @@ -446,8 +168,8 @@ private: }; static const size_t MAX_MSGSTACK = 16; // The maximum size of the message stack, 16 it should be enough - MessagePool m_pool; // A fixed-size memory pool stack for reusable - MessageStack m_stack; + MessagePool m_pool; // A fixed-size memory pool stack for reusable + MessageStack m_stack; using HookRegistry_t = IVoidHookChainRegistryImpl; HookRegistry_t *m_hooks[MAX_USERMESSAGES]{}; From 9935f8bc759a3288d4543c76182e5b34706462f3 Mon Sep 17 00:00:00 2001 From: Eason <62255465+jonathan-up@users.noreply.github.com> Date: Wed, 31 Jul 2024 02:37:36 +0800 Subject: [PATCH 21/31] Workflows: fix linux build(Action Checkout) (#1040) * Update build.yml --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d76a2c6..ab038a1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,9 @@ on: release: types: [published] +env: + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true + jobs: windows: name: 'Windows' From ed954a710fb0888328db1bd9b2c916cf9fc63062 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 1 Aug 2024 00:09:32 +0700 Subject: [PATCH 22/31] [HLTV] Fix reverse-engineering mistake in ObjectDictionary::RemoveIndex --- rehlds/common/ObjectDictionary.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rehlds/common/ObjectDictionary.cpp b/rehlds/common/ObjectDictionary.cpp index 84a0f88..6f6f88c 100644 --- a/rehlds/common/ObjectDictionary.cpp +++ b/rehlds/common/ObjectDictionary.cpp @@ -195,7 +195,7 @@ bool ObjectDictionary::RemoveIndex(int index, bool freeObjectMemory) CheckSize(); ClearCache(); - return false; + return true; } bool ObjectDictionary::RemoveIndexRange(int minIndex, int maxIndex) From c8308a2c60d34e4f735b3404f86701109e85f73b Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 1 Aug 2024 00:12:34 +0700 Subject: [PATCH 23/31] [HLTV] Fix reverse-engineering mistake in World::WritePacketEntities --- rehlds/HLTV/Core/src/World.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rehlds/HLTV/Core/src/World.cpp b/rehlds/HLTV/Core/src/World.cpp index b355420..2363c11 100644 --- a/rehlds/HLTV/Core/src/World.cpp +++ b/rehlds/HLTV/Core/src/World.cpp @@ -1341,7 +1341,7 @@ void World::WritePacketEntities(BitBuffer *stream, frame_t *frame, frame_t *delt header.remove = false; delta_t *delta = GetDeltaEncoder(newindex, header.custom); - m_Delta.WriteDelta(stream, (byte *)&deltaEntities[oldnum], (byte *)&frameEntities[newnum], true, delta, &header); + m_Delta.WriteDelta(stream, (byte *)&deltaEntities[newindex], (byte *)&frameEntities[newnum], false, delta, &header); oldnum++; newnum++; From 2834fb8494bc2808a071adf48ac00758816f7bbe Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 1 Aug 2024 02:56:20 +0700 Subject: [PATCH 24/31] Minor refactoring and improve readability --- rehlds/HLTV/Core/src/World.cpp | 287 ++++++++++++++++-------------- rehlds/HLTV/Core/src/World.h | 3 + rehlds/HLTV/common/net_internal.h | 3 + rehlds/engine/delta_packet.h | 3 + rehlds/engine/sv_main.cpp | 265 ++++++++++++++------------- 5 files changed, 300 insertions(+), 261 deletions(-) diff --git a/rehlds/HLTV/Core/src/World.cpp b/rehlds/HLTV/Core/src/World.cpp index 2363c11..4a79594 100644 --- a/rehlds/HLTV/Core/src/World.cpp +++ b/rehlds/HLTV/Core/src/World.cpp @@ -1060,15 +1060,18 @@ void World::ParseClientData(BitBuffer *stream, unsigned int deltaSeqNr, BitBuffe } } +// When a delta command is received from the server +// We need to grab the entity # out of it any the bit settings, too int World::ParseDeltaHeader(BitBuffer *stream, bool &remove, bool &custom, int &numbase, bool &newbl, int &newblindex, bool full, int &offset) { int num; - bool isdelta, isnext; + bool isdelta; offset = 0; custom = false; - newbl = false; + newbl = false; + // This full update (non-delta) if (full) { isdelta = stream->ReadBit() ? true : false; @@ -1077,6 +1080,8 @@ int World::ParseDeltaHeader(BitBuffer *stream, bool &remove, bool &custom, int & else { isdelta = false; + + // the entity was removed from server or not remove = stream->ReadBit() ? true : false; } @@ -1088,11 +1093,11 @@ int World::ParseDeltaHeader(BitBuffer *stream, bool &remove, bool &custom, int & { if (stream->ReadBit()) { - num = stream->ReadBits(11); + num = stream->ReadBits(MAX_EDICT_BITS); } else { - int delta = stream->ReadBits(6); + int delta = stream->ReadBits(DELTA_OFFSET_BITS); num = delta + numbase; } } @@ -1105,20 +1110,19 @@ int World::ParseDeltaHeader(BitBuffer *stream, bool &remove, bool &custom, int & if (m_MaxInstanced_BaseLine) { - isnext = stream->ReadBit() ? true : false; - if (isnext) + newbl = stream->ReadBit() ? true : false; + + if (newbl) { - newbl = true; - newblindex = stream->ReadBits(6); + newblindex = stream->ReadBits(MAX_BASELINE_BITS); } } if (full && !newbl) { - isnext = stream->ReadBit() ? true : false; - if (isnext) + if (stream->ReadBit() != 0) { - offset = stream->ReadBits(6); + offset = stream->ReadBits(MAX_BASELINE_BITS); } } } @@ -1152,10 +1156,10 @@ int World::CompressFrame(frame_t *from, BitBuffer *stream) unsigned char *start = stream->CurrentByte(); for (auto entnum = 0u; entnum < from->entitynum; entnum++) { - header.num = entities[entnum].number; - header.custom = (entities[entnum].entityType & ENTITY_BEAM) == ENTITY_BEAM; + header.num = entities[entnum].number; + header.custom = (entities[entnum].entityType & ENTITY_BEAM) == ENTITY_BEAM; header.newblindex = 0; - header.newbl = false; + header.newbl = false; entity_state_t *baseline = &m_BaseLines[header.num]; header.offset = FindBestBaseline(entnum, &baseline, entities, header.num, header.custom); @@ -1273,26 +1277,25 @@ bool World::GetDeltaFromCache(unsigned int seqNr, unsigned int deltaNr, BitBuffe return 0; } +// marker an entity index that not in an old packet (for svc_packetentities) +#define ENTITY_SENTINEL 9999 + void World::WritePacketEntities(BitBuffer *stream, frame_t *frame, frame_t *deltaframe) { - int oldmax; - int oldindex; - int newindex; - int newnum; - int oldnum; - entity_state_t *frameEntities; - entity_state_t *deltaEntities; + unsigned int oldmax, oldindex, newindex; + int newnum, oldnum; + entity_state_t *frameEntities, *deltaEntities; deltacallback_t header; header.instanced_baseline = (m_MaxInstanced_BaseLine > 0) ? true : false; - header.num = 0; - header.offset = 0; - header.numbase = 0; + header.num = 0; + header.offset = 0; + header.numbase = 0; header.newblindex = 0; - header.full = false; - header.newbl = false; - header.remove = false; - header.custom = false; + header.newbl = false; + header.remove = false; + header.full = false; + header.custom = false; if (frame->delta || deltaframe->delta) { m_System->Errorf("World::WritePacketEntities: frame and delta frame must be uncompressed.\n"); @@ -1302,79 +1305,82 @@ void World::WritePacketEntities(BitBuffer *stream, frame_t *frame, frame_t *delt m_Delta.SetTime(frame->time); oldmax = deltaframe->entitynum; - newnum = 0; // index in frame->entities - oldnum = 0; // index in deltaframe->entities + newindex = 0; // index in frame->entities + oldindex = 0; // index in deltaframe->entities frameEntities = (entity_state_t *)frame->entities; deltaEntities = (entity_state_t *)deltaframe->entities; stream->StartBitMode(); - while (true) + + while (newindex < frame->entitynum || oldindex < oldmax) { - if ((unsigned)newnum < frame->entitynum) - { - newindex = frameEntities[newnum].number; - } - else - { - if (oldnum >= oldmax) - break; + newnum = (newindex >= frame->entitynum) ? ENTITY_SENTINEL : frameEntities[newindex].number; + oldnum = (oldindex >= oldmax) ? ENTITY_SENTINEL : deltaEntities[oldindex].number; - // TODO: Unreachable code - if ((unsigned)newnum < frame->entitynum) - newindex = frameEntities[newnum].number; - else - newindex = 9999; - } - - if (oldnum < oldmax) - oldindex = deltaEntities[oldnum].number; - else - oldindex = 9999; - - if (newindex == oldindex) + // this is a delta update of the entity from previous position + if (newnum == oldnum) { - header.custom = (frameEntities[newnum].entityType & ENTITY_BEAM) == ENTITY_BEAM; - header.num = newindex; + // delta update from old position + // because the force parm is false, this will not result + // in any bytes being emitted if the entity has not changed at all + // note that players are always 'newentities', this updates their oldorigin always + // and prevents warping + + header.custom = (frameEntities[newindex].entityType & ENTITY_BEAM) == ENTITY_BEAM; + header.num = newnum; header.newblindex = 0; - header.newbl = false; - header.remove = false; + header.newbl = false; + header.remove = false; - delta_t *delta = GetDeltaEncoder(newindex, header.custom); - m_Delta.WriteDelta(stream, (byte *)&deltaEntities[newindex], (byte *)&frameEntities[newnum], false, delta, &header); + delta_t *delta = GetDeltaEncoder(newnum, header.custom); + m_Delta.WriteDelta(stream, (byte *)&deltaEntities[oldindex], (byte *)&frameEntities[newindex], false, delta, &header); - oldnum++; - newnum++; + oldindex++; + newindex++; continue; } - if (newindex >= oldindex) + // Figure out how we want to update the entity + // This is a new entity, send it from the baseline + if (newnum < oldnum) { - if (newindex > oldindex) - { - header.num = oldindex; - header.remove = true; - header.newbl = false; - header.newblindex = 0; + // + // If the entity was not in the old packet (oldnum == 9999), + // then delta from the baseline since this is a new entity + header.custom = (frameEntities[newindex].entityType & ENTITY_BEAM) == ENTITY_BEAM; + header.num = newnum; + header.newblindex = 0; + header.newbl = false; + header.remove = false; - m_Delta.WriteHeader(stream, &header); - ++oldnum; - } + delta_t *delta = GetDeltaEncoder(newnum, header.custom); + m_Delta.WriteDelta(stream, (byte *)&m_BaseLines[newnum], (byte *)&frameEntities[newindex], true, delta, &header); + newindex++; continue; } - header.custom = (frameEntities[newnum].entityType & ENTITY_BEAM) == ENTITY_BEAM; - header.newblindex = 0; - header.num = newindex; - header.remove = false; - header.newbl = false; + // the old entity isn't present in the new message + if (newnum > oldnum) + { + // + // If the entity was in the old list, but is not in the new list (newindex == 9999), + // then construct a special remove message - delta_t *delta = GetDeltaEncoder(newindex, header.custom); - m_Delta.WriteDelta(stream, (byte *)&m_BaseLines[oldnum], (byte *)&frameEntities[newnum], true, delta, &header); - newnum++; + header.num = oldnum; + header.newblindex = 0; + header.newbl = false; + header.remove = true; // tell the client that entity was removed from server + + m_Delta.WriteHeader(stream, &header); + oldindex++; + continue; + } } + // No more entities.. (end of packet entities) stream->WriteBits(0, 16); + stream->EndBitMode(); } @@ -1467,13 +1473,14 @@ double World::GetTime() return m_WorldTime; } +// An svc_packetentities has just been parsed, deal with the rest of the data stream bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsigned int from) { - int newnum, oldnum; - int oldindex, newindex; + int newnum, oldnum; + unsigned int oldindex, newindex; - bool remove, custom, newbl; - int newblindex, numbase, offset; + bool remove, custom, newbl; + int newblindex, numbase, offset; frame_t deltaFrame; if (!GetUncompressedFrame(from, &deltaFrame)) { @@ -1485,11 +1492,15 @@ bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsi oldindex = 0; newindex = 0; - remove = false; - custom = false; - newbl = false; + numbase = 0; newblindex = 0; - numbase = 0; + newbl = false; + remove = false; + custom = false; + + // + // Parse uncompress entities + // m_Delta.SetTime(frame->time); stream->StartBitMode(); @@ -1500,60 +1511,75 @@ bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsi while (stream->PeekBits(16)) { newnum = ParseDeltaHeader(stream, remove, custom, numbase, newbl, newblindex, false, offset); + oldnum = (oldindex >= deltaFrame.entitynum) ? ENTITY_SENTINEL : deltaEntity[oldindex].number; - if ((unsigned)oldindex < deltaFrame.entitynum) - oldnum = deltaEntity[oldindex].number; - else - oldnum = 9999; - - while (newnum > oldnum) + while (oldnum < newnum) { + // + // one or more entities from the old packet are unchanged + if (newindex >= MAX_PACKET_ENTITIES) { m_System->DPrintf("WARNING!World::UncompressEntitiesFromStream: newindex >= MAX_PACKET_ENTITIES.\n"); stream->m_Overflowed = true; } + // copy one of the old entities over to the new packet unchanged Q_memcpy(&entity[newindex], &deltaEntity[oldindex], sizeof(entity[newindex])); newindex++; oldindex++; - if ((unsigned)oldindex < deltaFrame.entitynum) - oldnum = deltaEntity[oldindex].number; - else - oldnum = 9999; + // lookup next index + oldnum = (oldindex >= deltaFrame.entitynum) ? ENTITY_SENTINEL : deltaEntity[oldindex].number; } - if (newnum >= oldnum) + // delta from previous state + if (newnum == oldnum) { - if (newnum == oldnum) + if (remove) { - if (remove) - { - ++oldindex; - } - else - { - entity[newindex].entityType = custom ? ENTITY_BEAM : ENTITY_NORMAL; - - delta_t *delta = GetDeltaEncoder(newnum, custom); - m_Delta.ParseDelta(stream, (byte *)&deltaEntity[oldindex], (byte *)&entity[newindex], delta); - - entity[newindex].number = newnum; - ++newindex; - ++oldindex; - } + oldindex++; + continue; } + + // + // Insert new the entity to frame + + entity[newindex].entityType = custom ? ENTITY_BEAM : ENTITY_NORMAL; + + delta_t *delta = GetDeltaEncoder(newnum, custom); + m_Delta.ParseDelta(stream, (byte *)&deltaEntity[oldindex], (byte *)&entity[newindex], delta); + + entity[newindex].number = newnum; + newindex++; + oldindex++; + + continue; } - else if (!remove) + + // Figure out how we want to update the entity + // This is a new entity, sent it from the baseline + if (newnum < oldnum) { + // + // If the entity was not in the old packet (oldnum == 9999), + // then delta from the baseline since this is a new entity + + if (remove) + { + continue; + } + if (newindex >= MAX_PACKET_ENTITIES) { m_System->DPrintf("World::UncompressEntitiesFromStream: newindex >= MAX_PACKET_ENTITIES.\n"); stream->m_Overflowed = true; } + // + // Insert new the entity to frame + entity_state_t *baseline; if (newbl) { @@ -1574,9 +1600,12 @@ bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsi m_Delta.ParseDelta(stream, (byte *)baseline, (byte *)&entity[newindex], delta); entity[newindex].number = newnum; newindex++; + + continue; } } + // peek bit == 0 end of packet entities if (stream->ReadShort()) { m_System->DPrintf("WARNING! World::UncompressEntitiesFromStream: missing end tag.\n"); @@ -1585,7 +1614,10 @@ bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsi stream->EndBitMode(); - while ((unsigned)oldindex < deltaFrame.entitynum) + // Copy all the rest of the entities from the old packet + // + + while (oldindex != ENTITY_SENTINEL && oldindex < deltaFrame.entitynum) { if (newindex >= MAX_PACKET_ENTITIES) { @@ -1593,33 +1625,22 @@ bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream, unsi stream->m_Overflowed = true; } + // copy everything to the new state we are delta'ing Q_memcpy(&entity[newindex], &deltaEntity[oldindex], sizeof(entity[newindex])); newindex++; oldindex++; } - if (newindex != frame->entitynum) { - m_System->DPrintf("WARNING! World::UncompressEntitiesFromStream: newindex != frame->entitynum.\n"); - } - return true; } bool World::UncompressEntitiesFromStream(frame_t *frame, BitBuffer *stream) { - int num; - int newindex = 0; - int entnum = frame->entitynum; + int num, newindex = 0; + int entnum = frame->entitynum; entity_state_t *baseline; - bool remove, custom, newbl; - int newblindex, numbase, offset; - - newblindex = 0; - numbase = 0; - - remove = false; - custom = false; - newbl = false; + bool remove = false, custom = false, newbl = false; + int newblindex = 0, numbase = 0, offset; entity_state_t *entities = (entity_state_t *)frame->entities; m_Delta.SetTime(frame->time); @@ -2240,8 +2261,8 @@ void World::RearrangeFrame(frame_t *frame, int seqNrOffset, float timeOffset) BitBuffer tempStream(frame->entities, frame->entitiesSize); Q_memset(frame->entities, 0, frame->entitiesSize); - int newsize = CompressFrame(&fullFrame, &tempStream); - if ((unsigned)newsize > frame->entitiesSize || tempStream.IsOverflowed()) { + unsigned int newsize = CompressFrame(&fullFrame, &tempStream); + if (newsize > frame->entitiesSize || tempStream.IsOverflowed()) { m_System->Printf("WARNING! World::RearrangeFrame: wrong entities size (%i != %i).\n", frame->entitiesSize, newsize); return; } diff --git a/rehlds/HLTV/Core/src/World.h b/rehlds/HLTV/Core/src/World.h index b84b99a..056b398 100644 --- a/rehlds/HLTV/Core/src/World.h +++ b/rehlds/HLTV/Core/src/World.h @@ -39,6 +39,9 @@ #define RESOURCE_INDEX_BITS 12 #define RESOURCE_MAX_COUNT (1 << RESOURCE_INDEX_BITS) +#define MAX_BASELINE_BITS 6 +#define MAX_BASELINES (1 << MAX_BASELINE_BITS) + class World: public IWorld, public BaseSystemModule { public: World() {} diff --git a/rehlds/HLTV/common/net_internal.h b/rehlds/HLTV/common/net_internal.h index 5b3a8cd..ce20e94 100644 --- a/rehlds/HLTV/common/net_internal.h +++ b/rehlds/HLTV/common/net_internal.h @@ -98,6 +98,9 @@ // Max size of udp packet payload #define MAX_UDP_PACKET 4010 // 9 bytes SPLITHEADER + 4000 payload? +#define DELTA_OFFSET_BITS 6 +#define DELTA_OFFSET_MAX ((1 << DELTA_OFFSET_BITS) - 1) + enum svc_commands_e { svc_bad, diff --git a/rehlds/engine/delta_packet.h b/rehlds/engine/delta_packet.h index 6bed988..942031d 100644 --- a/rehlds/engine/delta_packet.h +++ b/rehlds/engine/delta_packet.h @@ -30,6 +30,9 @@ #include "entity_state.h" +// marker an entity index that not in an old packet (for svc_packetentities) +#define ENTITY_SENTINEL 9999 + typedef struct packet_entities_s { int num_entities; diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 5dd76f2..b95c1b1 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -4529,15 +4529,20 @@ int SV_CreatePacketEntities(sv_delta_t type, client_t *client, packet_entities_t return g_RehldsHookchains.m_SV_CreatePacketEntities.callChain(SV_CreatePacketEntities_api, type, GetRehldsApiClient(client), to, msg); } +// Computes either a compressed, or uncompressed delta buffer for the client +// Returns the size IN BITS of the message buffer created int SV_CreatePacketEntities_internal(sv_delta_t type, client_t *client, packet_entities_t *to, sizebuf_t *msg) { - packet_entities_t *from; - int oldindex; - int newindex; - int oldnum; - int newnum; - int oldmax; - int numbase; + edict_t *ent; + client_frame_t *fromframe; + packet_entities_t *from; // Entity packet for that frame + delta_t *delta; + int oldindex, newindex; + int oldnum, newnum; + int oldmax; + qboolean custom = FALSE; + int offset; + int numbase = 0; // fix for https://github.com/dreamstalker/rehlds/issues/24 #ifdef REHLDS_FIXES @@ -4545,159 +4550,163 @@ int SV_CreatePacketEntities_internal(sv_delta_t type, client_t *client, packet_e uint64 toBaselinesForceMask[MAX_PACKET_ENTITIES]; #endif - numbase = 0; + // See if this is a full update if (type == sv_packet_delta) { - client_frame_t *fromframe = &client->frames[SV_UPDATE_MASK & client->delta_sequence]; + // This is the frame that we are going to delta update from + fromframe = &client->frames[SV_UPDATE_MASK & client->delta_sequence]; from = &fromframe->entities; _mm_prefetch((const char*)&from->entities[0], _MM_HINT_T0); _mm_prefetch(((const char*)&from->entities[0]) + 64, _MM_HINT_T0); - oldmax = from->num_entities; - MSG_WriteByte(msg, svc_deltapacketentities); - MSG_WriteShort(msg, to->num_entities); - MSG_WriteByte(msg, client->delta_sequence); + oldmax = fromframe->entities.num_entities; + + MSG_WriteByte(msg, svc_deltapacketentities); // This is a delta + MSG_WriteShort(msg, to->num_entities); // This is how many ents are in the new packet + MSG_WriteByte(msg, client->delta_sequence); // This is the sequence # that we are updating from } else { - oldmax = 0; + oldmax = 0; // no delta update from = NULL; - MSG_WriteByte(msg, svc_packetentities); - MSG_WriteShort(msg, to->num_entities); + + MSG_WriteByte(msg, svc_packetentities); // Just a packet update. + MSG_WriteShort(msg, to->num_entities); // This is the # of entities we are sending. } - newnum = 0; //index in to->entities - oldnum = 0; //index in from->entities + newindex = 0; // index in to->entities + oldindex = 0; // index in from->entities + MSG_StartBitWriting(msg); - while (1) + + while (newindex < to->num_entities || oldindex < oldmax) { - if (newnum < to->num_entities) - { - newindex = to->entities[newnum].number; - } - else - { - if (oldnum >= oldmax) - break; + newnum = (newindex >= to->num_entities) ? ENTITY_SENTINEL : to->entities[newindex].number; + oldnum = (!from || oldindex >= oldmax) ? ENTITY_SENTINEL : from->entities[oldindex].number; // FIXED: from can be null - if (newnum < to->num_entities) - newindex = to->entities[newnum].number; + // this is a delta update of the entity from old position + if (newnum == oldnum) + { + // delta update from old position + // because the force parm is false, this will not result + // in any bytes being emitted if the entity has not changed at all + // note that players are always 'newentities', this updates their oldorigin always + // and prevents warping + + entity_state_t *baseline = &to->entities[newindex]; + custom = (baseline->entityType == ENTITY_BEAM) ? TRUE : FALSE; + SV_SetCallback(newnum, FALSE, custom, &numbase, FALSE, 0); + DELTA_WriteDelta((uint8 *)&from->entities[oldindex], (uint8 *)baseline, FALSE, custom ? g_pcustomentitydelta : (SV_IsPlayerIndex(newnum) ? g_pplayerdelta : g_pentitydelta), &SV_InvokeCallback); + oldindex++; + _mm_prefetch((const char*)&from->entities[oldindex], _MM_HINT_T0); + _mm_prefetch(((const char*)&from->entities[oldindex]) + 64, _MM_HINT_T0); + newindex++; + continue; + } + + // Figure out how we want to update the entity + // This is a new entity, send it from the baseline + if (newnum < oldnum) + { + // + // If the entity was not in the old packet (oldnum == 9999), + // then delta from the baseline since this is a new entity + + ent = EDICT_NUM(newnum); + custom = (to->entities[newindex].entityType == ENTITY_BEAM) ? TRUE : FALSE; + + if (from == NULL) + SV_SetCallback(newnum, FALSE, custom, &numbase, TRUE, 0); else - newindex = 9999; - } + SV_SetCallback(newnum, FALSE, custom, &numbase, FALSE, 0); -#ifdef REHLDS_FIXES - if (oldnum < oldmax && from) -#else - if (oldnum < oldmax) -#endif - oldindex = from->entities[oldnum].number; - else - oldindex = 9999; + // this is a new entity, send it from the baseline + entity_state_t *baseline = &g_psv.baselines[newnum]; - if (newindex == oldindex) - { - entity_state_t *baseline_ = &to->entities[newnum]; - qboolean custom = baseline_->entityType & 0x2 ? TRUE : FALSE; - SV_SetCallback(newindex, FALSE, custom, &numbase, FALSE, 0); - DELTA_WriteDelta((uint8 *)&from->entities[oldnum], (uint8 *)baseline_, FALSE, custom ? g_pcustomentitydelta : (SV_IsPlayerIndex(newindex) ? g_pplayerdelta : g_pentitydelta), &SV_InvokeCallback); - ++oldnum; - _mm_prefetch((const char*)&from->entities[oldnum], _MM_HINT_T0); - _mm_prefetch(((const char*)&from->entities[oldnum]) + 64, _MM_HINT_T0); - ++newnum; - continue; - } - - if (newindex >= oldindex) - { - if (newindex > oldindex) + if (sv_instancedbaseline.value && g_psv.instance_baselines->number != 0 && newnum > sv_lastnum) { - SV_WriteDeltaHeader(oldindex, TRUE, FALSE, &numbase, FALSE, 0, FALSE, 0); - ++oldnum; - _mm_prefetch((const char*)&from->entities[oldnum], _MM_HINT_T0); - _mm_prefetch(((const char*)&from->entities[oldnum]) + 64, _MM_HINT_T0); - } - continue; - } - - edict_t *ent = EDICT_NUM(newindex); - qboolean custom = to->entities[newnum].entityType & 0x2 ? TRUE : FALSE; - SV_SetCallback( - newindex, - FALSE, - custom, - &numbase, - from == NULL, - 0); - - entity_state_t *baseline_ = &g_psv.baselines[newindex]; - if (sv_instancedbaseline.value != 0.0f && g_psv.instance_baselines->number != 0 && newindex > sv_lastnum) - { - for (int i = 0; i < g_psv.instance_baselines->number; i++) - { - if (g_psv.instance_baselines->classname[i] == ent->v.classname) + for (int i = 0; i < g_psv.instance_baselines->number; i++) { - SV_SetNewInfo(i); - baseline_ = &g_psv.instance_baselines->baseline[i]; - break; + if (g_psv.instance_baselines->classname[i] == ent->v.classname) + { + SV_SetNewInfo(i); + baseline = &g_psv.instance_baselines->baseline[i]; + break; + } } } - } - else - { - if (!from) + else { - int offset = SV_FindBestBaseline(newnum, &baseline_, to->entities, newindex, custom); - _mm_prefetch((const char*)baseline_, _MM_HINT_T0); - _mm_prefetch(((const char*)baseline_) + 64, _MM_HINT_T0); - if (offset) - SV_SetCallback(newindex, FALSE, custom, &numbase, TRUE, offset); + // If this is full update + if (!from) + { + offset = SV_FindBestBaseline(newindex, &baseline, to->entities, newnum, custom); + _mm_prefetch((const char*)baseline, _MM_HINT_T0); + _mm_prefetch(((const char*)baseline) + 64, _MM_HINT_T0); + if (offset) + SV_SetCallback(newnum, FALSE, custom, &numbase, TRUE, offset); - // fix for https://github.com/dreamstalker/rehlds/issues/24 + // fix for https://github.com/dreamstalker/rehlds/issues/24 #ifdef REHLDS_FIXES - if (offset) - baselineToIdx = newnum - offset; + if (offset) + baselineToIdx = newindex - offset; #endif + } } + + delta = custom ? g_pcustomentitydelta : (SV_IsPlayerIndex(newnum) ? g_pplayerdelta : g_pentitydelta); + + // fix for https://github.com/dreamstalker/rehlds/issues/24 +#ifdef REHLDS_FIXES + DELTA_WriteDeltaForceMask( + (uint8 *)baseline, + (uint8 *)&to->entities[newindex], + TRUE, + delta, + &SV_InvokeCallback, + baselineToIdx != -1 ? &toBaselinesForceMask[baselineToIdx] : NULL + ); + baselineToIdx = -1; + + uint64 origMask = DELTA_GetOriginalMask(delta); + uint64 usedMask = DELTA_GetMaskU64(delta); + uint64 diffMask = origMask ^ usedMask; + + // Remember changed fields that was marked in original mask, but unmarked by the conditional encoder + toBaselinesForceMask[newindex] = diffMask & origMask; + +#else // REHLDS_FIXES + DELTA_WriteDelta( + (uint8 *)baseline, + (uint8 *)&to->entities[newindex], + TRUE, + delta, + &SV_InvokeCallback + ); +#endif // REHLDS_FIXES + + newindex++; + continue; } + // the old entity isn't present in the new message + if (newnum > oldnum) + { + // + // If the entity was in the old list, but is not in the new list (newnum == 9999), + // then construct a special remove message - delta_t* delta = custom ? g_pcustomentitydelta : (SV_IsPlayerIndex(newindex) ? g_pplayerdelta : g_pentitydelta); - - // fix for https://github.com/dreamstalker/rehlds/issues/24 -#ifdef REHLDS_FIXES - DELTA_WriteDeltaForceMask( - (uint8 *)baseline_, - (uint8 *)&to->entities[newnum], - TRUE, - delta, - &SV_InvokeCallback, - baselineToIdx != -1 ? &toBaselinesForceMask[baselineToIdx] : NULL - ); - baselineToIdx = -1; - - uint64 origMask = DELTA_GetOriginalMask(delta); - uint64 usedMask = DELTA_GetMaskU64(delta); - uint64 diffMask = origMask ^ usedMask; - - //Remember changed fields that was marked in original mask, but unmarked by the conditional encoder - toBaselinesForceMask[newnum] = diffMask & origMask; - - -#else //REHLDS_FIXES - DELTA_WriteDelta( - (uint8 *)baseline_, - (uint8 *)&to->entities[newnum], - TRUE, - delta, - &SV_InvokeCallback - ); -#endif //REHLDS_FIXES - - ++newnum; - + // remove = TRUE, tell the client that entity was removed from server + SV_WriteDeltaHeader(oldnum, TRUE, FALSE, &numbase, FALSE, 0, FALSE, 0); + oldindex++; + _mm_prefetch((const char*)&from->entities[oldindex], _MM_HINT_T0); + _mm_prefetch(((const char*)&from->entities[oldindex]) + 64, _MM_HINT_T0); + continue; + } } + // No more entities.. (end of packet entities) MSG_WriteBits(0, 16); + MSG_EndBitWriting(msg); return msg->cursize; } From 7073363c885c8f0c6e9967c09f41fcd733306ab1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=82=AF=E5=AE=9A=E9=BE=99?= <52111952+overl4y@users.noreply.github.com> Date: Tue, 6 Aug 2024 20:24:29 -0400 Subject: [PATCH 25/31] Small fix in sv_user.cpp (#1042) --- rehlds/engine/sv_user.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rehlds/engine/sv_user.cpp b/rehlds/engine/sv_user.cpp index 34ec651..584ca4f 100644 --- a/rehlds/engine/sv_user.cpp +++ b/rehlds/engine/sv_user.cpp @@ -884,7 +884,11 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed) pmove->spectator = 0; pmove->waterjumptime = sv_player->v.teleport_time; +#ifdef REHLDS_FIXES + Q_memcpy(&pmove->cmd, ucmd, sizeof(pmove->cmd)); +#else Q_memcpy(&pmove->cmd, &cmd, sizeof(pmove->cmd)); +#endif pmove->dead = sv_player->v.health <= 0.0; pmove->movetype = sv_player->v.movetype; From b29740c19e8cfe7a1ee0605cadccd05b35e8678e Mon Sep 17 00:00:00 2001 From: s1lentq Date: Wed, 25 Sep 2024 02:02:44 +0700 Subject: [PATCH 26/31] CalcSurfaceExtents: MAX_SURFACE_TEXTURE_SIZE limit increased from 256 to 512 --- rehlds/engine/model.cpp | 2 +- rehlds/public/rehlds/model.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/rehlds/engine/model.cpp b/rehlds/engine/model.cpp index 12bd90f..162e4c5 100644 --- a/rehlds/engine/model.cpp +++ b/rehlds/engine/model.cpp @@ -891,7 +891,7 @@ 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] > 256) + if (!(tex->flags & TEX_SPECIAL) && s->extents[i] > MAX_SURFACE_TEXTURE_SIZE) Sys_Error("%s: Bad surface extents", __func__); } } diff --git a/rehlds/public/rehlds/model.h b/rehlds/public/rehlds/model.h index bcb952a..d0078bb 100644 --- a/rehlds/public/rehlds/model.h +++ b/rehlds/public/rehlds/model.h @@ -144,6 +144,13 @@ struct msurface_s // surface generation data struct surfcache_s *cachespots[MIPLEVELS]; + // Maximum s/t texture size on the surface +#if defined(GLQUAKE) || defined(SWDS) + #define MAX_SURFACE_TEXTURE_SIZE 512 +#else + #define MAX_SURFACE_TEXTURE_SIZE 256 // Software rendering is limited to 256 +#endif + short texturemins[2]; // smallest s/t position on the surface. short extents[2]; // ?? s/t texture size, 1..256 for all non-sky surfaces From fe184a82e0a361b9e8bd1625ca93fac948467be1 Mon Sep 17 00:00:00 2001 From: s1lentq Date: Thu, 24 Oct 2024 02:28:47 +0700 Subject: [PATCH 27/31] SV_ProcessFile: Ignore customization file uploads if upload is disabled --- rehlds/engine/sv_main.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index b95c1b1..211a319 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -3783,6 +3783,12 @@ void SV_ProcessFile(client_t *cl, char *filename) return; } + if (!sv_allow_upload.value) + { + Con_NetPrintf("Ignoring incoming customization file upload of %s from %s\n", filename, NET_AdrToString(cl->netchan.remote_address)); + return; + } + COM_HexConvert(filename + 4, 32, md5); resource = cl->resourcesneeded.pNext; bFound = FALSE; From 5a1f66a6b4d09faa41d82b73ee9e0b86bf51208c Mon Sep 17 00:00:00 2001 From: Sergey Shorokhov Date: Tue, 29 Oct 2024 09:20:39 +0300 Subject: [PATCH 28/31] CI Workflow Improvements and Fixes (#1056) chore(ci): standardize and update CI workflows for compatibility and consistency - Updated action versions (`checkout@v4`, `upload-artifact@v4`, `download-artifact@v4`) for enhanced compatibility and security - Removed unsecure Node.js version allowance to align with *-latest OS requirements - Downgraded MSBuild to version 16 to resolve CI compatibility issues - Migrated test demos to `rehldsorg/testdemos:latest` container with a streamlined testing approach via matrix strategy and `runTest.sh` - Standardized Linux dependency installation for consistent build environment across jobs - Unified CI workflow structure with `rehlds` and `regamedll` repositories for better consistency - Optimized artifact deployment steps and removed unnecessary cleanup operations Co-authored-by: s1lentq --- .github/workflows/build.yml | 123 ++++++++++++------------------------ 1 file changed, 42 insertions(+), 81 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ab038a1..d6297a4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,9 +11,6 @@ on: release: types: [published] -env: - ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true - jobs: windows: name: 'Windows' @@ -28,12 +25,12 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Setup MSBuild - uses: microsoft/setup-msbuild@v1.1.3 + uses: microsoft/setup-msbuild@v2 - name: Build and Run unittests run: | @@ -73,7 +70,7 @@ jobs: move msvc\${{ env.buildRelease }}\director.pdb publish\debug\director.pdb - name: Deploy artifacts - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4 with: name: win32 path: publish/* @@ -81,90 +78,66 @@ jobs: testdemos: name: 'Test demos' runs-on: ubuntu-latest - container: s1lentq/testdemos:latest + container: rehldsorg/testdemos:latest needs: [windows] - - env: - WINEDEBUG: -all - WINEDLLOVERRIDES: mshtml= - defaults: run: shell: bash - working-directory: ../../../opt/HLDS + working-directory: /opt/HLDS + + strategy: + fail-fast: false + matrix: + test: [ + { file: 'cstrike-muliplayer-1', desc: 'CS: Multiplayer' }, + { file: 'rehlds-phys-single1', desc: 'Half-Life: Physics singleplayer' }, + { file: 'crossfire-1-multiplayer-1', desc: 'Half-Life: Multiplayer on crossfire map' }, + { file: 'shooting-hl-1', desc: 'Half-Life: Shooting with several weapons' }, + ] steps: - name: Deploying windows artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: win32 - - name: Play demos + - name: Setup dependencies run: | chown root ~ rsync -a deps/rehlds/* . mv $GITHUB_WORKSPACE/tests/swds.dll . - descs=( - "CS: Multiplayer" - "Half-Life: Physics singleplayer" - "Half-Life: Multiplayer on crossfire map" - "Half-Life: Shooting with several weapons" - ) - - demos=( - "cstrike-muliplayer-1" - "rehlds-phys-single1" - "crossfire-1-multiplayer-1" - "shooting-hl-1" - ) - - retVal=0 - for i in "${!demos[@]}"; do - params=$(cat "testdemos/${demos[i]}.params") - - echo -e "\e[1m[$((i + 1))/${#demos[@]}] \e[1;36m${descs[i]} testing...\e[0m" - echo -e " - \e[0;33mParameters $params\e[0m" - - wine hlds.exe --rehlds-enable-all-hooks --rehlds-test-play "testdemos/${demos[i]}.bin" $params &> result.log || retVal=$? - - if [ $retVal -ne 777 ] && [ $retVal -ne 9 ]; then - # Print with catchy messages - while read line; do - echo -e " \e[0;33m$line" - done <<< $(cat result.log | sed '0,/demo failed/I!d;/wine:/d;/./,$!d') - - echo " 🔸 🔸 🔸 🔸 🔸 🔸 🔸 🔸 🔸 🔸" - while read line; do - echo -e " \e[1;31m$line"; - done < rehlds_demo_error.txt - echo -e " \e[30;41mExit code: $retVal\e[0m" - echo -e "\e[1m[$((i + 1))/${#demos[@]}] \e[1;36m${descs[i]} testing...\e[1;31m Failed ❌" - exit 6 # Test demo failed - else - # Print result HLDS console - while read line; do - echo -e " \e[0;33m$line" - done <<< $(cat result.log | sed '/wine:/d;/./,$!d') - echo -e " \e[30;43mExit code: $retVal\e[0m" - echo -e "\e[1m[$((i + 1))/${#demos[@]}] \e[1;36m${descs[i]} testing...\e[1;32m Succeed ✔" - fi - done + - name: Play test + env: + demo: ${{ matrix.test.file }} + desc: ${{ matrix.test.desc }} + run: ./runTest.sh linux: name: 'Linux' runs-on: ubuntu-latest - container: s1lentq/linux86buildtools:latest + container: debian:11-slim steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 + - name: Install dependencies + run: | + dpkg --add-architecture i386 + apt-get update + apt-get install -y \ + gcc-multilib g++-multilib \ + build-essential \ + libc6-dev libc6-dev-i386 \ + git cmake rsync \ + g++ gcc + - name: Build and Run unittests run: | - rm -rf build && CC=icc CXX=icpc cmake -DCMAKE_BUILD_TYPE=Unittests -B build && cmake --build build -j8 + rm -rf build && cmake -DCMAKE_BUILD_TYPE=Unittests -B build && cmake --build build -j8 retVal=0 export LD_LIBRARY_PATH="rehlds/lib/linux32:$LD_LIBRARY_PATH" ./build/rehlds/engine_i486 2> /dev/null > result.log || retVal=$? @@ -186,9 +159,9 @@ jobs: fi shell: bash - - name: Build using Intel C++ Compiler + - name: Build using GCC Compiler run: | - rm -rf build && CC=icc CXX=icpc cmake -B build && cmake --build build -j8 + rm -rf build && cmake -B build && cmake --build build -j8 - name: Prepare HLSDK run: | @@ -231,18 +204,12 @@ jobs: shell: bash - name: Deploy artifacts - uses: actions/upload-artifact@v3.1.1 + uses: actions/upload-artifact@v4 id: upload-job with: name: linux32 path: publish/* - - name: Cleanup temporary artifacts - if: success() && steps.upload-job.outcome == 'success' - run: | - rm -rf hlsdk - rm -f appversion.h - publish: name: 'Publish' runs-on: ubuntu-latest @@ -250,12 +217,12 @@ jobs: steps: - name: Deploying linux artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: linux32 - name: Deploying windows artifacts - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: win32 @@ -284,7 +251,7 @@ jobs: 7z a -t7z -m0=lzma2 -mx=9 -mfb=64 -aoa rehlds-dbg-${{ env.APP_VERSION }}.7z debug/ - name: Publish artifacts - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 id: publish-job if: | startsWith(github.ref, 'refs/tags/') && @@ -295,9 +262,3 @@ jobs: *.7z env: GITHUB_TOKEN: ${{ secrets.API_TOKEN }} - - - name: Cleanup temporary artifacts - if: success() && steps.publish-job.outcome == 'success' - run: | - rm -rf bin debug hlsdk - rm -f *.7z *.zip appversion.h From df704585aa31d8f24b40ae60b26378402d50ac7f Mon Sep 17 00:00:00 2001 From: STAM Date: Sun, 3 Nov 2024 14:46:02 +0300 Subject: [PATCH 29/31] meta improvemetns * added changelog --- .github/workflows/sync-with-gitlab.yml | 31 ++++ CHANGELOG.md | 238 +++++++++++++++++++++++++ LICENSE => LICENSE.md | 0 3 files changed, 269 insertions(+) create mode 100644 .github/workflows/sync-with-gitlab.yml create mode 100644 CHANGELOG.md rename LICENSE => LICENSE.md (100%) diff --git a/.github/workflows/sync-with-gitlab.yml b/.github/workflows/sync-with-gitlab.yml new file mode 100644 index 0000000..3314aff --- /dev/null +++ b/.github/workflows/sync-with-gitlab.yml @@ -0,0 +1,31 @@ +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 diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..280cf8c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,238 @@ +# [reHLDS](https://github.com/rehlds/rehlds) Changelog + +**ReHLDS** is a result of reverse engineering of original `HLDS` (build `6152`/`6153`) using `DWARF` debug info embedded into linux version of `HLDS`, `engine_i486.so`. + +Along with reverse engineering, a lot of defects and (potential) bugs were found and fixed. + +--- + +## [`3.13.0.788`](https://github.com/rehlds/rehlds/releases/tag/3.13.0.788) - 2023-07-12 + +### Added +- Added `SV_AllowPhysent` hook by @justgo97 in [(#951)](ttps://github.com/dreamstalker/rehlds/pull/951) +- `GetBonePosition`: Added bone index bounds check +- `GetAttachment`: Added attachment index bounds check +- Added more checks for possible `numleaf` overflow + +### Fixed +- `SV_BuildSoundMsg`: fix '`\n`' in args check + +### Changed +- Revert "change destinition folder for linux build" by @wopox1337 in [(#977)](https://github.com/dreamstalker/rehlds/pull/977) +- Allowed the clients to connect on the server of different game: Client should be use `setinfo _gd ` +- Increased limit leafs `MAX_MAP_LEAFS` up to `32767` + +## New Contributors +- @justgo97 made their first contribution in [(#951)](https://github.com/dreamstalker/rehlds/pull/951) + +**Full Changelog**: [3.12.0.780...3.13.0.788](https://github.com/rehlds/rehlds/compare/3.12.0.780...3.13.0.788) + +## [`3.12.0.780`](https://github.com/rehlds/rehlds/releases/tag/3.12.0.780) - 2022-09-19 + +### Fixed +- `Netchan_CreateFileFragments`: Fixed a very old and rare bug with dlfile while downloading direct from server, when content of resource size is less than header size first fragment. + +### Changed +- `API`: Implement `*_Precache_*`, `ClientPrintf`, `CheckUserInfo` and `AddResource` hooks by @ShadowsAdi in [(#903)](https://github.com/dreamstalker/rehlds/pull/903) + +## New Contributors +* @ShadowsAdi made their first contribution in [(#903)](https://github.com/dreamstalker/rehlds/pull/903) + +**Full Changelog**: [3.11.0.779...3.12.0.780](https://github.com/rehlds/rehlds/compare/3.11.0.779...3.12.0.780) + +## [`3.11.0.779`](https://github.com/rehlds/rehlds/releases/tag/3.11.0.779) - 2022-08-24 + +### Fixed +- `StripUnprintableWorker` did not count the null terminator [e9045e3](https://github.com/dreamstalker/rehlds/commit/e9045e3) +- Very old and rare bug in function `Netchan_CreateFileFragments` with dlfile hang while downloading direct from server, when content of resource size is less than header size first fragment [d76b06d](https://github.com/dreamstalker/rehlds/commit/d76b06d) + +**Full Changelog**: [3.11.0.777...3.11.0.779](https://github.com/rehlds/rehlds/compare/3.11.0.777...3.11.0.779) + +## [`3.11.0.777`](https://github.com/rehlds/rehlds/releases/tag/3.11.0.777) - 2022-06-20 + +### Fixed +* Fixed `null or empty` input string in `COM_LoadFile` (`FS_Open` with input empty string `""` will succeed on some POSIX systems) + - Resolved [(#919)](https://github.com/dreamstalker/rehlds/issues/919) + +**Full Changelog**: [3.11.0.776...3.11.0.777](https://github.com/rehlds/rehlds/compare/3.11.0.776...3.11.0.777) + +## [`3.11.0.776`](https://github.com/rehlds/rehlds/releases/tag/3.11.0.776) - 2022-04-20 + +### Fixed +* Fixed typo `ZONE_DYNAMIC_SIZE` + +**Full Changelog**: [3.11.0.767...3.11.0.776](https://github.com/rehlds/rehlds/compare/3.11.0.767...3.11.0.776) + +## [`3.11.0.767`](https://github.com/rehlds/rehlds/releases/tag/3.11.0.767) - 2021-10-25 + +### Added +* Implement `SV_EmitPings()` hook by @francoromaniello in [(#858)](https://github.com/rehlds/rehlds/pull/858) +* Implement `Con_Printf()` hook by @francoromaniello in [(#861)](https://github.com/rehlds/rehlds/pull/861) + +### Changed +* `API`: Add hooks `ED_Alloc()` & `ED_Free()`. by @StevenKal in [(#867)](https://github.com/rehlds/rehlds/pull/867) +* `SV_HullForEntity`: better log in `Sys_Error` by @wopox1337 in [(#843)](https://github.com/rehlds/rehlds/pull/843) +* Update on grammar/spelling by @mlgpero in [(#865)](https://github.com/rehlds/rehlds/pull/865) + +## New Contributors +* @StevenKal made their first contribution in [(#867)](https://github.com/rehlds/rehlds/pull/867) +* @francoromaniello made their first contribution in [(#858)](https://github.com/rehlds/rehlds/pull/858) +* @Urufusan made their first contribution in [(#865)](https://github.com/rehlds/rehlds/pull/865) + +**Full Changelog**: [v3.10.0.761...3.11.0.767](https://github.com/rehlds/rehlds/compare/v3.10.0.761...3.11.0.767) + +## [`3.10.0.760`](https://github.com/rehlds/rehlds/releases/tag/3.10.0.760) - 2021-06-23 + +### Changed +- Changed the destination folder for `Linux build` [(#842)](https://github.com/rehlds/rehlds/pull/842). +- Temporary removed `Windows build`. :warning: + +**Full Changelog**: [3.10.0.759...3.10.0.760](https://github.com/rehlds/rehlds/compare/3.10.0.759...3.10.0.760) + + +## [`3.10.0.761`](https://github.com/rehlds/rehlds/releases/tag/v3.10.0.761) - 2021-06-23 + +### Changed +- Reset `m_bSentNewResponse` to allow new connection when the client goes through the full stage of connection (`cl:connect` -> `sv:S2C_CONNECTION` -> `cl:new` -> `SV_New_f`) + - Related [3a9bfb9](https://github.com/rehlds/rehlds/commit/3a9bfb9) + +**Full Changelog**: [3.10.0.760...v3.10.0.761](https://github.com/rehlds/rehlds/compare/3.10.0.760...v3.10.0.761) + +## [`3.10.0.760`](https://github.com/rehlds/rehlds/releases/tag/3.10.0.760) - 2021-06-23 + +### Changed +- Changed the destination folder for `Linux build` [(#842)](https://github.com/rehlds/rehlds/pull/842). +- Temporary removed `Windows build`. :warning: + +**Full Changelog**: [3.10.0.759...3.10.0.760](https://github.com/rehlds/rehlds/compare/3.10.0.759...3.10.0.760) + +## [`3.10.0.759`](https://github.com/rehlds/rehlds/releases/tag/3.10.0.759) - 2021-06-22 + +### Fixed +- Fixed volume checking in emit sound [(#341)](https://github.com/rehlds/rehlds/pull/341) +- `static_map.h`: fix lowercase convert [(#806)](https://github.com/rehlds/rehlds/pull/806) +- `SV_New_f`: Deny new connection twice at a time if user messages are received; + - `SV_ReadClientMessage`: Fixed empty names on bad read. + +### Changed +- `sv_user.cpp`: Small code refactoring [(#810)](https://github.com/rehlds/rehlds/pull/810) +- `ReHLDS API`: Enhanced IGameClient/IRehldsServerData/IRehldsServerStatic interfaces +- `sv_main.cpp`: SV_New_f() uses Q_snprintf() unsafe format. #807 [()](https://github.com/rehlds/rehlds/pull/807) + +**Full Changelog**: [3.9.0.752...3.10.0.759](https://github.com/rehlds/rehlds/compare/3.9.0.752...3.10.0.759) + +## [`3.9.0.752`](https://github.com/rehlds/rehlds/releases/tag/3.9.0.752) - 2021-06-14 + +### Added +- `ReHLDS API`: Add GetEntityInit hook [(#832)](https://github.com/rehlds/rehlds/pull/832) +- Implement CVar `sv_usercmd_custom_random_seed` [(#837)](https://github.com/rehlds/rehlds/pull/837) + +### Fixed +- `HLTV`: Fix crash in ProcessStringCmd [(#838)](https://github.com/rehlds/rehlds/pull/838) + +### Changed +- `SV_ParseMove`, `SV_ParseConsistencyResponse`: check length + +**Full Changelog**: [3.8.0.739...3.9.0.752](https://github.com/rehlds/rehlds/compare/3.8.0.739...3.9.0.752) + +## [`3.8.0.739`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.739) - 2021-04-21 + +### Added +* Added libraries libm/librt built on `GLIBC` `2.11.1` [(#827)](https://github.com/rehlds/rehlds/pull/827) + +### Fixed +* `QuaternionSlerp`: Fixed wrong values [(#763)](https://github.com/rehlds/rehlds/pull/763) + +### Changed +* Updated `Intel C++ Compiler` version `17.0` up to `19.0` + +**Full Changelog**: [3.8.0.723...3.8.0.739](https://github.com/rehlds/rehlds/compare/3.8.0.723...3.8.0.739) + +## [`3.8.0.723`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.723) - 2021-03-23 + +### Fixed +* `CalcSurfaceExtents:` Fixed a fatal error on some maps due loss of floating point +* `HLTV:` ExecuteString Fix parser [(#821)](https://github.com/rehlds/rehlds/pull/821) + +### Changed +* `HLTV:` Downgrade GLIBC version + +**Full Changelog**: [3.8.0.715...3.8.0.723](https://github.com/rehlds/rehlds/compare/3.8.0.715...3.8.0.723) + +## [`3.8.0.715`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.715) - 2021-03-18 + +### Fixed +* Make sure the `timer` is high precision [(#799)](https://github.com/rehlds/rehlds/pull/799) + +**Full Changelog**: [3.8.0.711...3.8.0.715](https://github.com/rehlds/rehlds/compare/3.8.0.711...3.8.0.715) + +## [`3.8.0.711`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.711) - 2021-02-06 + +### Added +* `HLTV`: Added new chatdelay command [(#777)](https://github.com/rehlds/rehlds/pull/777) +* `HLTV`: prevent clients from setting userinfo * keys with setinfo command [(#792)](https://github.com/rehlds/rehlds/pull/792) +* `Cbuf_Execute`: Add checks commented out for multi-lines [(#719)](https://github.com/rehlds/rehlds/pull/719) + +### Fixed +* Fixed local-buffer overrun, may undefined behavior with hitboxes blending or crash (reverse-engineering mistake) [722e19d](https://github.com/rehlds/rehlds/commit/722e19d) +* Fixed dos attack on connection challenges system [(#802)](https://github.com/rehlds/rehlds/pull/802) +* Fixed crash `COM_ListMaps` [(#791)](https://github.com/rehlds/rehlds/pull/791) + +**Full Changelog**: [3.8.0.702...3.8.0.711](https://github.com/rehlds/rehlds/compare/3.8.0.702...3.8.0.711) + +## [`3.8.0.702`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.702) - 2020-11-09 + +### Fixed +* Fixed crash `MSG_ReadFloat` + +### Changed +* **ReHLDS API:** Implemented `SV_ShouldSendConsistencyList` +* **ReHLDS API:** Bump minor + +**Full Changelog**: [3.7.0.698...3.8.0.702](https://github.com/rehlds/rehlds/compare/3.7.0.698...3.8.0.702) + +## [`3.7.0.698`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.698) - 2020-08-19 + +### Added +* Graceful shutdown on sigterm [(#776)](https://github.com/rehlds/rehlds/pull/776) + - Add players kick on `SIGINT \ SIGTERM` + - Add SIGINT & SIGTERM handling linux console + +### Changed +* Changed shutdown method + +**Full Changelog**: [3.7.0.697...3.7.0.698](https://github.com/rehlds/rehlds/compare/3.7.0.697...3.7.0.698) + +## [`3.7.0.697`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.697) - 2020-08-10 + +### Fixed +* **SV_ProcessFile:** Wrap `Con_Printf` in `Con_NetPrintf` to avoid spam in HLDS console + +**Full Changelog**: [3.7.0.696...3.7.0.697](https://github.com/rehlds/rehlds/compare/3.7.0.696...3.7.0.697) + +## [`3.7.0.696`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.696) - 2020-05-18 + +### Added +* Implement `svc_exec` support in the engine and HLTV [(#737)](https://github.com/rehlds/rehlds/pull/737) + - Added `svc_exec` to the list of svc commands in engine + - Added `svc_exec` support to HLTV code + - Made the engine code forward-compatible with future svc_* additions + - Added reserved svc_* slots in the enumerations + +**Full Changelog**: [3.7.0.695...3.7.0.696](https://github.com/rehlds/rehlds/compare/3.7.0.695...3.7.0.696) + +## [`3.7.0.695`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.695) - 2020-04-06 + +### Fixed +* Vulnerability fix WAD part 2 + - Client-side: Fixed a potential vulnerability from bogus `tempdecal.wad` + +**Full Changelog**: [3.7.0.694...3.7.0.695](https://github.com/rehlds/rehlds/compare/3.7.0.694...3.7.0.695) + +## [`3.7.0.694`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.694) - 2020-03-20 + +### Fixed +* Vulnerability fix WAD part 1 + - Server-side: Fixed a potential vulnerability from bogus `tempdecal.wad` + +**Full Changelog**: [3.7.0.694](https://github.com/rehlds/rehlds/commits/3.7.0.694) diff --git a/LICENSE b/LICENSE.md similarity index 100% rename from LICENSE rename to LICENSE.md From c17dacd152e38d930fd8a4fefe9ca2fbaa406b2d Mon Sep 17 00:00:00 2001 From: STAM Date: Sat, 16 Nov 2024 00:56:21 +0300 Subject: [PATCH 30/31] reHLDS -> ReHLDS --- CHANGELOG.md | 162 +++++++++++++++++++++--------------------- LICENSE.md => LICENSE | 0 README.md | 14 ++-- 3 files changed, 88 insertions(+), 88 deletions(-) rename LICENSE.md => LICENSE (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 280cf8c..98091b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# [reHLDS](https://github.com/rehlds/rehlds) Changelog +# [ReHLDS](https://github.com/ReHLDS/ReHLDS) Changelog **ReHLDS** is a result of reverse engineering of original `HLDS` (build `6152`/`6153`) using `DWARF` debug info embedded into linux version of `HLDS`, `engine_i486.so`. @@ -6,10 +6,10 @@ Along with reverse engineering, a lot of defects and (potential) bugs were found --- -## [`3.13.0.788`](https://github.com/rehlds/rehlds/releases/tag/3.13.0.788) - 2023-07-12 +## [`3.13.0.788`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.13.0.788) - 2023-07-12 ### Added -- Added `SV_AllowPhysent` hook by @justgo97 in [(#951)](ttps://github.com/dreamstalker/rehlds/pull/951) +- Added `SV_AllowPhysent` hook by @justgo97 in [(#951)](ttps://github.com/dreamstalker/ReHLDS/pull/951) - `GetBonePosition`: Added bone index bounds check - `GetAttachment`: Added attachment index bounds check - Added more checks for possible `numleaf` overflow @@ -18,169 +18,169 @@ Along with reverse engineering, a lot of defects and (potential) bugs were found - `SV_BuildSoundMsg`: fix '`\n`' in args check ### Changed -- Revert "change destinition folder for linux build" by @wopox1337 in [(#977)](https://github.com/dreamstalker/rehlds/pull/977) +- Revert "change destinition folder for linux build" by @wopox1337 in [(#977)](https://github.com/dreamstalker/ReHLDS/pull/977) - Allowed the clients to connect on the server of different game: Client should be use `setinfo _gd ` - Increased limit leafs `MAX_MAP_LEAFS` up to `32767` ## New Contributors -- @justgo97 made their first contribution in [(#951)](https://github.com/dreamstalker/rehlds/pull/951) +- @justgo97 made their first contribution in [(#951)](https://github.com/dreamstalker/ReHLDS/pull/951) -**Full Changelog**: [3.12.0.780...3.13.0.788](https://github.com/rehlds/rehlds/compare/3.12.0.780...3.13.0.788) +**Full Changelog**: [3.12.0.780...3.13.0.788](https://github.com/ReHLDS/ReHLDS/compare/3.12.0.780...3.13.0.788) -## [`3.12.0.780`](https://github.com/rehlds/rehlds/releases/tag/3.12.0.780) - 2022-09-19 +## [`3.12.0.780`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.12.0.780) - 2022-09-19 ### Fixed - `Netchan_CreateFileFragments`: Fixed a very old and rare bug with dlfile while downloading direct from server, when content of resource size is less than header size first fragment. ### Changed -- `API`: Implement `*_Precache_*`, `ClientPrintf`, `CheckUserInfo` and `AddResource` hooks by @ShadowsAdi in [(#903)](https://github.com/dreamstalker/rehlds/pull/903) +- `API`: Implement `*_Precache_*`, `ClientPrintf`, `CheckUserInfo` and `AddResource` hooks by @ShadowsAdi in [(#903)](https://github.com/dreamstalker/ReHLDS/pull/903) ## New Contributors -* @ShadowsAdi made their first contribution in [(#903)](https://github.com/dreamstalker/rehlds/pull/903) +* @ShadowsAdi made their first contribution in [(#903)](https://github.com/dreamstalker/ReHLDS/pull/903) -**Full Changelog**: [3.11.0.779...3.12.0.780](https://github.com/rehlds/rehlds/compare/3.11.0.779...3.12.0.780) +**Full Changelog**: [3.11.0.779...3.12.0.780](https://github.com/ReHLDS/ReHLDS/compare/3.11.0.779...3.12.0.780) -## [`3.11.0.779`](https://github.com/rehlds/rehlds/releases/tag/3.11.0.779) - 2022-08-24 +## [`3.11.0.779`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.11.0.779) - 2022-08-24 ### Fixed -- `StripUnprintableWorker` did not count the null terminator [e9045e3](https://github.com/dreamstalker/rehlds/commit/e9045e3) -- Very old and rare bug in function `Netchan_CreateFileFragments` with dlfile hang while downloading direct from server, when content of resource size is less than header size first fragment [d76b06d](https://github.com/dreamstalker/rehlds/commit/d76b06d) +- `StripUnprintableWorker` did not count the null terminator [e9045e3](https://github.com/dreamstalker/ReHLDS/commit/e9045e3) +- Very old and rare bug in function `Netchan_CreateFileFragments` with dlfile hang while downloading direct from server, when content of resource size is less than header size first fragment [d76b06d](https://github.com/dreamstalker/ReHLDS/commit/d76b06d) -**Full Changelog**: [3.11.0.777...3.11.0.779](https://github.com/rehlds/rehlds/compare/3.11.0.777...3.11.0.779) +**Full Changelog**: [3.11.0.777...3.11.0.779](https://github.com/ReHLDS/ReHLDS/compare/3.11.0.777...3.11.0.779) -## [`3.11.0.777`](https://github.com/rehlds/rehlds/releases/tag/3.11.0.777) - 2022-06-20 +## [`3.11.0.777`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.11.0.777) - 2022-06-20 ### Fixed * Fixed `null or empty` input string in `COM_LoadFile` (`FS_Open` with input empty string `""` will succeed on some POSIX systems) - - Resolved [(#919)](https://github.com/dreamstalker/rehlds/issues/919) + - Resolved [(#919)](https://github.com/dreamstalker/ReHLDS/issues/919) -**Full Changelog**: [3.11.0.776...3.11.0.777](https://github.com/rehlds/rehlds/compare/3.11.0.776...3.11.0.777) +**Full Changelog**: [3.11.0.776...3.11.0.777](https://github.com/ReHLDS/ReHLDS/compare/3.11.0.776...3.11.0.777) -## [`3.11.0.776`](https://github.com/rehlds/rehlds/releases/tag/3.11.0.776) - 2022-04-20 +## [`3.11.0.776`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.11.0.776) - 2022-04-20 ### Fixed * Fixed typo `ZONE_DYNAMIC_SIZE` -**Full Changelog**: [3.11.0.767...3.11.0.776](https://github.com/rehlds/rehlds/compare/3.11.0.767...3.11.0.776) +**Full Changelog**: [3.11.0.767...3.11.0.776](https://github.com/ReHLDS/ReHLDS/compare/3.11.0.767...3.11.0.776) -## [`3.11.0.767`](https://github.com/rehlds/rehlds/releases/tag/3.11.0.767) - 2021-10-25 +## [`3.11.0.767`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.11.0.767) - 2021-10-25 ### Added -* Implement `SV_EmitPings()` hook by @francoromaniello in [(#858)](https://github.com/rehlds/rehlds/pull/858) -* Implement `Con_Printf()` hook by @francoromaniello in [(#861)](https://github.com/rehlds/rehlds/pull/861) +* Implement `SV_EmitPings()` hook by @francoromaniello in [(#858)](https://github.com/ReHLDS/ReHLDS/pull/858) +* Implement `Con_Printf()` hook by @francoromaniello in [(#861)](https://github.com/ReHLDS/ReHLDS/pull/861) ### Changed -* `API`: Add hooks `ED_Alloc()` & `ED_Free()`. by @StevenKal in [(#867)](https://github.com/rehlds/rehlds/pull/867) -* `SV_HullForEntity`: better log in `Sys_Error` by @wopox1337 in [(#843)](https://github.com/rehlds/rehlds/pull/843) -* Update on grammar/spelling by @mlgpero in [(#865)](https://github.com/rehlds/rehlds/pull/865) +* `API`: Add hooks `ED_Alloc()` & `ED_Free()`. by @StevenKal in [(#867)](https://github.com/ReHLDS/ReHLDS/pull/867) +* `SV_HullForEntity`: better log in `Sys_Error` by @wopox1337 in [(#843)](https://github.com/ReHLDS/ReHLDS/pull/843) +* Update on grammar/spelling by @mlgpero in [(#865)](https://github.com/ReHLDS/ReHLDS/pull/865) ## New Contributors -* @StevenKal made their first contribution in [(#867)](https://github.com/rehlds/rehlds/pull/867) -* @francoromaniello made their first contribution in [(#858)](https://github.com/rehlds/rehlds/pull/858) -* @Urufusan made their first contribution in [(#865)](https://github.com/rehlds/rehlds/pull/865) +* @StevenKal made their first contribution in [(#867)](https://github.com/ReHLDS/ReHLDS/pull/867) +* @francoromaniello made their first contribution in [(#858)](https://github.com/ReHLDS/ReHLDS/pull/858) +* @Urufusan made their first contribution in [(#865)](https://github.com/ReHLDS/ReHLDS/pull/865) -**Full Changelog**: [v3.10.0.761...3.11.0.767](https://github.com/rehlds/rehlds/compare/v3.10.0.761...3.11.0.767) +**Full Changelog**: [v3.10.0.761...3.11.0.767](https://github.com/ReHLDS/ReHLDS/compare/v3.10.0.761...3.11.0.767) -## [`3.10.0.760`](https://github.com/rehlds/rehlds/releases/tag/3.10.0.760) - 2021-06-23 +## [`3.10.0.760`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.10.0.760) - 2021-06-23 ### Changed -- Changed the destination folder for `Linux build` [(#842)](https://github.com/rehlds/rehlds/pull/842). +- Changed the destination folder for `Linux build` [(#842)](https://github.com/ReHLDS/ReHLDS/pull/842). - Temporary removed `Windows build`. :warning: -**Full Changelog**: [3.10.0.759...3.10.0.760](https://github.com/rehlds/rehlds/compare/3.10.0.759...3.10.0.760) +**Full Changelog**: [3.10.0.759...3.10.0.760](https://github.com/ReHLDS/ReHLDS/compare/3.10.0.759...3.10.0.760) -## [`3.10.0.761`](https://github.com/rehlds/rehlds/releases/tag/v3.10.0.761) - 2021-06-23 +## [`3.10.0.761`](https://github.com/ReHLDS/ReHLDS/releases/tag/v3.10.0.761) - 2021-06-23 ### Changed - Reset `m_bSentNewResponse` to allow new connection when the client goes through the full stage of connection (`cl:connect` -> `sv:S2C_CONNECTION` -> `cl:new` -> `SV_New_f`) - - Related [3a9bfb9](https://github.com/rehlds/rehlds/commit/3a9bfb9) + - Related [3a9bfb9](https://github.com/ReHLDS/ReHLDS/commit/3a9bfb9) -**Full Changelog**: [3.10.0.760...v3.10.0.761](https://github.com/rehlds/rehlds/compare/3.10.0.760...v3.10.0.761) +**Full Changelog**: [3.10.0.760...v3.10.0.761](https://github.com/ReHLDS/ReHLDS/compare/3.10.0.760...v3.10.0.761) -## [`3.10.0.760`](https://github.com/rehlds/rehlds/releases/tag/3.10.0.760) - 2021-06-23 +## [`3.10.0.760`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.10.0.760) - 2021-06-23 ### Changed -- Changed the destination folder for `Linux build` [(#842)](https://github.com/rehlds/rehlds/pull/842). +- Changed the destination folder for `Linux build` [(#842)](https://github.com/ReHLDS/ReHLDS/pull/842). - Temporary removed `Windows build`. :warning: -**Full Changelog**: [3.10.0.759...3.10.0.760](https://github.com/rehlds/rehlds/compare/3.10.0.759...3.10.0.760) +**Full Changelog**: [3.10.0.759...3.10.0.760](https://github.com/ReHLDS/ReHLDS/compare/3.10.0.759...3.10.0.760) -## [`3.10.0.759`](https://github.com/rehlds/rehlds/releases/tag/3.10.0.759) - 2021-06-22 +## [`3.10.0.759`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.10.0.759) - 2021-06-22 ### Fixed -- Fixed volume checking in emit sound [(#341)](https://github.com/rehlds/rehlds/pull/341) -- `static_map.h`: fix lowercase convert [(#806)](https://github.com/rehlds/rehlds/pull/806) +- Fixed volume checking in emit sound [(#341)](https://github.com/ReHLDS/ReHLDS/pull/341) +- `static_map.h`: fix lowercase convert [(#806)](https://github.com/ReHLDS/ReHLDS/pull/806) - `SV_New_f`: Deny new connection twice at a time if user messages are received; - `SV_ReadClientMessage`: Fixed empty names on bad read. ### Changed -- `sv_user.cpp`: Small code refactoring [(#810)](https://github.com/rehlds/rehlds/pull/810) +- `sv_user.cpp`: Small code refactoring [(#810)](https://github.com/ReHLDS/ReHLDS/pull/810) - `ReHLDS API`: Enhanced IGameClient/IRehldsServerData/IRehldsServerStatic interfaces -- `sv_main.cpp`: SV_New_f() uses Q_snprintf() unsafe format. #807 [()](https://github.com/rehlds/rehlds/pull/807) +- `sv_main.cpp`: SV_New_f() uses Q_snprintf() unsafe format. #807 [()](https://github.com/ReHLDS/ReHLDS/pull/807) -**Full Changelog**: [3.9.0.752...3.10.0.759](https://github.com/rehlds/rehlds/compare/3.9.0.752...3.10.0.759) +**Full Changelog**: [3.9.0.752...3.10.0.759](https://github.com/ReHLDS/ReHLDS/compare/3.9.0.752...3.10.0.759) -## [`3.9.0.752`](https://github.com/rehlds/rehlds/releases/tag/3.9.0.752) - 2021-06-14 +## [`3.9.0.752`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.9.0.752) - 2021-06-14 ### Added -- `ReHLDS API`: Add GetEntityInit hook [(#832)](https://github.com/rehlds/rehlds/pull/832) -- Implement CVar `sv_usercmd_custom_random_seed` [(#837)](https://github.com/rehlds/rehlds/pull/837) +- `ReHLDS API`: Add GetEntityInit hook [(#832)](https://github.com/ReHLDS/ReHLDS/pull/832) +- Implement CVar `sv_usercmd_custom_random_seed` [(#837)](https://github.com/ReHLDS/ReHLDS/pull/837) ### Fixed -- `HLTV`: Fix crash in ProcessStringCmd [(#838)](https://github.com/rehlds/rehlds/pull/838) +- `HLTV`: Fix crash in ProcessStringCmd [(#838)](https://github.com/ReHLDS/ReHLDS/pull/838) ### Changed - `SV_ParseMove`, `SV_ParseConsistencyResponse`: check length -**Full Changelog**: [3.8.0.739...3.9.0.752](https://github.com/rehlds/rehlds/compare/3.8.0.739...3.9.0.752) +**Full Changelog**: [3.8.0.739...3.9.0.752](https://github.com/ReHLDS/ReHLDS/compare/3.8.0.739...3.9.0.752) -## [`3.8.0.739`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.739) - 2021-04-21 +## [`3.8.0.739`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.8.0.739) - 2021-04-21 ### Added -* Added libraries libm/librt built on `GLIBC` `2.11.1` [(#827)](https://github.com/rehlds/rehlds/pull/827) +* Added libraries libm/librt built on `GLIBC` `2.11.1` [(#827)](https://github.com/ReHLDS/ReHLDS/pull/827) ### Fixed -* `QuaternionSlerp`: Fixed wrong values [(#763)](https://github.com/rehlds/rehlds/pull/763) +* `QuaternionSlerp`: Fixed wrong values [(#763)](https://github.com/ReHLDS/ReHLDS/pull/763) ### Changed * Updated `Intel C++ Compiler` version `17.0` up to `19.0` -**Full Changelog**: [3.8.0.723...3.8.0.739](https://github.com/rehlds/rehlds/compare/3.8.0.723...3.8.0.739) +**Full Changelog**: [3.8.0.723...3.8.0.739](https://github.com/ReHLDS/ReHLDS/compare/3.8.0.723...3.8.0.739) -## [`3.8.0.723`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.723) - 2021-03-23 +## [`3.8.0.723`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.8.0.723) - 2021-03-23 ### Fixed * `CalcSurfaceExtents:` Fixed a fatal error on some maps due loss of floating point -* `HLTV:` ExecuteString Fix parser [(#821)](https://github.com/rehlds/rehlds/pull/821) +* `HLTV:` ExecuteString Fix parser [(#821)](https://github.com/ReHLDS/ReHLDS/pull/821) ### Changed * `HLTV:` Downgrade GLIBC version -**Full Changelog**: [3.8.0.715...3.8.0.723](https://github.com/rehlds/rehlds/compare/3.8.0.715...3.8.0.723) +**Full Changelog**: [3.8.0.715...3.8.0.723](https://github.com/ReHLDS/ReHLDS/compare/3.8.0.715...3.8.0.723) -## [`3.8.0.715`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.715) - 2021-03-18 +## [`3.8.0.715`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.8.0.715) - 2021-03-18 ### Fixed -* Make sure the `timer` is high precision [(#799)](https://github.com/rehlds/rehlds/pull/799) +* Make sure the `timer` is high precision [(#799)](https://github.com/ReHLDS/ReHLDS/pull/799) -**Full Changelog**: [3.8.0.711...3.8.0.715](https://github.com/rehlds/rehlds/compare/3.8.0.711...3.8.0.715) +**Full Changelog**: [3.8.0.711...3.8.0.715](https://github.com/ReHLDS/ReHLDS/compare/3.8.0.711...3.8.0.715) -## [`3.8.0.711`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.711) - 2021-02-06 +## [`3.8.0.711`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.8.0.711) - 2021-02-06 ### Added -* `HLTV`: Added new chatdelay command [(#777)](https://github.com/rehlds/rehlds/pull/777) -* `HLTV`: prevent clients from setting userinfo * keys with setinfo command [(#792)](https://github.com/rehlds/rehlds/pull/792) -* `Cbuf_Execute`: Add checks commented out for multi-lines [(#719)](https://github.com/rehlds/rehlds/pull/719) +* `HLTV`: Added new chatdelay command [(#777)](https://github.com/ReHLDS/ReHLDS/pull/777) +* `HLTV`: prevent clients from setting userinfo * keys with setinfo command [(#792)](https://github.com/ReHLDS/ReHLDS/pull/792) +* `Cbuf_Execute`: Add checks commented out for multi-lines [(#719)](https://github.com/ReHLDS/ReHLDS/pull/719) ### Fixed -* Fixed local-buffer overrun, may undefined behavior with hitboxes blending or crash (reverse-engineering mistake) [722e19d](https://github.com/rehlds/rehlds/commit/722e19d) -* Fixed dos attack on connection challenges system [(#802)](https://github.com/rehlds/rehlds/pull/802) -* Fixed crash `COM_ListMaps` [(#791)](https://github.com/rehlds/rehlds/pull/791) +* Fixed local-buffer overrun, may undefined behavior with hitboxes blending or crash (reverse-engineering mistake) [722e19d](https://github.com/ReHLDS/ReHLDS/commit/722e19d) +* Fixed dos attack on connection challenges system [(#802)](https://github.com/ReHLDS/ReHLDS/pull/802) +* Fixed crash `COM_ListMaps` [(#791)](https://github.com/ReHLDS/ReHLDS/pull/791) -**Full Changelog**: [3.8.0.702...3.8.0.711](https://github.com/rehlds/rehlds/compare/3.8.0.702...3.8.0.711) +**Full Changelog**: [3.8.0.702...3.8.0.711](https://github.com/ReHLDS/ReHLDS/compare/3.8.0.702...3.8.0.711) -## [`3.8.0.702`](https://github.com/rehlds/rehlds/releases/tag/3.8.0.702) - 2020-11-09 +## [`3.8.0.702`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.8.0.702) - 2020-11-09 ### Fixed * Fixed crash `MSG_ReadFloat` @@ -189,50 +189,50 @@ Along with reverse engineering, a lot of defects and (potential) bugs were found * **ReHLDS API:** Implemented `SV_ShouldSendConsistencyList` * **ReHLDS API:** Bump minor -**Full Changelog**: [3.7.0.698...3.8.0.702](https://github.com/rehlds/rehlds/compare/3.7.0.698...3.8.0.702) +**Full Changelog**: [3.7.0.698...3.8.0.702](https://github.com/ReHLDS/ReHLDS/compare/3.7.0.698...3.8.0.702) -## [`3.7.0.698`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.698) - 2020-08-19 +## [`3.7.0.698`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.7.0.698) - 2020-08-19 ### Added -* Graceful shutdown on sigterm [(#776)](https://github.com/rehlds/rehlds/pull/776) +* Graceful shutdown on sigterm [(#776)](https://github.com/ReHLDS/ReHLDS/pull/776) - Add players kick on `SIGINT \ SIGTERM` - Add SIGINT & SIGTERM handling linux console ### Changed * Changed shutdown method -**Full Changelog**: [3.7.0.697...3.7.0.698](https://github.com/rehlds/rehlds/compare/3.7.0.697...3.7.0.698) +**Full Changelog**: [3.7.0.697...3.7.0.698](https://github.com/ReHLDS/ReHLDS/compare/3.7.0.697...3.7.0.698) -## [`3.7.0.697`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.697) - 2020-08-10 +## [`3.7.0.697`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.7.0.697) - 2020-08-10 ### Fixed * **SV_ProcessFile:** Wrap `Con_Printf` in `Con_NetPrintf` to avoid spam in HLDS console -**Full Changelog**: [3.7.0.696...3.7.0.697](https://github.com/rehlds/rehlds/compare/3.7.0.696...3.7.0.697) +**Full Changelog**: [3.7.0.696...3.7.0.697](https://github.com/ReHLDS/ReHLDS/compare/3.7.0.696...3.7.0.697) -## [`3.7.0.696`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.696) - 2020-05-18 +## [`3.7.0.696`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.7.0.696) - 2020-05-18 ### Added -* Implement `svc_exec` support in the engine and HLTV [(#737)](https://github.com/rehlds/rehlds/pull/737) +* Implement `svc_exec` support in the engine and HLTV [(#737)](https://github.com/ReHLDS/ReHLDS/pull/737) - Added `svc_exec` to the list of svc commands in engine - Added `svc_exec` support to HLTV code - Made the engine code forward-compatible with future svc_* additions - Added reserved svc_* slots in the enumerations -**Full Changelog**: [3.7.0.695...3.7.0.696](https://github.com/rehlds/rehlds/compare/3.7.0.695...3.7.0.696) +**Full Changelog**: [3.7.0.695...3.7.0.696](https://github.com/ReHLDS/ReHLDS/compare/3.7.0.695...3.7.0.696) -## [`3.7.0.695`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.695) - 2020-04-06 +## [`3.7.0.695`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.7.0.695) - 2020-04-06 ### Fixed * Vulnerability fix WAD part 2 - Client-side: Fixed a potential vulnerability from bogus `tempdecal.wad` -**Full Changelog**: [3.7.0.694...3.7.0.695](https://github.com/rehlds/rehlds/compare/3.7.0.694...3.7.0.695) +**Full Changelog**: [3.7.0.694...3.7.0.695](https://github.com/ReHLDS/ReHLDS/compare/3.7.0.694...3.7.0.695) -## [`3.7.0.694`](https://github.com/rehlds/rehlds/releases/tag/3.7.0.694) - 2020-03-20 +## [`3.7.0.694`](https://github.com/ReHLDS/ReHLDS/releases/tag/3.7.0.694) - 2020-03-20 ### Fixed * Vulnerability fix WAD part 1 - Server-side: Fixed a potential vulnerability from bogus `tempdecal.wad` -**Full Changelog**: [3.7.0.694](https://github.com/rehlds/rehlds/commits/3.7.0.694) +**Full Changelog**: [3.7.0.694](https://github.com/ReHLDS/ReHLDS/commits/3.7.0.694) diff --git a/LICENSE.md b/LICENSE similarity index 100% rename from LICENSE.md rename to LICENSE diff --git a/README.md b/README.md index 9098f12..9d5f8eb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ReHLDS [![C/C++ CI](https://github.com/dreamstalker/rehlds/actions/workflows/build.yml/badge.svg)](https://github.com/dreamstalker/rehlds/actions/workflows/build.yml) [![GitHub release (by tag)](https://img.shields.io/github/downloads/dreamstalker/rehlds/latest/total)](https://github.com/dreamstalker/rehlds/releases/latest) ![GitHub all releases](https://img.shields.io/github/downloads/dreamstalker/rehlds/total) [![Percentage of issues still open](http://isitmaintained.com/badge/open/dreamstalker/rehlds.svg)](http://isitmaintained.com/project/dreamstalker/rehlds "Percentage of issues still open") [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ReHLDS +# ReHLDS [![C/C++ CI](https://github.com/rehlds/ReHLDS/actions/workflows/build.yml/badge.svg)](https://github.com/rehlds/ReHLDS/actions/workflows/build.yml) [![GitHub release (by tag)](https://img.shields.io/github/downloads/rehlds/ReHLDS/latest/total)](https://github.com/rehlds/ReHLDS/releases/latest) ![GitHub all releases](https://img.shields.io/github/downloads/rehlds/ReHLDS/total) [![Percentage of issues still open](http://isitmaintained.com/badge/open/rehlds/ReHLDS.svg)](http://isitmaintained.com/project/rehlds/ReHLDS "Percentage of issues still open") [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) ReHLDS Reverse-engineered (and bugfixed) HLDS ## What is this? @@ -15,7 +15,7 @@ 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. +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. @@ -27,13 +27,13 @@ app_update 90 -beta steam_legacy validate ``` ## Downloads -* [Release builds](https://github.com/dreamstalker/rehlds/releases) -* [Dev builds](https://github.com/dreamstalker/rehlds/actions/workflows/build.yml) +* [Release builds](https://github.com/rehlds/ReHLDS/releases) +* [Dev builds](https://github.com/rehlds/ReHLDS/actions/workflows/build.yml) ReHLDS binaries require `SSE`, `SSE2` and `SSE3` instruction sets to run and can benefit from `SSE4.1` and `SSE4.2` Warning! ReHLDS is not binary compatible with original hlds since it's compiled with compilers other than ones used for original hlds. -This means that plugins that do binary code analysis (Orpheu for example) probably will not work with rehlds. +This means that plugins that do binary code analysis (Orpheu for example) probably will not work with ReHLDS. ## Configuring
    @@ -57,7 +57,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
  • sv_rehlds_stringcmdrate_avg_punish // Time in minutes for which the player will be banned (0 - Permanent, use a negative number for a kick). Default: 5
  • sv_rehlds_stringcmdrate_max_burst // Max burst level of 'string' cmds for ban. Default: 400
  • sv_rehlds_stringcmdrate_burst_punish // Time in minutes for which the player will be banned (0 - Permanent, use a negative number for a kick). Default: 5 -
  • sv_rehlds_userinfo_transmitted_fields // Userinfo fields only with these keys will be transmitted to clients via network. If not set then all fields will be transmitted (except prefixed with underscore). Each key must be prefixed by backslash, for example "\name\model\*sid\*hltv\bottomcolor\topcolor". See [wiki](https://github.com/dreamstalker/rehlds/wiki/Userinfo-keys) to collect sufficient set of keys for your server. Default: "" +
  • sv_rehlds_userinfo_transmitted_fields // Userinfo fields only with these keys will be transmitted to clients via network. If not set then all fields will be transmitted (except prefixed with underscore). Each key must be prefixed by backslash, for example "\name\model\*sid\*hltv\bottomcolor\topcolor". See [wiki](https://github.com/rehlds/ReHLDS/wiki/Userinfo-keys) to collect sufficient set of keys for your server. Default: ""
  • sv_rehlds_attachedentities_playeranimationspeed_fix // Fixes bug with gait animation speed increase when player has some attached entities (aiments). Can cause animation lags when cl_updaterate is low. Default: 0
  • sv_rehlds_maxclients_from_single_ip // Limit number of connections at the same time from single IP address, not confuse to already connected players. Default: 5
  • sv_rehlds_local_gametime <1|0> // A feature of local gametime which decrease "lags" if you run same map for a long time. Default: 0 @@ -83,7 +83,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab ## Build instructions ### Checking requirements -There are several software requirements for building rehlds: +There are several software requirements for building ReHLDS: #### Windows
    
    From 12b35dbe8c9b9e74fa431d233dc1ffb9a33ef0fd Mon Sep 17 00:00:00 2001
    From: STAM 
    Date: Wed, 20 Nov 2024 00:25:27 +0300
    Subject: [PATCH 31/31] 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