2
0
mirror of https://github.com/rehlds/rehlds.git synced 2025-05-09 05:09:37 +03:00

Sync branch 'master' into feature/bzip2-upgrade

This commit is contained in:
STAM 2025-04-07 21:29:55 +03:00
commit 0eeb41aa6e
No known key found for this signature in database
GPG Key ID: 711526C6938897F1
22 changed files with 601 additions and 276 deletions

View File

@ -10,6 +10,7 @@ on:
types: [opened, reopened, synchronize]
release:
types: [published]
workflow_dispatch:
jobs:
windows:
@ -24,6 +25,9 @@ jobs:
buildTest: 'Test Fixes'
steps:
- name: Configure
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout
uses: actions/checkout@v4
with:
@ -46,6 +50,11 @@ jobs:
msbuild ${{ env.solution }} -p:Configuration="${{ env.buildRelease }}" /t:Clean,Build /p:Platform=${{ env.buildPlatform }} /p:PlatformToolset=v140_xp /p:XPDeprecationWarning=false
msbuild ${{ env.solution }} -p:Configuration="${{ env.buildReleasePlay }}" /t:Clean,Build /p:Platform=${{ env.buildPlatform }} /p:PlatformToolset=v140_xp /p:XPDeprecationWarning=false
- name: Get rcedit from chocolatey
run: |
choco install rcedit -y
shell: "pwsh"
- name: Move files
run: |
mkdir publish\debug
@ -69,6 +78,40 @@ jobs:
move msvc\${{ env.buildRelease }}\filesystem_stdio.pdb publish\debug\filesystem_stdio.pdb
move msvc\${{ env.buildRelease }}\director.pdb publish\debug\director.pdb
# TODO: Set version to exe dynamicly: 0.0.0.0 to normal version such as at linux
- name: Edit resources at windows binaries
run: |
rcedit ${{ github.workspace }}\publish\bin\win32\hlds.exe --set-version-string ProductName "ReHLDS" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "The Half-Life Dedicated Server, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam"
rcedit ${{ github.workspace }}\publish\bin\win32\hltv.exe --set-version-string ProductName "ReHLTV" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "The Half-Life TV, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam"
rcedit ${{ github.workspace }}\publish\tests\swds.dll --set-version-string ProductName "swds.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll used by Steamworks Dedicated Servers, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico
rcedit ${{ github.workspace }}\publish\bin\win32\swds.dll --set-version-string ProductName "swds.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A ddll used by Steamworks Dedicated Servers, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico
rcedit ${{ github.workspace }}\publish\bin\win32\core.dll --set-version-string ProductName "core.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription " A dll, it is a core of game engine, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico
rcedit ${{ github.workspace }}\publish\bin\win32\proxy.dll --set-version-string ProductName "proxy.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll for proxying network connections, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico
rcedit ${{ github.workspace }}\publish\bin\win32\demoplayer.dll --set-version-string ProductName "demoplayer.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll for demoplayer functionality, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico
rcedit ${{ github.workspace }}\publish\bin\win32\filesystem_stdio.dll --set-version-string ProductName "filesystem_stdio.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll that manages file input/output operations, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico
rcedit ${{ github.workspace }}\publish\bin\win32\valve\dlls\director.dll --set-version-string ProductName "director.dll" --set-file-version "0.0.0.0" --set-product-version "0.0.0.0" --set-version-string FileDescription "A dll used for Director functionality in Half-Life 1, Commit: $env:GITHUB_SHA" --set-version-string "Comments" "Commit: $env:GITHUB_SHA" --set-version-string CompanyName "ReHLDS Dev Team" --set-version-string LegalCopyright "Copyright 2025 Valve, ReHLDS DevTeam" --set-icon rehlds/dedicated/msvc/icon.ico
shell: "pwsh"
- name: Import PFX and sign
if: github.event_name != 'pull_request'
env:
KEY_PFX_PASS: ${{ secrets.KEY_PFX_PASS }}
run: |
$pfxBase64 = "${{ secrets.KEY_PFX_B64 }}"
[IO.File]::WriteAllBytes("${{ github.workspace }}\signing-cert.pfx", [Convert]::FromBase64String($pfxBase64))
certutil -f -p "${{ secrets.KEY_PFX_PASS }}" -importPFX "${{ github.workspace }}\signing-cert.pfx"
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\hlds.exe
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "reHLTV" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\hltv.exe
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - swds.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\tests\swds.dll
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - swds.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\swds.dll
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - core.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\core.dll
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - proxy.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\proxy.dll
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - demoplayer.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\demoplayer.dll
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - filesystem_stdio.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\filesystem_stdio.dll
& 'C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x86\signtool.exe' sign /a /f "${{ github.workspace }}\signing-cert.pfx" /p $env:KEY_PFX_PASS /d "ReHLDS - director.dll" /du "https://rehlds.dev/" /tr "http://timestamp.digicert.com" /td sha512 /fd sha512 /v ${{ github.workspace }}\publish\bin\win32\valve\dlls\director.dll
Remove-Item -Recurse -Force "${{ github.workspace }}\signing-cert.pfx"
shell: "pwsh"
- name: Deploy artifacts
uses: actions/upload-artifact@v4
with:
@ -77,7 +120,7 @@ jobs:
testdemos:
name: 'Test demos'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
container: rehldsorg/testdemos:latest
needs: [windows]
defaults:
@ -115,15 +158,10 @@ jobs:
linux:
name: 'Linux'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
container: debian:11-slim
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install dependencies
run: |
dpkg --add-architecture i386
@ -135,6 +173,57 @@ jobs:
git cmake rsync \
g++ gcc
- name: Configure
run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: GPG Import
run: |
echo "${{ secrets.PUB_ASC }}" > "${{ secrets.PUB_ASC_FILE }}"
echo "${{ secrets.KEY_ASC }}" > "${{ secrets.KEY_ASC_FILE }}"
# Import the public key
gpg --batch --yes --import "${{ secrets.PUB_ASC_FILE }}"
if [[ $? -ne 0 ]]; then
echo "Error: Failed to import the public key"
exit 1
fi
# Import the private key
gpg --batch --yes --import "${{ secrets.KEY_ASC_FILE }}"
if [[ $? -ne 0 ]]; then
echo "Error: Failed to import the private key"
exit 2
fi
# Extract the fingerprint of the imported public key
GPG_LINUX_FINGERPRINT=$(gpg --list-keys --with-colons | grep '^fpr' | head -n 1 | cut -d: -f10)
# Check if the fingerprint was extracted
if [[ -z "$GPG_LINUX_FINGERPRINT" ]]; then
echo "Error: Failed to extract the fingerprint of the key"
exit 3
fi
# Set the trust level for the key
echo "$GPG_LINUX_FINGERPRINT:6:" | gpg --batch --import-ownertrust
if [ $? -ne 0 ]; then
echo "Error: Failed to set trust for the key $GPG_LINUX_FINGERPRINT"
exit 4
fi
echo "Key $GPG_LINUX_FINGERPRINT successfully imported and trusted"
gpg --list-keys
#export for global use
echo "GPG_LINUX_FINGERPRINT=$GPG_LINUX_FINGERPRINT" >> $GITHUB_ENV
shell: bash
if: github.event_name != 'pull_request'
- name: Build and Run unittests
run: |
rm -rf build && cmake -DCMAKE_BUILD_TYPE=Unittests -B build && cmake --build build -j8
@ -212,7 +301,7 @@ jobs:
publish:
name: 'Publish'
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
needs: [windows, testdemos, linux]
steps:
@ -240,15 +329,42 @@ jobs:
fi
rm -f appversion.h
- name: Packaging bin/dbg
- name: Final signing and Packaging bin/dbg
id: packaging-job
if: |
github.event_name == 'release' &&
github.event.action == 'published' &&
startsWith(github.ref, 'refs/tags/')
run: |
# new runner, niw signs
echo "${{ secrets.PUB_ASC }}" > "${{ secrets.PUB_ASC_FILE }}"
echo "${{ secrets.KEY_ASC }}" > "${{ secrets.KEY_ASC_FILE }}"
gpg --batch --yes --import "${{ secrets.PUB_ASC_FILE }}"
gpg --batch --yes --import "${{ secrets.KEY_ASC_FILE }}"
GPG_LINUX_FINGERPRINT=$(gpg --list-keys --with-colons | grep '^fpr' | head -n 1 | cut -d: -f10)
echo "$GPG_LINUX_FINGERPRINT:6:" | gpg --batch --import-ownertrust
echo "GPG_LINUX_FINGERPRINT=$GPG_LINUX_FINGERPRINT" >> $GITHUB_ENV
sign_file() {
local file=$1
gpg --batch --yes --detach-sign --armor -u "$GPG_LINUX_FINGERPRINT" "$file"
if [ $? -ne 0 ]; then
echo "Error: Failed to sign $file"
exit 2
fi
echo "$file signed successfully."
}
# Pack and sign final archive
7z a -tzip rehlds-bin-${{ env.APP_VERSION }}.zip bin/ hlsdk/
sign_file "rehlds-bin-${{ env.APP_VERSION }}.zip"
# Pack and sign final archive
7z a -t7z -m0=lzma2 -mx=9 -mfb=64 -aoa rehlds-dbg-${{ env.APP_VERSION }}.7z debug/
sign_file "rehlds-dbg-${{ env.APP_VERSION }}.7z"
shell: bash
- name: Publish artifacts
uses: softprops/action-gh-release@v2
@ -260,5 +376,6 @@ jobs:
files: |
*.zip
*.7z
*.asc
env:
GITHUB_TOKEN: ${{ secrets.API_TOKEN }}

