Skip to content

Commit

Permalink
Merge pull request #3 from dreamstalker/master
Browse files Browse the repository at this point in the history
Add steamcmd instructions to readme (rehlds#1021)
  • Loading branch information
jonathan-up authored May 2, 2024
2 parents 8bdbe5d + 0d1bdba commit 20096bf
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 64 deletions.
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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) <img align="right" src="https://user-images.githubusercontent.com/5860435/111066129-040e5e00-84f0-11eb-9e1f-7a7e8611da2b.png" alt="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) <img align="right" src="https://user-images.githubusercontent.com/5860435/111066129-040e5e00-84f0-11eb-9e1f-7a7e8611da2b.png" alt="ReHLDS" />
Reverse-engineered (and bugfixed) HLDS

## What is this?
Expand All @@ -15,8 +15,16 @@ You can try playing on one of many servers that are using ReHLDS: [Game Tracker]
</ul>

## How can use it?
ReHLDS is fully compatible with latest official HLDS downloaded by steamcmd. All you have to do is to download rehlds binaries and replace original swds.dll/engine_i486.so. For windows you can also copy a swds.pdb file with a debug information.
<br /><b>Warning!</b> ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool.
ReHLDS is fully compatible with the official pre-anniversary edition of HLDS (engine version <= 8684) downloaded by steamcmd. All you have to do is to download rehlds binaries and replace original swds.dll/engine_i486.so. For windows you can also copy a swds.pdb file with a debug information.

<b>Warning!</b> ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool.

#### Downloading HLDS via steamcmd

```
app_set_config 90 mod cstrike
app_update 90 -beta steam_legacy validate
```

## Downloads
* [Release builds](https://github.com/dreamstalker/rehlds/releases)
Expand Down Expand Up @@ -55,6 +63,10 @@ This means that plugins that do binary code analysis (Orpheu for example) probab
<li>sv_rehlds_local_gametime &lt;1|0&gt; // A feature of local gametime which decrease "lags" if you run same map for a long time. Default: 0
<li>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.
<li>sv_usercmd_custom_random_seed // When enabled server will populate an additional random seed independent of the client. Default: 0
<li>sv_net_incoming_decompression <1|0> // When enabled server will decompress of incoming compressed file transfer payloads. Default: 1
<li>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
<li>sv_net_incoming_decompression_max_size <16|65536> // Sets the max allowed size for decompressed file transfer data. Default: 65536 bytes
<li>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
<li>sv_tags &lt;comma-delimited string list of tags&gt; // 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: ""
</ul>
</details>
Expand Down
69 changes: 30 additions & 39 deletions rehlds/engine/decals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
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 wad dimensions %s\n", __func__, wad->name);
Con_Printf("%s: Bad cached wad tex size %ux%u on %s\n", __func__, tex.width, tex.height, wad->name);
return FALSE;
}

for (int i = 0; i < MIPLEVELS; i++)
tex.offsets[i] = wad->cacheExtra + LittleLong(tmp.offsets[i]);

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(miptex_t);
palettesize = *(u_short *)(data + wad->cacheExtra + 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);
return FALSE;
// 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;
}

nPalleteCount = *(u_short *)(data + pixoffset + sizeof(texture_t));
if (nPalleteCount > 256)
if (palettesize > 256)
{
Con_Printf("%s: Bad cached wad palette size %i on %s\n", __func__, nPalleteCount, wad->name);
Con_Printf("%s: Bad cached wad palette size %i on %s\n", __func__, palettesize, 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;
Expand Down
2 changes: 2 additions & 0 deletions rehlds/engine/hashpak.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
103 changes: 90 additions & 13 deletions rehlds/engine/net_chan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -1203,7 +1209,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));
Expand Down Expand Up @@ -1431,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();

Expand Down Expand Up @@ -1468,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;

chan->incomingbufs[FRAG_NORMAL_STREAM] = nullptr;
chan->incomingready[FRAG_NORMAL_STREAM] = FALSE;
// 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);

return TRUE;
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);
}
}

return success;
}

qboolean Netchan_CopyFileFragments(netchan_t *chan)
Expand Down Expand Up @@ -1824,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);
}

Expand Down
6 changes: 6 additions & 0 deletions rehlds/engine/sv_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
27 changes: 18 additions & 9 deletions rehlds/engine/sv_upld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,13 +138,16 @@ void SV_CreateCustomizationList(client_t *pHost)
{
pCust->nUserData2 = nLumps;
gEntityInterface.pfnPlayerCustomization(pHost->edict, pCust);
#ifdef REHLDS_FIXES
SV_Customization(pHost, pResource, TRUE);
#endif
}
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);
}
}
}
Expand All @@ -157,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++)
{
Expand Down Expand Up @@ -205,10 +213,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)
{
Expand Down Expand Up @@ -509,8 +513,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);
}
}

0 comments on commit 20096bf

Please sign in to comment.