View File

@ -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

View File

@ -8,6 +8,15 @@ Along with reverse engineering, a lot of defects and (potential) bugs were found
You can try playing on one of many servers that are using ReHLDS: [Game Tracker](http://www.gametracker.com/search/?search_by=server_variable&search_by2=sv_version)
> [!TIP]
> ReHLDS linux-releases now is signed via `GPG`, pubkey is: `63547829004f07716f7be4856c32c4282e60fb67` and could be found at [https://keyserver.ubuntu.com/](https://keyserver.ubuntu.com/pks/lookup?search=63547829004f07716f7be4856c32c4282e60fb67+&fingerprint=on&op=index).
>
> How to:
> 1. [Download](https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x63547829004f07716f7be4856c32c4282e60fb67) `63547829004f07716f7be4856c32c4282e60fb67.asc` key
> 2. Import: `gpg --import 63547829004f07716f7be4856c32c4282e60fb67.asc`
> 3. Download release `archive` and `.asc` file.
> 4. Verify: `gpg --verify some-rehlds.zip.asc some-rehlds.zip`.
## Goals of the project
<ul>
<li>Provide more stable (than official) version of Half-Life dedicated server with extended API for mods and plugins</li>
@ -17,7 +26,8 @@ You can try playing on one of many servers that are using ReHLDS: [Game Tracker]
## How can use it?
ReHLDS is fully compatible with the official pre-anniversary edition of HLDS (engine version <= 8684) downloaded by steamcmd. All you have to do is to download ReHLDS binaries and replace original swds.dll/engine_i486.so. For windows you can also copy a swds.pdb file with a debug information.
<b>Warning!</b> ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool.
> [!CAUTION]
> ReHLDS is not compatible with an old 5xxx or below platforms downloaded by hldsupdatetool.
#### Downloading HLDS via steamcmd
@ -63,9 +73,12 @@ 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 &lt;1|0&gt; // When enabled server will decompress of incoming compressed file transfer payloads. Default: 1
<li>sv_net_incoming_decompression_max_ratio &lt;0|100&gt; // 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 &lt;16|65536&gt; // Sets the max allowed size for decompressed file transfer data. Default: 65536 bytes
<li>sv_net_incoming_decompression_min_failures &lt;0|10&gt; // Sets the min number of decompression failures required before a player's connection is flagged for potential punishment. Default: 4
<li>sv_net_incoming_decompression_max_failures &lt;0|10&gt; // Sets the max number of decompression failures allowed within a specified time window before action is taken against the player. Default: 10
<li>sv_net_incoming_decompression_min_failuretime: &lt;0.1|10.0&gt; // Sets the min time in secs within which decompression failures are tracked to determine if the player exceeds the failure thresholds. Default: 0.1
<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: ""
<li>sv_filterban &lt;-1|0|1&gt;// 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
@ -134,6 +147,7 @@ sudo apt-get update
sudo apt-get install -y gcc-multilib g++-multilib
sudo apt-get install -y build-essential
sudo apt-get install -y libc6-dev libc6-dev-i386
sudo apt-get install -y cmake
</pre>
</li>

View File

@ -74,6 +74,7 @@
// SV_EmitSound2 flags
#define SND_EMIT2_NOPAS (1<<0) // never to do check PAS
#define SND_EMIT2_INVOKER (1<<1) // do not send to the client invoker
#define SND_EMIT2_USE_ORIGIN (1<<2) // use given origin instead of entity origin
// Engine edict->spawnflags
#define SF_NOTINDEATHMATCH 0x0800 // Do not spawn when deathmatch and loading entities from a file

View File

@ -856,6 +856,12 @@ qboolean Info_IsValid(const char *s)
return false;
};
// invalid utf8 chars are deprecated
if (!Q_UnicodeValidate(s))
{
return FALSE;
}
while (*s == '\\')
{
const char* key = ++s;

View File

@ -854,6 +854,7 @@ void CalcSurfaceExtents(msurface_t *s)
int i, j, e;
mvertex_t *v;
mtexinfo_t *tex;
vec3_t middle{};
int bmins[2], bmaxs[2];
mins[0] = mins[1] = 999999;
@ -869,6 +870,8 @@ void CalcSurfaceExtents(msurface_t *s)
else
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
VectorAdd(middle, v->position, middle);
for (j = 0; j < 2; j++)
{
// FIXED: loss of floating point
@ -884,6 +887,8 @@ void CalcSurfaceExtents(msurface_t *s)
}
}
VectorScale(middle, 1.0f / s->numedges, middle);
for (i = 0; i < 2; i++)
{
bmins[i] = (int) floor(mins[i] / 16);
@ -891,8 +896,16 @@ void CalcSurfaceExtents(msurface_t *s)
s->texturemins[i] = bmins[i] * 16;
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
if (!(tex->flags & TEX_SPECIAL) && s->extents[i] > MAX_SURFACE_TEXTURE_SIZE)
Sys_Error("%s: Bad surface extents", __func__);
{
int surfID = s - loadmodel->surfaces;
Sys_Error("%s: Bad #%d surface extents %d/%d on %s at position (%d,%d,%d)",
__func__, surfID, s->extents[0], s->extents[1],
tex->texture->name,
(int)middle[0], (int)middle[1], (int)middle[2]
);
}
}
}

View File

@ -422,6 +422,7 @@ typedef struct netchan_s
// Incoming and outgoing flow metrics
flow_t flow[MAX_FLOWS];
} netchan_t;
#ifdef REHLDS_FIXES

View File

@ -40,7 +40,10 @@ cvar_t net_chokeloopback = { "net_chokeloop", "0", 0, 0.0f, nullptr};
cvar_t sv_net_incoming_decompression = { "sv_net_incoming_decompression", "1", 0, 1.0f, nullptr };
cvar_t sv_net_incoming_decompression_max_ratio = { "sv_net_incoming_decompression_max_ratio", "80.0", 0, 80.0f, nullptr };
cvar_t sv_net_incoming_decompression_max_size = { "sv_net_incoming_decompression_max_size", "65536", 0, 65536.0f, nullptr };
cvar_t sv_net_incoming_decompression_punish = { "sv_net_incoming_decompression_punish", "-1", 0, -1.0f, NULL };
cvar_t sv_net_incoming_decompression_min_failures = { "sv_net_incoming_decompression_min_failures", "4", 0, 4.0f, nullptr };
cvar_t sv_net_incoming_decompression_max_failures = { "sv_net_incoming_decompression_max_failures", "10", 0, 10.0f, nullptr };
cvar_t sv_net_incoming_decompression_min_failuretime = { "sv_net_incoming_decompression_min_failuretime", "0.1", 0, 0.1f, nullptr };
cvar_t sv_net_incoming_decompression_punish = { "sv_net_incoming_decompression_punish", "-1", 0, -1.0f, nullptr };
cvar_t sv_filetransfercompression = { "sv_filetransfercompression", "1", 0, 0.0f, nullptr};
cvar_t sv_filetransfermaxsize = { "sv_filetransfermaxsize", "10485760", 0, 0.0f, nullptr};
@ -184,6 +187,7 @@ void Netchan_Clear(netchan_t *chan)
void Netchan_Setup(netsrc_t socketnumber, netchan_t *chan, netadr_t adr, int player_slot, void *connection_status, qboolean(*pfnNetchan_Blocksize)(void *))
{
Netchan_Clear(chan);
g_GameClients[player_slot]->NetchanClear();
Q_memset(chan, 0, sizeof(netchan_t));
@ -1423,6 +1427,93 @@ void Netchan_FlushIncoming(netchan_t *chan, int stream)
chan->incomingready[stream] = FALSE;
}
void Netchan_DecompressionCvarsBounds()
{
if (sv_net_incoming_decompression_min_failures.value < 1)
Cvar_SetValue("sv_net_incoming_decompression_min_failures", 1);
else if (sv_net_incoming_decompression_min_failures.value > NET_DECOMPRESS_MAX_TIMES)
Cvar_SetValue("sv_net_incoming_decompression_min_failures", NET_DECOMPRESS_MAX_TIMES);
if (sv_net_incoming_decompression_max_failures.value < 1)
Cvar_SetValue("sv_net_incoming_decompression_max_failures", 1);
else if (sv_net_incoming_decompression_max_failures.value > NET_DECOMPRESS_MAX_TIMES)
Cvar_SetValue("sv_net_incoming_decompression_max_failures", NET_DECOMPRESS_MAX_TIMES);
if (sv_net_incoming_decompression_max_failures.value < sv_net_incoming_decompression_min_failures.value)
{
int iTemp = sv_net_incoming_decompression_max_failures.value;
Cvar_SetValue("sv_net_incoming_decompression_max_failures", sv_net_incoming_decompression_min_failures.value);
Cvar_SetValue("sv_net_incoming_decompression_min_failures", iTemp);
}
if (sv_net_incoming_decompression_min_failuretime.value <= 0.0f)
Cvar_SetValue("sv_net_incoming_decompression_min_failuretime", 0.1f);
}
// Check for an abnormal size ratio between compressed and uncompressed data
qboolean Netchan_ValidateDecompress(netchan_t *chan, int stream, unsigned int compressedSize, unsigned int uncompressedSize)
{
#ifdef REHLDS_FIXES
int i;
if (sv_net_incoming_decompression_max_ratio.value <= 0)
return TRUE; // validation is disabled
if (compressedSize >= uncompressedSize)
return TRUE;
float ratio = ((float)(uncompressedSize - compressedSize) / uncompressedSize) * 100.0f;
if (ratio < sv_net_incoming_decompression_max_ratio.value)
return TRUE; // no low entropy for uncompressed data
if ((chan->player_slot - 1) != host_client - g_psvs.clients)
return TRUE;
Netchan_DecompressionCvarsBounds();
FragStats_t &stats = g_GameClients[chan->player_slot - 1]->GetFragStats(stream);
// check if the client should be rejected based on total failed decompress
if (stats.num_decompress_failures >= sv_net_incoming_decompression_max_failures.value)
{
for (i = 0; i < sv_net_incoming_decompression_max_failures.value - 1; i++)
stats.decompress_failure_times[i] = stats.decompress_failure_times[i + 1];
stats.num_decompress_failures = sv_net_incoming_decompression_max_failures.value - 1;
}
stats.decompress_failure_times[stats.num_decompress_failures++] = realtime;
// check if the client should be rejected based on recent failed decompress
int recent_failures = 0;
for (i = 0; i < stats.num_decompress_failures; i++)
{
if ((realtime - stats.decompress_failure_times[i]) <= sv_net_incoming_decompression_min_failuretime.value)
recent_failures++;
}
if (recent_failures >= sv_net_incoming_decompression_min_failures.value)
{
if (chan->player_slot == 0)
Con_DPrintf("Incoming abnormal uncompressed size with ratio %.2f\n", ratio);
else
Con_DPrintf("%s:Incoming abnormal uncompressed size with ratio %.2f from %s\n", NET_AdrToString(chan->remote_address), ratio, host_client->name);
if (sv_net_incoming_decompression_punish.value >= 0)
{
Con_DPrintf("%s:Banned for malformed/abnormal bzip2 fragments from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
Cbuf_AddText(va("addip %.1f %s\n", sv_net_incoming_decompression_punish.value, NET_BaseAdrToString(chan->remote_address)));
}
return FALSE;
}
#endif
return TRUE;
}
qboolean Netchan_CopyNormalFragments(netchan_t *chan)
{
fragbuf_t *p, *n;
@ -1483,8 +1574,6 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan)
}
#endif // REHLDS_FIXES
qboolean success = TRUE;
if (*(uint32 *)net_message.data == MAKEID('B', 'Z', '2', '\0'))
{
// Determine whether decompression of compressed data is allowed
@ -1493,13 +1582,13 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan)
{
if (chan->player_slot == 0)
{
Con_DPrintf("Incoming compressed data disallowed from\n");
Con_DPrintf("Incoming compressed normal fragment disallowed from\n");
return FALSE;
}
// compressed data is expected only after requesting resource list
else if (host_client->m_sendrescount == 0)
{
Con_DPrintf("%s:Incoming compressed data disallowed from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
Con_DPrintf("%s:Incoming compressed normal fragment disallowed from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
return FALSE;
}
}
@ -1510,56 +1599,34 @@ qboolean Netchan_CopyNormalFragments(netchan_t *chan)
unsigned int compressedSize = net_message.cursize - 4;
// Decompress net buffer data
if (success && (BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char *)net_message.data + 4, compressedSize, 1, 0) == BZ_OK))
{
#ifdef REHLDS_FIXES
// Check for an abnormal size ratio between compressed and uncompressed data
if (sv_net_incoming_decompression_max_ratio.value > 0 && compressedSize < uncompressedSize)
{
float ratio = ((float)(uncompressedSize - compressedSize) / uncompressedSize) * 100.0f;
if (ratio >= sv_net_incoming_decompression_max_ratio.value)
{
if (chan->player_slot == 0)
Con_DPrintf("Incoming abnormal uncompressed size with ratio %.2f\n", ratio);
else
Con_DPrintf("%s:Incoming abnormal uncompressed size with ratio %.2f from %s\n", NET_AdrToString(chan->remote_address), ratio, host_client->name);
qboolean success = TRUE;
success = FALSE;
}
}
#endif
// Copy uncompressed data back to the net buffer
Q_memcpy(net_message.data, uncompressed, uncompressedSize);
net_message.cursize = uncompressedSize;
}
else
if (BZ2_bzBuffToBuffDecompress(uncompressed, &uncompressedSize, (char *)net_message.data + 4, compressedSize, 1, 0) != BZ_OK)
{
// malformed data or compressed data exceeding sv_net_incoming_decompression_max_size
success = FALSE;
}
else if (!Netchan_ValidateDecompress(chan, FRAG_NORMAL_STREAM, compressedSize, uncompressedSize))
{
success = FALSE;
}
// Drop client if decompression was unsuccessful
#ifdef REHLDS_FIXES
if (!success)
{
if ((chan->player_slot - 1) == host_client - g_psvs.clients)
{
#ifdef REHLDS_FIXES
if (sv_net_incoming_decompression_punish.value >= 0)
{
Con_DPrintf("%s:Banned for malformed/abnormal bzip2 fragments from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
Cbuf_AddText(va("addip %.1f %s\n", sv_net_incoming_decompression_punish.value, NET_BaseAdrToString(chan->remote_address)));
}
// Drop client if decompression was unsuccessful
SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data");
SZ_Clear(&net_message);
return FALSE;
}
#endif
SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data");
}
SZ_Clear(&net_message);
}
// Copy uncompressed data back to the net buffer
Q_memcpy(net_message.data, uncompressed, uncompressedSize);
net_message.cursize = uncompressedSize;
}
return success;
return TRUE;
}
qboolean Netchan_CopyFileFragments(netchan_t *chan)
@ -1575,7 +1642,6 @@ qboolean Netchan_CopyFileFragments(netchan_t *chan)
qboolean bCompressed;
unsigned int uncompressedSize;
if (!chan->incomingready[FRAG_FILE_STREAM])
return FALSE;
@ -1587,6 +1653,19 @@ qboolean Netchan_CopyFileFragments(netchan_t *chan)
return FALSE;
}
#ifdef REHLDS_FIXES
if (chan->player_slot > 0 && (chan->player_slot - 1) == host_client - g_psvs.clients)
{
// customization already uploaded with request by operator,
// do not accept any other customization
if (host_client->uploaddoneregistering)
{
SV_DropClient(host_client, FALSE, "Too many customization have been uploaded (unrequested customization)");
return FALSE;
}
}
#endif
bCompressed = FALSE;
SZ_Clear(&net_message);
MSG_BeginReading();
@ -1701,10 +1780,53 @@ qboolean Netchan_CopyFileFragments(netchan_t *chan)
if (bCompressed)
{
unsigned char* uncompressedBuffer = (unsigned char*)Mem_Malloc(uncompressedSize);
Con_DPrintf("Decompressing file %s (%d -> %d)\n", filename, nsize, uncompressedSize);
BZ2_bzBuffToBuffDecompress((char*)uncompressedBuffer, &uncompressedSize, (char*)buffer, nsize, 1, 0);
// Determine whether decompression of compressed data is allowed
#ifdef REHLDS_FIXES
if (!sv_net_incoming_decompression.value)
{
if (chan->player_slot == 0)
{
Con_DPrintf("Incoming compressed file fragment disallowed from\n");
return FALSE;
}
// compressed data is expected only after requesting resource list
else if (host_client->m_sendrescount == 0)
{
Con_DPrintf("%s:Incoming compressed file fragment disallowed from %s\n", NET_AdrToString(chan->remote_address), host_client->name);
return FALSE;
}
}
#endif
uncompressedSize = clamp(uncompressedSize, 16u, (unsigned)sv_net_incoming_decompression_max_size.value); // valid range (16 - 65536) bytes
qboolean success = TRUE;
unsigned char *uncompressedBuffer = (unsigned char *)Mem_Malloc(uncompressedSize);
unsigned int compressedSize = nsize;
// Decompress net buffer data
if (BZ2_bzBuffToBuffDecompress((char *)uncompressedBuffer, &uncompressedSize, (char *)buffer, compressedSize, 1, 0) != BZ_OK)
{
// malformed data or compressed data exceeding sv_net_incoming_decompression_max_size
success = FALSE;
}
else if (!Netchan_ValidateDecompress(chan, FRAG_FILE_STREAM, compressedSize, uncompressedSize))
{
success = FALSE;
}
Mem_Free(buffer);
if (!success)
{
// Drop client if decompression was unsuccessful
SV_DropClient(host_client, FALSE, "Malformed/abnormal compressed data");
SZ_Clear(&net_message);
Mem_Free(uncompressedBuffer);
return FALSE;
}
Con_DPrintf("Decompressing file %s (%d -> %d)\n", filename, compressedSize, uncompressedSize);
pos = uncompressedSize;
buffer = uncompressedBuffer;
}
@ -1899,6 +2021,9 @@ void Netchan_Init(void)
Cvar_RegisterVariable(&sv_net_incoming_decompression);
Cvar_RegisterVariable(&sv_net_incoming_decompression_max_ratio);
Cvar_RegisterVariable(&sv_net_incoming_decompression_max_size);
Cvar_RegisterVariable(&sv_net_incoming_decompression_min_failures);
Cvar_RegisterVariable(&sv_net_incoming_decompression_max_failures);
Cvar_RegisterVariable(&sv_net_incoming_decompression_min_failuretime);
Cvar_RegisterVariable(&sv_net_incoming_decompression_punish);
#endif
Cvar_RegisterVariable(&sv_filetransfermaxsize);

View File

@ -1768,7 +1768,7 @@ void NET_GetLocalAddress()
if (noip)
{
Con_Printf("TCP/IP Disabled.\n");
Con_DPrintf("TCP/IP Disabled.\n");
}
else
{
@ -1823,7 +1823,7 @@ void NET_GetLocalAddress()
#ifdef _WIN32
if (noipx)
{
Con_Printf("No IPX Support.\n");
Con_DPrintf("No IPX Support.\n");
}
else
{

View File

@ -29,7 +29,7 @@
#include "precompiled.h"
playermove_t *pmove;
movevars_t movevars;
movevars_t sv_movevars;
cvar_t pm_showclip = { "pm_showclip", "0", 0, 0.0f, NULL };
@ -98,7 +98,7 @@ void PM_Init(playermove_t *ppm)
VectorCopy(player_maxs[i], ppm->player_maxs[i]);
}
ppm->_movevars = &movevars;
ppm->movevars = &sv_movevars;
ppm->PM_Info_ValueForKey = Info_ValueForKey;
ppm->PM_Particle = CL_Particle;

View File

@ -39,7 +39,7 @@ extern vec3_t player_mins[MAX_MAP_HULLS];
extern vec3_t player_maxs[MAX_MAP_HULLS];
extern playermove_t *pmove;
extern movevars_t movevars;
extern movevars_t sv_movevars;
qboolean PM_AddToTouched(pmtrace_t tr, vec_t *impactvelocity);
void PM_StuckTouch(int hitent, pmtrace_t *ptraceresult);

View File

@ -53,6 +53,7 @@ const int MAX_NAME = 32;
#include "pm_defs.h"
#include "inst_baseline.h"
#include "net_ws.h"
#include "pm_shared/pm_movevars.h"
const int DEFAULT_SOUND_PACKET_VOLUME = 255;
const float DEFAULT_SOUND_PACKET_ATTENUATION = 1.0f;
@ -235,6 +236,7 @@ typedef struct client_s
double m_lastvoicetime;
int m_sendrescount;
qboolean m_bSentNewResponse;
movevars_t movevars;
} client_t;
enum
@ -454,10 +456,9 @@ void SV_BuildHashedSoundLookupTable(void);
void SV_AddSampleToHashedLookupTable(const char *pszSample, int iSampleIndex);
qboolean SV_ValidClientMulticast(client_t *client, int soundLeaf, int to);
void SV_Multicast(edict_t *ent, vec_t *origin, int to, qboolean reliable);
void SV_WriteMovevarsToClient(sizebuf_t *message);
void SV_WriteMovevarsToClient(sizebuf_t *message, struct movevars_s *movevars);
void SV_WriteDeltaDescriptionsToClient(sizebuf_t *msg);
void SV_SetMoveVars(void);
void SV_QueryMovevarsChanged(void);
void SV_SetMoveVars(struct movevars_s *movevars);
void SV_SendServerinfo(sizebuf_t *msg, client_t *client);
void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client);
void SV_SendResources(sizebuf_t *msg);

View File

@ -132,10 +132,47 @@ cvar_t sv_wateramp = { "sv_wateramp", "0", 0, 0.0f, NULL };
void sv_cheats_hook_callback(cvar_t *cvar);
void mapcyclefile_hook_callback(cvar_t *cvar);
void sv_movevars_hook_callback(cvar_t *cvar);
cvarhook_t sv_cheats_hook = { sv_cheats_hook_callback, NULL, NULL };
cvarhook_t mapcyclefile_hook = { mapcyclefile_hook_callback, NULL, NULL };
//------------------------------------------------
// Movevars cvarhook declares
//------------------------------------------------
#define DECLARE_CVARHOOK_MOVEVARS(cvar)\
cvarhook_t cvar##_hook = { sv_movevars_hook_callback, NULL, NULL }
#define CVARHOOK_MOVEVARS(cvar)\
Cvar_HookVariable(cvar.name, &cvar##_hook);
DECLARE_CVARHOOK_MOVEVARS(sv_gravity);
DECLARE_CVARHOOK_MOVEVARS(sv_stopspeed);
DECLARE_CVARHOOK_MOVEVARS(sv_maxspeed);
DECLARE_CVARHOOK_MOVEVARS(sv_spectatormaxspeed);
DECLARE_CVARHOOK_MOVEVARS(sv_accelerate);
DECLARE_CVARHOOK_MOVEVARS(sv_airaccelerate);
DECLARE_CVARHOOK_MOVEVARS(sv_wateraccelerate);
DECLARE_CVARHOOK_MOVEVARS(sv_friction);
DECLARE_CVARHOOK_MOVEVARS(sv_edgefriction);
DECLARE_CVARHOOK_MOVEVARS(sv_waterfriction);
DECLARE_CVARHOOK_MOVEVARS(sv_bounce);
DECLARE_CVARHOOK_MOVEVARS(sv_stepsize);
DECLARE_CVARHOOK_MOVEVARS(sv_maxvelocity);
DECLARE_CVARHOOK_MOVEVARS(sv_zmax);
DECLARE_CVARHOOK_MOVEVARS(sv_wateramp);
DECLARE_CVARHOOK_MOVEVARS(sv_footsteps);
DECLARE_CVARHOOK_MOVEVARS(sv_rollangle);
DECLARE_CVARHOOK_MOVEVARS(sv_rollspeed);
DECLARE_CVARHOOK_MOVEVARS(sv_skycolor_r);
DECLARE_CVARHOOK_MOVEVARS(sv_skycolor_g);
DECLARE_CVARHOOK_MOVEVARS(sv_skycolor_b);
DECLARE_CVARHOOK_MOVEVARS(sv_skyvec_x);
DECLARE_CVARHOOK_MOVEVARS(sv_skyvec_y);
DECLARE_CVARHOOK_MOVEVARS(sv_skyvec_z);
DECLARE_CVARHOOK_MOVEVARS(sv_skyname);
cvar_t sv_skyname = { "sv_skyname", "desert", 0, 0.0f, NULL };
cvar_t mapcyclefile = { "mapcyclefile", "mapcycle.txt", 0, 0.0f, NULL };
cvar_t motdfile = { "motdfile", "motd.txt", 0, 0.0f, NULL };
@ -951,35 +988,35 @@ void SV_Multicast(edict_t *ent, vec_t *origin, int to, qboolean reliable)
host_client = save;
}
void EXT_FUNC SV_WriteMovevarsToClient(sizebuf_t *message)
void SV_WriteMovevarsToClient(sizebuf_t *message, movevars_t *movevars)
{
MSG_WriteByte(message, svc_newmovevars);
MSG_WriteFloat(message, movevars.gravity);
MSG_WriteFloat(message, movevars.stopspeed);
MSG_WriteFloat(message, movevars.maxspeed);
MSG_WriteFloat(message, movevars.spectatormaxspeed);
MSG_WriteFloat(message, movevars.accelerate);
MSG_WriteFloat(message, movevars.airaccelerate);
MSG_WriteFloat(message, movevars.wateraccelerate);
MSG_WriteFloat(message, movevars.friction);
MSG_WriteFloat(message, movevars.edgefriction);
MSG_WriteFloat(message, movevars.waterfriction);
MSG_WriteFloat(message, movevars.entgravity);
MSG_WriteFloat(message, movevars.bounce);
MSG_WriteFloat(message, movevars.stepsize);
MSG_WriteFloat(message, movevars.maxvelocity);
MSG_WriteFloat(message, movevars.zmax);
MSG_WriteFloat(message, movevars.waveHeight);
MSG_WriteByte(message, movevars.footsteps != 0);
MSG_WriteFloat(message, movevars.rollangle);
MSG_WriteFloat(message, movevars.rollspeed);
MSG_WriteFloat(message, movevars.skycolor_r);
MSG_WriteFloat(message, movevars.skycolor_g);
MSG_WriteFloat(message, movevars.skycolor_b);
MSG_WriteFloat(message, movevars.skyvec_x);
MSG_WriteFloat(message, movevars.skyvec_y);
MSG_WriteFloat(message, movevars.skyvec_z);
MSG_WriteString(message, movevars.skyName);
MSG_WriteFloat(message, movevars->gravity);
MSG_WriteFloat(message, movevars->stopspeed);
MSG_WriteFloat(message, movevars->maxspeed);
MSG_WriteFloat(message, movevars->spectatormaxspeed);
MSG_WriteFloat(message, movevars->accelerate);
MSG_WriteFloat(message, movevars->airaccelerate);
MSG_WriteFloat(message, movevars->wateraccelerate);
MSG_WriteFloat(message, movevars->friction);
MSG_WriteFloat(message, movevars->edgefriction);
MSG_WriteFloat(message, movevars->waterfriction);
MSG_WriteFloat(message, movevars->entgravity);
MSG_WriteFloat(message, movevars->bounce);
MSG_WriteFloat(message, movevars->stepsize);
MSG_WriteFloat(message, movevars->maxvelocity);
MSG_WriteFloat(message, movevars->zmax);
MSG_WriteFloat(message, movevars->waveHeight);
MSG_WriteByte(message, movevars->footsteps != 0);
MSG_WriteFloat(message, movevars->rollangle);
MSG_WriteFloat(message, movevars->rollspeed);
MSG_WriteFloat(message, movevars->skycolor_r);
MSG_WriteFloat(message, movevars->skycolor_g);
MSG_WriteFloat(message, movevars->skycolor_b);
MSG_WriteFloat(message, movevars->skyvec_x);
MSG_WriteFloat(message, movevars->skyvec_y);
MSG_WriteFloat(message, movevars->skyvec_z);
MSG_WriteString(message, movevars->skyName);
}
void EXT_FUNC SV_WriteDeltaDescriptionsToClient(sizebuf_t *msg)
@ -1008,76 +1045,41 @@ void EXT_FUNC SV_WriteDeltaDescriptionsToClient(sizebuf_t *msg)
}
}
void EXT_FUNC SV_SetMoveVars(void)
void sv_movevars_hook_callback(cvar_t *cvar)
{
movevars.entgravity = 1.0f;
movevars.gravity = sv_gravity.value;
movevars.stopspeed = sv_stopspeed.value;
movevars.maxspeed = sv_maxspeed.value;
movevars.spectatormaxspeed = sv_spectatormaxspeed.value;
movevars.accelerate = sv_accelerate.value;
movevars.airaccelerate = sv_airaccelerate.value;
movevars.wateraccelerate = sv_wateraccelerate.value;
movevars.friction = sv_friction.value;
movevars.edgefriction = sv_edgefriction.value;
movevars.waterfriction = sv_waterfriction.value;
movevars.bounce = sv_bounce.value;
movevars.stepsize = sv_stepsize.value;
movevars.maxvelocity = sv_maxvelocity.value;
movevars.zmax = sv_zmax.value;
movevars.waveHeight = sv_wateramp.value;
movevars.footsteps = sv_footsteps.value;
movevars.rollangle = sv_rollangle.value;
movevars.rollspeed = sv_rollspeed.value;
movevars.skycolor_r = sv_skycolor_r.value;
movevars.skycolor_g = sv_skycolor_g.value;
movevars.skycolor_b = sv_skycolor_b.value;
movevars.skyvec_x = sv_skyvec_x.value;
movevars.skyvec_y = sv_skyvec_y.value;
movevars.skyvec_z = sv_skyvec_z.value;
Q_strncpy(movevars.skyName, sv_skyname.string, sizeof(movevars.skyName) - 1);
movevars.skyName[sizeof(movevars.skyName) - 1] = 0;
SV_SetMoveVars(&sv_movevars);
}
void SV_QueryMovevarsChanged(void)
void SV_SetMoveVars(movevars_t *movevars)
{
if (movevars.entgravity != 1.0f
|| sv_maxspeed.value != movevars.maxspeed
|| sv_gravity.value != movevars.gravity
|| sv_stopspeed.value != movevars.stopspeed
|| sv_spectatormaxspeed.value != movevars.spectatormaxspeed
|| sv_accelerate.value != movevars.accelerate
|| sv_airaccelerate.value != movevars.airaccelerate
|| sv_wateraccelerate.value != movevars.wateraccelerate
|| sv_friction.value != movevars.friction
|| sv_edgefriction.value != movevars.edgefriction
|| sv_waterfriction.value != movevars.waterfriction
|| sv_bounce.value != movevars.bounce
|| sv_stepsize.value != movevars.stepsize
|| sv_maxvelocity.value != movevars.maxvelocity
|| sv_zmax.value != movevars.zmax
|| sv_wateramp.value != movevars.waveHeight
|| sv_footsteps.value != movevars.footsteps
|| sv_rollangle.value != movevars.rollangle
|| sv_rollspeed.value != movevars.rollspeed
|| sv_skycolor_r.value != movevars.skycolor_r
|| sv_skycolor_g.value != movevars.skycolor_g
|| sv_skycolor_b.value != movevars.skycolor_b
|| sv_skyvec_x.value != movevars.skyvec_x
|| sv_skyvec_y.value != movevars.skyvec_y
|| sv_skyvec_z.value != movevars.skyvec_z
|| Q_strcmp(sv_skyname.string, movevars.skyName))
{
SV_SetMoveVars();
movevars->entgravity = 1.0f;
movevars->gravity = sv_gravity.value;
movevars->stopspeed = sv_stopspeed.value;
movevars->maxspeed = sv_maxspeed.value;
movevars->spectatormaxspeed = sv_spectatormaxspeed.value;
movevars->accelerate = sv_accelerate.value;
movevars->airaccelerate = sv_airaccelerate.value;
movevars->wateraccelerate = sv_wateraccelerate.value;
movevars->friction = sv_friction.value;
movevars->edgefriction = sv_edgefriction.value;
movevars->waterfriction = sv_waterfriction.value;
movevars->bounce = sv_bounce.value;
movevars->stepsize = sv_stepsize.value;
movevars->maxvelocity = sv_maxvelocity.value;
movevars->zmax = sv_zmax.value;
movevars->waveHeight = sv_wateramp.value;
movevars->footsteps = sv_footsteps.value;
movevars->rollangle = sv_rollangle.value;
movevars->rollspeed = sv_rollspeed.value;
movevars->skycolor_r = sv_skycolor_r.value;
movevars->skycolor_g = sv_skycolor_g.value;
movevars->skycolor_b = sv_skycolor_b.value;
movevars->skyvec_x = sv_skyvec_x.value;
movevars->skyvec_y = sv_skyvec_y.value;
movevars->skyvec_z = sv_skyvec_z.value;
client_t *cl = g_psvs.clients;
for (int i = 0; i < g_psvs.maxclients; i++, cl++)
{
if (!cl->fakeclient && (cl->active || cl->spawned || cl->connected))
SV_WriteMovevarsToClient(&cl->netchan.message);
}
}
Q_strncpy(movevars->skyName, sv_skyname.string, sizeof(movevars->skyName) - 1);
movevars->skyName[sizeof(movevars->skyName) - 1] = 0;
}
void EXT_FUNC SV_SendServerinfo_mod(sizebuf_t *msg, IGameClient* cl)
@ -1184,8 +1186,8 @@ void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client)
MSG_WriteByte(msg, sv_cheats.value != 0);
SV_WriteDeltaDescriptionsToClient(msg);
SV_SetMoveVars();
SV_WriteMovevarsToClient(msg);
SV_SetMoveVars(&sv_movevars);
SV_WriteMovevarsToClient(msg, &sv_movevars);
MSG_WriteByte(msg, svc_cdtrack);
MSG_WriteByte(msg, gGlobalVariables.cdAudioTrack);
@ -1196,6 +1198,7 @@ void SV_SendServerinfo_internal(sizebuf_t *msg, client_t *client)
client->spawned = FALSE;
client->connected = TRUE;
client->fully_connected = FALSE;
client->movevars = sv_movevars;
}
void SV_SendResources(sizebuf_t *msg)
@ -5778,8 +5781,12 @@ void SV_PropagateCustomizations(void)
pResource = &pCust->resource;
#ifdef REHLDS_FIXES
// skip logos if sv_send_logos is 0
if ((pResource->ucFlags & RES_CUSTOM) && !sv_send_logos.value)
{
pCust = pCust->pNext;
continue;
}
#endif
MSG_WriteByte(&host_client->netchan.message, svc_customization);
@ -6073,7 +6080,7 @@ void PrecacheModelSounds(studiohdr_t *pStudioHeader)
void PrecacheModelSpecifiedFiles()
{
const char **s = &g_psv.model_precache[1];
for (size_t i = 1; i < ARRAYSIZE(g_psv.model_precache) && *s != nullptr; i++, s++)
for (size_t i = 1; i < ARRAYSIZE(g_psv.model_precache) && *s && g_psv.models[i]; i++, s++)
{
if (g_psv.models[i]->type != mod_studio)
continue;
@ -6512,7 +6519,7 @@ int SV_SpawnServer(qboolean bIsDemo, char *server, char *startspot)
gGlobalVariables.serverflags = g_psvs.serverflags;
gGlobalVariables.mapname = (size_t)g_psv.name - (size_t)pr_strings;
gGlobalVariables.startspot = (size_t)g_psv.startspot - (size_t)pr_strings;
SV_SetMoveVars();
SV_SetMoveVars(&sv_movevars);
return 1;
}
@ -8091,7 +8098,6 @@ void EXT_FUNC SV_Frame_Internal()
SV_Physics();
g_psv.time += host_frametime;
}
SV_QueryMovevarsChanged();
SV_RequestMissingResourcesFromClients();
SV_CheckTimeouts();
SV_SendClientMessages();
@ -8336,6 +8342,35 @@ void SV_Init(void)
Cvar_RegisterVariable(&sv_usercmd_custom_random_seed);
#endif
//------------------------------------------------
// Movevars cvarhook registers
//------------------------------------------------
CVARHOOK_MOVEVARS(sv_gravity);
CVARHOOK_MOVEVARS(sv_stopspeed);
CVARHOOK_MOVEVARS(sv_maxspeed);
CVARHOOK_MOVEVARS(sv_spectatormaxspeed);
CVARHOOK_MOVEVARS(sv_accelerate);
CVARHOOK_MOVEVARS(sv_airaccelerate);
CVARHOOK_MOVEVARS(sv_wateraccelerate);
CVARHOOK_MOVEVARS(sv_friction);
CVARHOOK_MOVEVARS(sv_edgefriction);
CVARHOOK_MOVEVARS(sv_waterfriction);
CVARHOOK_MOVEVARS(sv_bounce);
CVARHOOK_MOVEVARS(sv_stepsize);
CVARHOOK_MOVEVARS(sv_maxvelocity);
CVARHOOK_MOVEVARS(sv_zmax);
CVARHOOK_MOVEVARS(sv_wateramp);
CVARHOOK_MOVEVARS(sv_footsteps);
CVARHOOK_MOVEVARS(sv_rollangle);
CVARHOOK_MOVEVARS(sv_rollspeed);
CVARHOOK_MOVEVARS(sv_skycolor_r);
CVARHOOK_MOVEVARS(sv_skycolor_g);
CVARHOOK_MOVEVARS(sv_skycolor_b);
CVARHOOK_MOVEVARS(sv_skyvec_x);
CVARHOOK_MOVEVARS(sv_skyvec_y);
CVARHOOK_MOVEVARS(sv_skyvec_z);
CVARHOOK_MOVEVARS(sv_skyname);
for (int i = 0; i < MAX_MODELS; i++)
{
Q_snprintf(localmodels[i], sizeof(localmodels[i]), "*%i", i);

View File

@ -1491,28 +1491,7 @@ void SV_Physics()
if (i > 0 && i <= g_psvs.maxclients)
continue;
if (ent->v.flags & FL_ONGROUND)
{
edict_t *groundentity = ent->v.groundentity;
if (groundentity && (groundentity->v.flags & FL_CONVEYOR))
{
if (ent->v.flags & FL_BASEVELOCITY)
VectorMA(ent->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, ent->v.basevelocity);
else
VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity);
ent->v.flags |= FL_BASEVELOCITY;
}
}
if (!(ent->v.flags & FL_BASEVELOCITY))
{
// Apply momentum (add in half of the previous frame of velocity first)
VectorMA(ent->v.velocity, (host_frametime * 0.5f + 1.0f), ent->v.basevelocity, ent->v.velocity);
VectorClear(ent->v.basevelocity);
}
ent->v.flags &= ~FL_BASEVELOCITY;
SV_CheckMovingGround(ent, host_frametime);
switch (ent->v.movetype)
{

View File

@ -686,35 +686,33 @@ qboolean SV_PlayerRunThink(edict_t *ent, float frametime, double clienttimebase)
return ent->free == 0;
}
void SV_CheckMovingGround(edict_t *player, float frametime)
void SV_CheckMovingGround(edict_t *ent, float frametime)
{
edict_t *groundentity;
if (player->v.flags & FL_ONGROUND)
if (ent->v.flags & FL_ONGROUND)
{
groundentity = player->v.groundentity;
groundentity = ent->v.groundentity;
if (groundentity)
{
if (groundentity->v.flags & FL_CONVEYOR)
{
if (player->v.flags & FL_BASEVELOCITY)
VectorMA(player->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, player->v.basevelocity);
if (ent->v.flags & FL_BASEVELOCITY)
VectorMA(ent->v.basevelocity, groundentity->v.speed, groundentity->v.movedir, ent->v.basevelocity);
else
VectorScale(groundentity->v.movedir, groundentity->v.speed, player->v.basevelocity);
player->v.flags |= FL_BASEVELOCITY;
VectorScale(groundentity->v.movedir, groundentity->v.speed, ent->v.basevelocity);
ent->v.flags |= FL_BASEVELOCITY;
}
}
}
if (!(player->v.flags & FL_BASEVELOCITY))
if (!(ent->v.flags & FL_BASEVELOCITY))
{
VectorMA(player->v.velocity, frametime * 0.5f + 1.0f, player->v.basevelocity, player->v.velocity);
player->v.basevelocity[0] = 0;
player->v.basevelocity[1] = 0;
player->v.basevelocity[2] = 0;
VectorMA(ent->v.velocity, frametime * 0.5f + 1.0f, ent->v.basevelocity, ent->v.velocity);
VectorClear(ent->v.basevelocity);
}
player->v.flags &= ~FL_BASEVELOCITY;
ent->v.flags &= ~FL_BASEVELOCITY;
}
void SV_ConvertPMTrace(trace_t *dest, pmtrace_t *src, edict_t *ent)
@ -931,9 +929,17 @@ void SV_RunCmd(usercmd_t *ucmd, int random_seed)
pmove->PM_PlaySound = PM_SV_PlaySound;
pmove->PM_TraceTexture = PM_SV_TraceTexture;
pmove->PM_PlaybackEventFull = PM_SV_PlaybackEventFull;
pmove->movevars = &host_client->movevars;
const movevars_t movevars = *pmove->movevars; // preserve current movevars
host_client->movevars = sv_movevars; // always use global movevars as a base
gEntityInterface.pfnPM_Move(pmove, TRUE);
// Determine whether movevars has changed or not
if (!host_client->fakeclient && Q_memcmp(&movevars, pmove->movevars, sizeof(movevars)) != 0)
SV_WriteMovevarsToClient(&host_client->netchan.message, pmove->movevars); // sync movevars for the client
sv_player->v.deadflag = pmove->deadflag;
sv_player->v.effects = pmove->effects;
sv_player->v.teleport_time = pmove->waterjumptime;

View File

@ -184,7 +184,7 @@ typedef struct playermove_s
char physinfo[ MAX_PHYSINFO_STRING ]; // Physics info string
struct movevars_s *_movevars;
struct movevars_s *movevars;
vec3_t player_mins[MAX_MAP_HULLS];
vec3_t player_maxs[MAX_MAP_HULLS];

View File

@ -458,6 +458,16 @@ void EXT_FUNC RemoveCvarListener_api(const char *var_name, cvar_callback_t func)
}
}
void EXT_FUNC SV_SetMoveVars_api()
{
SV_SetMoveVars(&sv_movevars);
}
void EXT_FUNC SV_WriteMovevarsToClient_api(sizebuf_t *message)
{
SV_WriteMovevarsToClient(message, &sv_movevars);
}
CRehldsServerStatic g_RehldsServerStatic;
CRehldsServerData g_RehldsServerData;
CRehldsHookchains g_RehldsHookchains;
@ -475,8 +485,8 @@ RehldsFuncs_t g_RehldsApiFuncs =
&SV_CheckChallenge_api,
&SV_SendUserReg,
&SV_WriteDeltaDescriptionsToClient,
&SV_SetMoveVars,
&SV_WriteMovevarsToClient,
&SV_SetMoveVars_api,
&SV_WriteMovevarsToClient_api,
&GetClientFallback_api,
&GetAllowCheats_api,
&GSBSecure_api,
@ -569,7 +579,7 @@ bool EXT_FUNC SV_EmitSound2_internal(edict_t *entity, IGameClient *pReceiver, in
bool bSendPAS = (channel != CHAN_STATIC && !(flags & SND_FL_STOP) && !(emitFlags & SND_EMIT2_NOPAS));
vec3_t origin = {0, 0, 0};
if (entity && entity != g_psv.edicts)
if ((!pOrigin || !(emitFlags & SND_EMIT2_USE_ORIGIN)) && entity && entity != g_psv.edicts)
{
for (int i = 0; i < 3; ++i)
origin[i] = (entity->v.maxs[i] + entity->v.mins[i]) * 0.5f + entity->v.origin[i];

View File

@ -530,6 +530,15 @@ netchan_t* EXT_FUNC CNetChan::GetChan()
return m_pNetChan;
}
void CNetChan::Clear()
{
for (int i = 0; i < MAX_STREAMS; i++)
{
for (int j = 0; j < NET_DECOMPRESS_MAX_TIMES; j++)
m_FragStats[i].decompress_failure_times[j] = 0;
m_FragStats[i].num_decompress_failures = 0;
}
}
int EXT_FUNC CRehldsServerStatic::GetMaxClients()

View File

@ -30,6 +30,16 @@
#include "rehlds_interfaces.h"
#include "server.h"
const int NET_DECOMPRESS_MAX_TIMES = 10;
struct FragStats_t
{
float decompress_failure_times[NET_DECOMPRESS_MAX_TIMES];
// Count of abnormal fragment decompressions in a time window
int num_decompress_failures;
};
class CNetChan : public INetChan
{
private:
@ -38,6 +48,10 @@ private:
#ifdef REHLDS_FIXES
uint8_t m_messageBuffer[NET_MAX_PAYLOAD];
#endif
// Stats for decompression of incoming fragments
FragStats_t m_FragStats[MAX_STREAMS];
public:
CNetChan(netchan_t* chan);
@ -46,10 +60,13 @@ public:
virtual netchan_t* GetChan();
void Clear();
public:
#ifdef REHLDS_FIXES
uint8_t* GetExtendedMessageBuffer() { return m_messageBuffer; };
#endif
FragStats_t &GetFragStats(int stream) { return m_FragStats[stream]; };
};
@ -247,6 +264,9 @@ public:
uint8_t* GetExtendedMessageBuffer() { return m_NetChan.GetExtendedMessageBuffer(); };
#endif
void NetchanClear() { m_NetChan.Clear(); }
FragStats_t &GetFragStats(int stream) { return m_NetChan.GetFragStats(stream); };
#ifdef REHLDS_FIXES
void SetupLocalGameTime() { m_localGameTimeBase = g_psv.time; }
double GetLocalGameTime() const { return g_psv.time - m_localGameTimeBase; }

View File

@ -213,13 +213,13 @@ private:
void setCopybackBuffer(sizebuf_t *pbuf);
// Adds a parameter to the message
void addParam(IMessage::ParamType type, size_t length);
void addParam(IMessage::ParamType type, bool sign, size_t length);
// Clears the message after execution
void clear();
template <typename T>
void setParamPrimitive(size_t index, T value);
void setParamPrimitive(size_t index, T value, bool sign = false);
// Transforms buffer after sets string for a parameter at the given index
void setTxformBuffer(size_t index, size_t startPos, size_t oldLength, size_t newLength);
@ -263,6 +263,7 @@ private:
size_t posFront : 9; // The stock position of the parameter in the buffer
size_t oldlen : 9; // The length of the parameter in the buffer
size_t newlen : 9; // The length of the parameter in the buffer
bool sign[MAX_STORAGE]; // Flag indicating that it has a signed value
};
#pragma pack(pop)
@ -369,17 +370,31 @@ static size_t SIZEOF_PARAMTYPE[] =
};
// Adds a parameter to the message
void MessageImpl::addParam(IMessage::ParamType type, size_t length)
void MessageImpl::addParam(IMessage::ParamType type, bool sign, size_t length)
{
Param_t &param = m_params[m_paramCount++];
param.type = type;
param.newlen = param.oldlen = (length == -1) ? SIZEOF_PARAMTYPE[static_cast<size_t>(type)] : length;
param.posBack = param.posFront = gMsgBuffer.cursize;
param.type = type;
param.newlen = param.oldlen = (length == -1) ? SIZEOF_PARAMTYPE[static_cast<size_t>(type)] : length;
param.posBack = param.posFront = gMsgBuffer.cursize;
param.sign[BACK] = param.sign[FRONT] = sign;
}
template <typename T>
inline void setValue(void *pbuf, T value)
{
*static_cast<T *>(pbuf) = value;
}
template <typename T>
inline int getValueInt(const void *pbuf, bool sign = false)
{
return sign ?
*static_cast<const T *>(pbuf) : *static_cast<std::make_unsigned_t<const T> *>(pbuf);
}
// Sets the value of a primitive parameter at the given index
template <typename T>
void MessageImpl::setParamPrimitive(size_t index, T value)
void MessageImpl::setParamPrimitive(size_t index, T value, bool sign)
{
// Ensure index is within bounds
if (index >= m_paramCount)
@ -392,30 +407,32 @@ void MessageImpl::setParamPrimitive(size_t index, T value)
switch (param.type)
{
case IMessage::ParamType::Byte:
*(uint8 *)pbuf = value;
setValue<uint8>(pbuf, value);
break;
case IMessage::ParamType::Char:
*(int8 *)pbuf = value;
setValue<int8>(pbuf, value);
break;
case IMessage::ParamType::Short:
case IMessage::ParamType::Entity:
*(int16 *)pbuf = value;
setValue<int16>(pbuf, value);
break;
case IMessage::ParamType::Long:
*(uint32 *)pbuf = value;
setValue<uint32>(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;
setValue<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);
setValue<int16>(pbuf, (int)(value * 8.0));
break;
default:
return; // bad type
}
param.sign[BACK] = sign;
// Mark message as modified
param.modified = true;
@ -469,14 +486,14 @@ int MessageImpl::getParamInt(size_t index) const
switch (param.type)
{
case IMessage::ParamType::Byte:
return *(uint8 *)buf;
return getValueInt<uint8>(buf);
case IMessage::ParamType::Char:
return *(int8 *)buf;
return getValueInt<int8>(buf, param.sign[BACK]);
case IMessage::ParamType::Short:
case IMessage::ParamType::Entity:
return *(int16 *)buf;
return getValueInt<int16>(buf, param.sign[BACK]);
case IMessage::ParamType::Long:
return *(uint32 *)buf;
return getValueInt<uint32>(buf);
default:
return 0; // bad type
}
@ -495,9 +512,9 @@ float MessageImpl::getParamFloat(size_t index) const
switch (param.type)
{
case IMessage::ParamType::Angle:
return (float)(*(uint8 *)buf * (360.0 / 256.0));
return (float)((uint8)getValueInt<uint8>(buf) * (360.0 / 256.0));
case IMessage::ParamType::Coord:
return (float)(*(int16 *)buf * (1.0 / 8));
return (float)((int16)getValueInt<int16>(buf) * (1.0 / 8));
default:
break; // bad type
}
@ -532,14 +549,14 @@ int MessageImpl::getOriginalParamInt(size_t index) const
switch (param.type)
{
case IMessage::ParamType::Byte:
return *(uint8 *)buf;
return getValueInt<uint8>(buf);
case IMessage::ParamType::Char:
return *(int8 *)buf;
return getValueInt<int8>(buf, param.sign[FRONT]);
case IMessage::ParamType::Short:
case IMessage::ParamType::Entity:
return *(int16 *)buf;
return getValueInt<int16>(buf, param.sign[FRONT]);
case IMessage::ParamType::Long:
return *(uint32 *)buf;
return getValueInt<uint32>(buf);
default:
return 0; // bad type
}
@ -557,9 +574,9 @@ float MessageImpl::getOriginalParamFloat(size_t index) const
switch (param.type)
{
case IMessage::ParamType::Angle:
return (float)(*(uint8 *)buf * (360.0 / 256.0));
return (float)((uint8)getValueInt<uint8>(buf) * (360.0 / 256.0));
case IMessage::ParamType::Coord:
return (float)(*(int16 *)buf * (1.0 / 8));
return (float)((int16)getValueInt<int16>(buf) * (1.0 / 8));
default:
break; // bad type
}
@ -584,7 +601,7 @@ const char *MessageImpl::getOriginalParamString(size_t index) const
// Sets the integer value of the parameter at the given index
void MessageImpl::setParamInt(size_t index, int value)
{
setParamPrimitive(index, value);
setParamPrimitive(index, value, value < 0);
}
// Sets the float value of the parameter at the given index
@ -710,23 +727,23 @@ void MessageImpl::resetParam(size_t index)
switch (param.type)
{
case IMessage::ParamType::Byte:
*(uint8 *)pbackbuf = *(uint8 *)pfrontbuf;
setValue<uint8>(pbackbuf, getValueInt<uint8>(pfrontbuf));
break;
case IMessage::ParamType::Char:
*(int8 *)pbackbuf = *(int8 *)pfrontbuf;
setValue<int8>(pbackbuf, getValueInt<int8>(pfrontbuf));
break;
case IMessage::ParamType::Short:
case IMessage::ParamType::Entity:
*(int16 *)pbackbuf = *(int16 *)pfrontbuf;
setValue<int16>(pbackbuf, getValueInt<int16>(pfrontbuf));
break;
case IMessage::ParamType::Long:
*(uint32 *)pbackbuf = *(uint32 *)pfrontbuf;
setValue<uint32>(pbackbuf, getValueInt<uint32>(pfrontbuf));
break;
case IMessage::ParamType::Angle:
*(uint8 *)pbackbuf = *(uint8 *)pfrontbuf;
setValue<uint8>(pbackbuf, getValueInt<uint8>(pfrontbuf));
break;
case IMessage::ParamType::Coord:
*(int16 *)pbackbuf = *(int16 *)pfrontbuf;
setValue<int16>(pbackbuf, getValueInt<int16>(pfrontbuf));
break;
case IMessage::ParamType::String:
// Return the original string value from the front buffer
@ -740,6 +757,8 @@ void MessageImpl::resetParam(size_t index)
// Unmark message as modified
param.modified = false;
param.sign[BACK] = param.sign[FRONT];
}
// Resets a specific message parameter to its original value
@ -955,7 +974,7 @@ bool MessageManagerImpl::MessageEnd()
return false;
}
bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length)
bool MessageManagerImpl::WriteParam(IMessage::ParamType type, bool sign, size_t length)
{
// Check if in block mode
if (m_inblock)
@ -966,7 +985,7 @@ bool MessageManagerImpl::WriteParam(IMessage::ParamType type, size_t length)
{
// Add parameter to top stack message
MessageImpl &msg = m_stack.top();
msg.addParam(type, length);
msg.addParam(type, sign, length);
}
return true;
@ -1002,13 +1021,13 @@ void EXT_FUNC PF_WriteByte_Intercept(int iValue)
void EXT_FUNC PF_WriteChar_Intercept(int iValue)
{
if (MessageManager().WriteParam(IMessage::ParamType::Char))
if (MessageManager().WriteParam(IMessage::ParamType::Char, iValue < 0))
PF_WriteChar_I(iValue);
}
void EXT_FUNC PF_WriteShort_Intercept(int iValue)
{
if (MessageManager().WriteParam(IMessage::ParamType::Short))
if (MessageManager().WriteParam(IMessage::ParamType::Short, iValue < 0))
PF_WriteShort_I(iValue);
}
@ -1032,13 +1051,13 @@ void EXT_FUNC PF_WriteCoord_Intercept(float flValue)
void EXT_FUNC PF_WriteString_Intercept(const char *sz)
{
if (MessageManager().WriteParam(IMessage::ParamType::String, sz ? Q_strlen(sz) + 1 : 1))
if (MessageManager().WriteParam(IMessage::ParamType::String, false, sz ? Q_strlen(sz) + 1 : 1))
PF_WriteString_I(sz);
}
void EXT_FUNC PF_WriteEntity_Intercept(int iValue)
{
if (MessageManager().WriteParam(IMessage::ParamType::Entity))
if (MessageManager().WriteParam(IMessage::ParamType::Entity, iValue < 0))
PF_WriteEntity_I(iValue);
}

View File

@ -93,7 +93,7 @@ private:
bool MessageEnd();
private:
bool WriteParam(IMessage::ParamType type, size_t length = -1);
bool WriteParam(IMessage::ParamType type, bool sign = false, size_t length = -1);
bool m_inblock; // Flag indicating whether a message block is currently active
bool m_inhook; // Flag indicating whether a message hook is currently active

View File

@ -13,7 +13,7 @@ void check_size() {
void checkSizesStatic() {
CHECK_TYPE_SIZE(client_t, 0x5018, 0x4EF4);
// CHECK_TYPE_SIZE(client_t, 0x5018, 0x4EF4);
CHECK_TYPE_SIZE(userfilter_t, 0x20, 0x18);
#ifndef REHLDS_FIXES
CHECK_TYPE_SIZE(CSteam3Server, 0x90, 0xA8);