From 4d87e215d377c218267df272ac5e2495bd5c721f Mon Sep 17 00:00:00 2001 From: Asmodai Date: Sat, 17 Feb 2018 01:45:10 +0300 Subject: [PATCH 1/7] Add fragments validation to hltv --- rehlds/HLTV/Proxy/build.gradle | 6 +++++ rehlds/HLTV/common/NetChannel.cpp | 43 +++++++++++++++++++++++++++++++ rehlds/HLTV/common/NetChannel.h | 12 +++++++++ 3 files changed, 61 insertions(+) diff --git a/rehlds/HLTV/Proxy/build.gradle b/rehlds/HLTV/Proxy/build.gradle index 89778c5..dc191a0 100644 --- a/rehlds/HLTV/Proxy/build.gradle +++ b/rehlds/HLTV/Proxy/build.gradle @@ -16,6 +16,8 @@ project.ext.dep_bzip2 = project(':dep/bzip2') void setupToolchain(NativeBinarySpec b) { boolean useGcc = project.hasProperty("useGcc") + boolean rehltvFixes = b.flavor.name.contains('rehltvFixes') + def cfg = rootProject.createToolchainConfig(b); cfg.projectInclude(project, '/..', '/../..', '/src', '/../Director/src', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); cfg.projectInclude(dep_bzip2, '/include') @@ -64,6 +66,10 @@ void setupToolchain(NativeBinarySpec b) { cfg.extraLibs "steam_api" } + if (rehltvFixes) { + cfg.singleDefines 'REHLTV_FIXES', 'REHLTV_CHECKS' + } + ToolchainConfigUtils.apply(project, cfg, b); } diff --git a/rehlds/HLTV/common/NetChannel.cpp b/rehlds/HLTV/common/NetChannel.cpp index ac1240a..34cc85d 100644 --- a/rehlds/HLTV/common/NetChannel.cpp +++ b/rehlds/HLTV/common/NetChannel.cpp @@ -653,6 +653,44 @@ bool NetChannel::CheckForCompletion(int stream, int intotalbuffers) return false; } +#ifdef REHLTV_FIXES +bool NetChannel::ValidateFragments(int pkt_size, qboolean *frag_message, unsigned int *fragid, int *frag_offset, int *frag_length) +{ + for (int i = 0; i < MAX_STREAMS; i++) + { + if (!frag_message[i]) + continue; + + // total fragments should be <= MAX_FRAGMENTS and current fragment can't be > total fragments + if (i == FRAG_NORMAL_STREAM && FRAG_GETCOUNT(fragid[i]) > MAX_NORMAL_FRAGMENTS) + return false; + if (i == FRAG_FILE_STREAM && FRAG_GETCOUNT(fragid[i]) > MAX_FILE_FRAGMENTS) + return false; + if (FRAG_GETID(fragid[i]) > FRAG_GETCOUNT(fragid[i])) + return false; + if (!frag_length[i]) + return false; + if ((size_t)frag_length[i] > FRAGMENT_MAX_SIZE || (size_t)frag_offset[i] > MAX_POSSIBLE_MSG - 1) + return false; + + int frag_end = frag_offset[i] + frag_length[i]; + + // end of fragment is out of the packet + if (frag_end + msg_readcount > pkt_size) + return false; + + // fragment overlaps next stream's fragment or placed after it + for (int j = i + 1; j < MAX_STREAMS; j++) + { + if (frag_end > frag_offset[j] && frag_message[j]) // don't add msg_readcount for comparison + return false; + } + } + + return true; +} +#endif // REHLTV_FIXES + void NetChannel::ProcessIncoming(unsigned char *data, int size) { BitBuffer message(data, size); @@ -721,6 +759,11 @@ void NetChannel::ProcessIncoming(unsigned char *data, int size) frag_length[i] = message.ReadShort(); } } + +#ifdef REHLTV_FIXES + if (!ValidateFragments(size, frag_message, fragid, frag_offset, frag_length)) + return; +#endif } sequence &= ~(1 << 31); diff --git a/rehlds/HLTV/common/NetChannel.h b/rehlds/HLTV/common/NetChannel.h index 8c396e3..18b00f9 100644 --- a/rehlds/HLTV/common/NetChannel.h +++ b/rehlds/HLTV/common/NetChannel.h @@ -53,6 +53,15 @@ enum #define MAX_LATENT 32 #define FRAGMENT_MAX_SIZE 1400 // Size of fragmentation buffer internal buffers +#define CLIENT_FRAGMENT_SIZE_ONCONNECT 128 +#define CUSTOMIZATION_MAX_SIZE 20480 + +// Client sends normal fragments only while connecting +#define MAX_NORMAL_FRAGMENTS (MAX_POSSIBLE_MSG / CLIENT_FRAGMENT_SIZE_ONCONNECT) + +// While client is connecting it sending fragments with minimal size, also it transfers sprays with minimal fragments... +// But with sv_delayed_spray_upload it sends with cl_dlmax fragment size +#define MAX_FILE_FRAGMENTS (CUSTOMIZATION_MAX_SIZE / FRAGMENT_C2S_MIN_SIZE) #define UDP_HEADER_SIZE 28 #define MAX_RELIABLE_PAYLOAD 1200 @@ -152,6 +161,9 @@ public: fragbuf_t *fragbufs; // The actual buffers } fragbufwaiting_t; +#ifdef REHLTV_FIXES + bool ValidateFragments(int pkt_size, qboolean *frag_message, unsigned int *fragid, int *frag_offset, int *frag_length); +#endif bool CreateFragmentsFromFile(char *fileName); bool CopyFileFragments(); void GetFlowStats(float *avgInKBSec, float *avgOutKBSec); From 7cfae98e81337a85939444437fa21a94e1f2b2b9 Mon Sep 17 00:00:00 2001 From: s1lent Date: Thu, 22 Feb 2018 17:31:28 +0700 Subject: [PATCH 2/7] Reworked NetChannel::ValidateFragments Added macros HLTV_FIXES Minor refactoring BitBuffer.cpp and cosmetic changes --- rehlds/HLTV/Console/msvc/Console.vcxproj | 4 +- rehlds/HLTV/Core/build.gradle | 2 +- rehlds/HLTV/Core/msvc/Core.vcxproj | 4 +- rehlds/HLTV/Core/src/NetSocket.cpp | 41 ++++++- rehlds/HLTV/Core/src/World.h | 8 +- rehlds/HLTV/DemoPlayer/build.gradle | 2 +- .../HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj | 4 +- rehlds/HLTV/Director/build.gradle | 2 +- rehlds/HLTV/Director/msvc/Director.vcxproj | 4 +- rehlds/HLTV/Proxy/build.gradle | 7 +- rehlds/HLTV/Proxy/msvc/Proxy.vcxproj | 4 +- rehlds/HLTV/common/BitBuffer.cpp | 114 +++++++++--------- rehlds/HLTV/common/BitBuffer.h | 3 +- rehlds/HLTV/common/NetChannel.cpp | 48 ++++++-- rehlds/HLTV/common/NetChannel.h | 84 +++++++------ 15 files changed, 196 insertions(+), 135 deletions(-) diff --git a/rehlds/HLTV/Console/msvc/Console.vcxproj b/rehlds/HLTV/Console/msvc/Console.vcxproj index 8661108..bb8326e 100644 --- a/rehlds/HLTV/Console/msvc/Console.vcxproj +++ b/rehlds/HLTV/Console/msvc/Console.vcxproj @@ -159,7 +159,7 @@ Use Level3 Disabled - HLTV;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;%(AdditionalIncludeDirectories) precompiled.h MultiThreadedDebug @@ -190,7 +190,7 @@ Disabled true true - HLTV;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;%(AdditionalIncludeDirectories) precompiled.h MultiThreaded diff --git a/rehlds/HLTV/Core/build.gradle b/rehlds/HLTV/Core/build.gradle index c89287d..0f3c129 100644 --- a/rehlds/HLTV/Core/build.gradle +++ b/rehlds/HLTV/Core/build.gradle @@ -20,7 +20,7 @@ void setupToolchain(NativeBinarySpec b) { cfg.projectInclude(project, '/..', '/../..', '/src', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); cfg.projectInclude(dep_bzip2, '/include') - cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'CORE_MODULE' + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'HLTV_FIXES', 'CORE_MODULE' if (cfg instanceof MsvcToolchainConfig) { cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( diff --git a/rehlds/HLTV/Core/msvc/Core.vcxproj b/rehlds/HLTV/Core/msvc/Core.vcxproj index aca1150..d9f36a3 100644 --- a/rehlds/HLTV/Core/msvc/Core.vcxproj +++ b/rehlds/HLTV/Core/msvc/Core.vcxproj @@ -77,7 +77,7 @@ Use Level3 Disabled - HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;_DEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) @@ -112,7 +112,7 @@ MaxSpeed true true - HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;NDEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) diff --git a/rehlds/HLTV/Core/src/NetSocket.cpp b/rehlds/HLTV/Core/src/NetSocket.cpp index 263dc73..748d928 100644 --- a/rehlds/HLTV/Core/src/NetSocket.cpp +++ b/rehlds/HLTV/Core/src/NetSocket.cpp @@ -297,6 +297,14 @@ int NetSocket::GetLong(unsigned char *pData, int size) { m_NetSplitPacket.currentSequence = sequenceNumber; m_NetSplitPacket.splitCount = packetCount; + +#ifdef HLTV_FIXES + m_NetSplitPacket.totalSize = 0; + + // clear part's sequence + for (int i = 0; i < MAX_SPLIT_FRAGMENTS; i++) + netSplitFlags[i] = -1; +#endif } unsigned int packetPayloadSize = size - sizeof(SPLITPACKET); @@ -310,16 +318,47 @@ int NetSocket::GetLong(unsigned char *pData, int size) m_NetSplitPacket.totalSize = packetPayloadSize + SPLIT_SIZE * (packetCount - 1); } - --m_NetSplitPacket.splitCount; + m_NetSplitPacket.splitCount--; netSplitFlags[packetNumber] = sequenceNumber; + +#ifdef HLTV_FIXES + if (SPLIT_SIZE * packetNumber + packetPayloadSize > MAX_UDP_PACKET) + { + m_System->DPrintf("WARNING! NetSocket::GetLong: Malformed packet size (%i, %i)\n", SPLIT_SIZE * packetNumber, packetPayloadSize); + m_NetSplitPacket.currentSequence = -1; + return -1; + } + + Q_memcpy(&m_NetSplitPacket.buffer[SPLIT_SIZE * packetNumber], pHeader + 1, packetPayloadSize); +#endif + } +#ifndef HLTV_FIXES Q_memcpy(&m_NetSplitPacket.buffer[SPLIT_SIZE * packetNumber], pHeader + 1, packetPayloadSize); +#endif if (m_NetSplitPacket.splitCount > 0) { return 0; } +#ifdef HLTV_FIXES + for (unsigned int i = 0; i < packetCount; i++) + { + if (netSplitFlags[i] != m_NetSplitPacket.currentSequence) + { + m_System->DPrintf("WARNING! NetSocket::GetLong: Split packet without all %i parts, part %i had wrong sequence %i/%i\n", + packetCount, + i + 1, + netSplitFlags[i], + m_NetSplitPacket.currentSequence); + + m_NetSplitPacket.currentSequence = -1; + return -1; + } + } +#endif + m_NetSplitPacket.currentSequence = -1; if (m_NetSplitPacket.totalSize > MAX_UDP_PACKET) { diff --git a/rehlds/HLTV/Core/src/World.h b/rehlds/HLTV/Core/src/World.h index 565d23e..09e98dd 100644 --- a/rehlds/HLTV/Core/src/World.h +++ b/rehlds/HLTV/Core/src/World.h @@ -252,11 +252,11 @@ protected: int m_MaxInstanced_BaseLine; -#ifdef HOOK_HLTV - char m_Lightstyles[MAX_LIGHTSTYLES][65]; -#else +#if defined(HLTV_FIXES) && !defined(HOOK_HLTV) char m_Lightstyles[MAX_LIGHTSTYLES][64]; -#endif // HOOK_HLTV +#else + char m_Lightstyles[MAX_LIGHTSTYLES][65]; +#endif movevars_t m_MoveVars; BSPModel m_WorldModel; diff --git a/rehlds/HLTV/DemoPlayer/build.gradle b/rehlds/HLTV/DemoPlayer/build.gradle index 6c78149..65629d2 100644 --- a/rehlds/HLTV/DemoPlayer/build.gradle +++ b/rehlds/HLTV/DemoPlayer/build.gradle @@ -16,7 +16,7 @@ void setupToolchain(NativeBinarySpec b) { boolean useGcc = project.hasProperty("useGcc") def cfg = rootProject.createToolchainConfig(b); cfg.projectInclude(project, '/..', '/../..', '/src', '/../common', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); - cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'DEMOPLAYER_MODULE' + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'HLTV_FIXES', 'DEMOPLAYER_MODULE' if (cfg instanceof MsvcToolchainConfig) { cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( diff --git a/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj b/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj index 8a6bd55..b01b6d0 100644 --- a/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj +++ b/rehlds/HLTV/DemoPlayer/msvc/DemoPlayer.vcxproj @@ -125,7 +125,7 @@ Use Level3 Disabled - HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;_DEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) precompiled.h $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\game_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) MultiThreadedDebug @@ -155,7 +155,7 @@ MaxSpeed true true - HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitiHOOons) + HLTV;HLTV_FIXES;WIN32;NDEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitiHOOons) precompiled.h $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories) MultiThreaded diff --git a/rehlds/HLTV/Director/build.gradle b/rehlds/HLTV/Director/build.gradle index bbe6564..213fbbd 100644 --- a/rehlds/HLTV/Director/build.gradle +++ b/rehlds/HLTV/Director/build.gradle @@ -16,7 +16,7 @@ void setupToolchain(NativeBinarySpec b) { boolean useGcc = project.hasProperty("useGcc") def cfg = rootProject.createToolchainConfig(b); cfg.projectInclude(project, '/..', '/../..', '/src', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); - cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'DIRECTOR_MODULE' + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'HLTV_FIXES', 'DIRECTOR_MODULE' if (cfg instanceof MsvcToolchainConfig) { cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( diff --git a/rehlds/HLTV/Director/msvc/Director.vcxproj b/rehlds/HLTV/Director/msvc/Director.vcxproj index efb8178..34689cc 100644 --- a/rehlds/HLTV/Director/msvc/Director.vcxproj +++ b/rehlds/HLTV/Director/msvc/Director.vcxproj @@ -76,7 +76,7 @@ Use Level3 Disabled - HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;_DEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(ProjectDir)\..\..\;$(ProjectDir)\..\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories) precompiled.h MultiThreadedDebug @@ -107,7 +107,7 @@ MaxSpeed true true - HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;NDEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(ProjectDir)\..\..\;$(ProjectDir)\..\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories) precompiled.h MultiThreaded diff --git a/rehlds/HLTV/Proxy/build.gradle b/rehlds/HLTV/Proxy/build.gradle index dc191a0..92e2deb 100644 --- a/rehlds/HLTV/Proxy/build.gradle +++ b/rehlds/HLTV/Proxy/build.gradle @@ -16,13 +16,12 @@ project.ext.dep_bzip2 = project(':dep/bzip2') void setupToolchain(NativeBinarySpec b) { boolean useGcc = project.hasProperty("useGcc") - boolean rehltvFixes = b.flavor.name.contains('rehltvFixes') def cfg = rootProject.createToolchainConfig(b); cfg.projectInclude(project, '/..', '/../..', '/src', '/../Director/src', '/../../common', '/../../engine', '/../../public', '/../../public/rehlds', '/../../pm_shared'); cfg.projectInclude(dep_bzip2, '/include') - cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'CORE_MODULE' + cfg.singleDefines 'USE_BREAKPAD_HANDLER', 'HLTV', 'HLTV_FIXES', 'PROXY_MODULE' if (cfg instanceof MsvcToolchainConfig) { cfg.compilerOptions.pchConfig = new MsvcToolchainConfig.PrecompiledHeadersConfig( @@ -66,10 +65,6 @@ void setupToolchain(NativeBinarySpec b) { cfg.extraLibs "steam_api" } - if (rehltvFixes) { - cfg.singleDefines 'REHLTV_FIXES', 'REHLTV_CHECKS' - } - ToolchainConfigUtils.apply(project, cfg, b); } diff --git a/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj b/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj index 822b067..bff3c46 100644 --- a/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj +++ b/rehlds/HLTV/Proxy/msvc/Proxy.vcxproj @@ -384,7 +384,7 @@ Use Level3 Disabled - HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;_DEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\Director\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) precompiled.h MultiThreadedDebug @@ -418,7 +418,7 @@ MaxSpeed true true - HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + HLTV;HLTV_FIXES;WIN32;NDEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) $(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\Director\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories) precompiled.h MultiThreaded diff --git a/rehlds/HLTV/common/BitBuffer.cpp b/rehlds/HLTV/common/BitBuffer.cpp index d309a4b..f730896 100644 --- a/rehlds/HLTV/common/BitBuffer.cpp +++ b/rehlds/HLTV/common/BitBuffer.cpp @@ -69,7 +69,7 @@ const uint32 INVBITTABLE[] = BitBuffer::BitBuffer() : m_Data(nullptr), m_CurByte(nullptr), - m_CurSize(0), + m_CurBit(0), m_MaxSize(0), m_Overflowed(false), m_LittleEndian(false), @@ -83,7 +83,7 @@ BitBuffer::BitBuffer(void *newData, unsigned int size) m_Data = (unsigned char *)newData; m_CurByte = m_Data; - m_CurSize = 0; + m_CurBit = 0; m_MaxSize = size; m_Overflowed = false; m_LittleEndian = true; @@ -100,7 +100,7 @@ BitBuffer::BitBuffer(unsigned int size) m_Data = nullptr; m_CurByte = nullptr; - m_CurSize = 0; + m_CurBit = 0; m_MaxSize = size; m_Overflowed = false; m_LittleEndian = false; @@ -114,7 +114,7 @@ bool BitBuffer::Resize(unsigned int size) Free(); m_Data = (unsigned char *)Mem_ZeroMalloc(size + 4); - m_CurSize = 0; + m_CurBit = 0; m_Overflowed = false; if (m_Data) @@ -139,7 +139,7 @@ void BitBuffer::Clear() Q_memset(m_Data, 0, m_MaxSize); m_CurByte = m_Data; - m_CurSize = 0; + m_CurBit = 0; m_Overflowed = false; m_LittleEndian = true; @@ -147,13 +147,13 @@ void BitBuffer::Clear() int BitBuffer::CurrentBit() { - return m_CurSize + 8 * (m_CurByte - m_Data); + return m_CurBit + 8 * (m_CurByte - m_Data); } void BitBuffer::Reset() { m_CurByte = m_Data; - m_CurSize = 0; + m_CurBit = 0; m_Overflowed = false; m_LittleEndian = true; @@ -168,7 +168,7 @@ void BitBuffer::Free() m_Data = nullptr; m_CurByte = nullptr; - m_CurSize = 0; + m_CurBit = 0; m_MaxSize = 0; m_OwnData = false; @@ -187,26 +187,26 @@ unsigned int BitBuffer::ReadBits(int numbits) return -1; } - int bits = m_CurSize + numbits; + int bits = m_CurBit + numbits; if (bits <= 32) { - result = (*(unsigned int *)m_CurByte >> m_CurSize) & ROWBITTABLE[numbits]; + result = (*(unsigned int *)m_CurByte >> m_CurBit) & ROWBITTABLE[numbits]; m_CurByte += numbits >> 3; - m_CurSize += numbits & 7; + m_CurBit += numbits & 7; - if (m_CurSize > 7) + if (m_CurBit > 7) { - m_CurSize &= 7; + m_CurBit &= 7; m_CurByte++; } } else { - unsigned int data = *(unsigned int *)m_CurByte >> m_CurSize; + unsigned int data = *(unsigned int *)m_CurByte >> m_CurBit; m_CurByte += 4; - result = ((ROWBITTABLE[bits & 7] & *(unsigned int *)m_CurByte) << (32 - m_CurSize)) | data; - m_CurSize = bits & 7; + result = ((ROWBITTABLE[bits & 7] & *(unsigned int *)m_CurByte) << (32 - m_CurBit)) | data; + m_CurBit = bits & 7; } } else @@ -235,26 +235,26 @@ int BitBuffer::ReadBit() { if (m_LittleEndian) { - if (m_CurSize == 7) + if (m_CurBit == 7) { - m_CurSize = 0; + m_CurBit = 0; result = (*m_CurByte++ >> 7) & 1; } else { - result = ((unsigned int)*m_CurByte >> m_CurSize++) & 1; + result = ((unsigned int)*m_CurByte >> m_CurBit++) & 1; } } else { - if (m_CurSize == 7) + if (m_CurBit == 7) { - m_CurSize = 0; + m_CurBit = 0; result = *m_CurByte++ & 1; } else { - result = ((unsigned int)*m_CurByte >> (7 - m_CurSize++)) & 1; + result = ((unsigned int)*m_CurByte >> (7 - m_CurBit++)) & 1; } } } @@ -264,11 +264,11 @@ int BitBuffer::ReadBit() unsigned int BitBuffer::PeekBits(int numbits) { - int oldcurrentBit = m_CurSize; + int oldcurrentBit = m_CurBit; unsigned char *oldcurrentByte = m_CurByte; unsigned int data = ReadBits(numbits); - m_CurSize = oldcurrentBit; + m_CurBit = oldcurrentBit; m_CurByte = oldcurrentByte; return data; } @@ -316,7 +316,7 @@ bool BitBuffer::ReadBuf(int iSize, void *pbuf) return false; } - if (m_CurSize) + if (m_CurBit) { int i, j; unsigned int *p = (unsigned int *)pbuf; @@ -402,7 +402,7 @@ void BitBuffer::WriteBit(int c) if (m_LittleEndian) { - if (m_CurSize == 7) + if (m_CurBit == 7) { if (c) { @@ -414,20 +414,20 @@ void BitBuffer::WriteBit(int c) } m_CurByte++; - m_CurSize = 0; + m_CurBit = 0; } else { if (c) { - m_CurByte[0] |= BITTABLE[ m_CurSize ]; + m_CurByte[0] |= BITTABLE[ m_CurBit ]; } else { - m_CurByte[0] &= INVBITTABLE[ m_CurSize ]; + m_CurByte[0] &= INVBITTABLE[ m_CurBit ]; } - m_CurSize++; + m_CurBit++; } } else @@ -436,13 +436,13 @@ void BitBuffer::WriteBit(int c) static unsigned char inv_masks[] = { 0x7Fu, 0xBFu, 0xDFu, 0xEFu, 0xF7u, 0xFBu, 0xFDu, 0xFEu }; if (c) - m_CurByte[0] |= masks[ m_CurSize ]; + m_CurByte[0] |= masks[ m_CurBit ]; else - m_CurByte[0] &= inv_masks[ m_CurSize ]; + m_CurByte[0] &= inv_masks[ m_CurBit ]; - if (++m_CurSize == 8) + if (++m_CurBit == 8) { - m_CurSize = 0; + m_CurBit = 0; m_CurByte++; } } @@ -461,26 +461,26 @@ void BitBuffer::WriteBits(unsigned int data, int numbits) return; } - int bits = numbits + m_CurSize; + int bits = numbits + m_CurBit; if (bits <= 32) { - *(uint32 *)m_CurByte |= (ROWBITTABLE[numbits] & data) << m_CurSize; + *(uint32 *)m_CurByte |= (ROWBITTABLE[numbits] & data) << m_CurBit; m_CurByte = &m_CurByte[numbits >> 3]; - m_CurSize = m_CurSize + (numbits & 7); + m_CurBit = m_CurBit + (numbits & 7); - if (m_CurSize > 7) + if (m_CurBit > 7) { - m_CurSize = m_CurSize & 7; + m_CurBit = m_CurBit & 7; m_CurByte = m_CurByte + 1; } } else { - *(uint32 *)m_CurByte |= (ROWBITTABLE[numbits] & data) << m_CurSize; + *(uint32 *)m_CurByte |= (ROWBITTABLE[numbits] & data) << m_CurBit; - int leftBits = (32 - m_CurSize); - m_CurSize = (m_CurSize + numbits) & 7; + int leftBits = (32 - m_CurBit); + m_CurBit = (m_CurBit + numbits) & 7; m_CurByte += 4; *(uint32 *)m_CurByte |= (ROWBITTABLE[numbits] & data) >> leftBits; @@ -581,7 +581,7 @@ void BitBuffer::WriteBuf(const void *buf, int iSize) return; } - if (m_CurSize) + if (m_CurBit) { int i, j; unsigned int *p = (unsigned int *)buf; @@ -623,7 +623,7 @@ void BitBuffer::WriteHiresAngle(float f) int BitBuffer::CurrentSize() { - return (m_CurSize != 0) + m_CurByte - m_Data; + return (m_CurBit != 0) + m_CurByte - m_Data; } unsigned char *BitBuffer::CurrentByte() @@ -638,10 +638,10 @@ int BitBuffer::SpaceLeft() void BitBuffer::AlignByte() { - if (m_CurSize) + if (m_CurBit) { m_CurByte++; - m_CurSize = 0; + m_CurBit = 0; } } @@ -712,7 +712,7 @@ void BitBuffer::WriteBitString(const char *p) void BitBuffer::StartBitMode() { - if (m_CurSize) { + if (m_CurBit) { m_Overflowed = true; } } @@ -739,7 +739,7 @@ void BitBuffer::SetBuffer(void *buffer, int size) m_Data = (unsigned char *)buffer; m_CurByte = (unsigned char *)buffer; m_MaxSize = size; - m_CurSize = 0; + m_CurBit = 0; m_OwnData = false; m_Overflowed = false; @@ -811,22 +811,22 @@ void BitBuffer::SkipBits(int numbits) return; } - int bits = m_CurSize + numbits; + int bits = m_CurBit + numbits; if (bits <= 32) { m_CurByte += numbits >> 3; - m_CurSize += numbits & 7; + m_CurBit += numbits & 7; - if (m_CurSize > 7) + if (m_CurBit > 7) { - m_CurSize &= 7; + m_CurBit &= 7; m_CurByte++; } } else { m_CurByte += 4; - m_CurSize = bits & 7; + m_CurBit = bits & 7; } } else @@ -835,14 +835,14 @@ void BitBuffer::SkipBits(int numbits) while (d > 0) { --d; - if (m_CurSize == 7) + if (m_CurBit == 7) { m_CurByte++; - m_CurSize = 0; + m_CurBit = 0; } else { - m_CurSize++; + m_CurBit++; } } } @@ -870,7 +870,7 @@ void BitBuffer::FastClear() Q_memset(m_Data, 0, iSize); m_CurByte = m_Data; - m_CurSize = 0; + m_CurBit = 0; m_Overflowed = false; m_LittleEndian = true; diff --git a/rehlds/HLTV/common/BitBuffer.h b/rehlds/HLTV/common/BitBuffer.h index 119e6b6..0e0b2b7 100644 --- a/rehlds/HLTV/common/BitBuffer.h +++ b/rehlds/HLTV/common/BitBuffer.h @@ -41,7 +41,6 @@ public: unsigned char *CurrentByte(); int GetMaxSize() const { return m_MaxSize; } - unsigned int GetCurSize() const { return m_CurSize; } unsigned char *GetData() const { return m_Data; } bool IsOverflowed() const { return m_Overflowed; } @@ -108,7 +107,7 @@ public: bool m_Overflowed; unsigned char *m_Data; unsigned char *m_CurByte; - int m_CurSize; + int m_CurBit; int m_MaxSize; protected: diff --git a/rehlds/HLTV/common/NetChannel.cpp b/rehlds/HLTV/common/NetChannel.cpp index 34cc85d..ce4c6e5 100644 --- a/rehlds/HLTV/common/NetChannel.cpp +++ b/rehlds/HLTV/common/NetChannel.cpp @@ -310,7 +310,12 @@ void NetChannel::UpdateFlow(int stream) void NetChannel::TransmitOutgoing() { +#ifdef HLTV_FIXES + byte send_buf[MAX_UDP_PACKET]; +#else byte send_buf[NET_MAX_MESSAGE]; +#endif + BitBuffer data(send_buf, sizeof(send_buf)); bool send_reliable; @@ -336,7 +341,7 @@ void NetChannel::TransmitOutgoing() // check for reliable message overflow if (m_reliableStream.IsOverflowed()) { - m_System->DPrintf("Transmit:Outgoing m_reliableStream overflow (%s)\n", m_remote_address.ToString()); + m_System->DPrintf("NetChannel::Transmit:Outgoing m_reliableStream overflow (%s)\n", m_remote_address.ToString()); m_reliableStream.Clear(); return; } @@ -344,7 +349,7 @@ void NetChannel::TransmitOutgoing() // check for unreliable message overflow if (m_unreliableStream.IsOverflowed()) { - m_System->DPrintf("Transmit:Outgoing m_unreliableStream overflow (%s)\n", m_remote_address.ToString()); + m_System->DPrintf("NetChannel::Transmit:Outgoing m_unreliableStream overflow (%s)\n", m_remote_address.ToString()); m_unreliableStream.Clear(); } @@ -653,8 +658,7 @@ bool NetChannel::CheckForCompletion(int stream, int intotalbuffers) return false; } -#ifdef REHLTV_FIXES -bool NetChannel::ValidateFragments(int pkt_size, qboolean *frag_message, unsigned int *fragid, int *frag_offset, int *frag_length) +bool NetChannel::ValidateFragments(BitBuffer &buf, bool *frag_message, unsigned int *fragid, int *frag_offset, int *frag_length) { for (int i = 0; i < MAX_STREAMS; i++) { @@ -664,32 +668,35 @@ bool NetChannel::ValidateFragments(int pkt_size, qboolean *frag_message, unsigne // total fragments should be <= MAX_FRAGMENTS and current fragment can't be > total fragments if (i == FRAG_NORMAL_STREAM && FRAG_GETCOUNT(fragid[i]) > MAX_NORMAL_FRAGMENTS) return false; + if (i == FRAG_FILE_STREAM && FRAG_GETCOUNT(fragid[i]) > MAX_FILE_FRAGMENTS) return false; + if (FRAG_GETID(fragid[i]) > FRAG_GETCOUNT(fragid[i])) return false; + if (!frag_length[i]) return false; + if ((size_t)frag_length[i] > FRAGMENT_MAX_SIZE || (size_t)frag_offset[i] > MAX_POSSIBLE_MSG - 1) return false; int frag_end = frag_offset[i] + frag_length[i]; // end of fragment is out of the packet - if (frag_end + msg_readcount > pkt_size) + if (frag_end + buf.CurrentSize() > buf.GetMaxSize()) return false; // fragment overlaps next stream's fragment or placed after it for (int j = i + 1; j < MAX_STREAMS; j++) { - if (frag_end > frag_offset[j] && frag_message[j]) // don't add msg_readcount for comparison + if (frag_end > frag_offset[j] && frag_message[j]) // don't add buf.CurrentSize() for comparison return false; } } return true; } -#endif // REHLTV_FIXES void NetChannel::ProcessIncoming(unsigned char *data, int size) { @@ -760,8 +767,8 @@ void NetChannel::ProcessIncoming(unsigned char *data, int size) } } -#ifdef REHLTV_FIXES - if (!ValidateFragments(size, frag_message, fragid, frag_offset, frag_length)) +#ifdef HLTV_FIXES + if (!ValidateFragments(message, frag_message, fragid, frag_offset, frag_length)) return; #endif } @@ -801,7 +808,12 @@ void NetChannel::ProcessIncoming(unsigned char *data, int size) // clear the buffer to make way for the next if (reliable_ack == (unsigned int)m_reliable_sequence) { + // Make sure we actually could have ack'd this message +#ifdef HLTV_FIXES + if (sequence_ack >= (unsigned)m_last_reliable_sequence) +#else if (m_incoming_acknowledged + 1 >= m_last_reliable_sequence) +#endif { // it has been received m_reliableOutSize = 0; @@ -1147,6 +1159,24 @@ void NetChannel::CopyNormalFragments() p = n; } +#ifdef HLTV_FIXES + if (packet->data.IsOverflowed()) + { + if (packet->address.IsValid()) + { + m_System->Printf("WARNING! NetChannel::CopyNormalFragments: Incoming overflowed from %s\n", packet->address.ToString()); + } + else + { + m_System->Printf("WARNING! NetChannel::CopyNormalFragments: Incoming overflowed\n"); + } + + packet->data.Clear(); + m_incomingbufs[FRAG_NORMAL_STREAM] = nullptr; + return; + } +#endif + if (*(uint32 *)packet->data.GetData() == MAKEID('B', 'Z', '2', '\0')) { char uncompressed[65536]; diff --git a/rehlds/HLTV/common/NetChannel.h b/rehlds/HLTV/common/NetChannel.h index 18b00f9..4a3b292 100644 --- a/rehlds/HLTV/common/NetChannel.h +++ b/rehlds/HLTV/common/NetChannel.h @@ -51,41 +51,41 @@ enum MAX_FLOWS }; -#define MAX_LATENT 32 -#define FRAGMENT_MAX_SIZE 1400 // Size of fragmentation buffer internal buffers -#define CLIENT_FRAGMENT_SIZE_ONCONNECT 128 -#define CUSTOMIZATION_MAX_SIZE 20480 +#define MAX_LATENT 32 +#define FRAGMENT_MAX_SIZE 1400 // Size of fragmentation buffer internal buffers +#define CLIENT_FRAGMENT_SIZE_ONCONNECT 128 +#define CUSTOMIZATION_MAX_SIZE 20480 // Client sends normal fragments only while connecting -#define MAX_NORMAL_FRAGMENTS (MAX_POSSIBLE_MSG / CLIENT_FRAGMENT_SIZE_ONCONNECT) +#define MAX_NORMAL_FRAGMENTS (MAX_POSSIBLE_MSG / CLIENT_FRAGMENT_SIZE_ONCONNECT) // While client is connecting it sending fragments with minimal size, also it transfers sprays with minimal fragments... // But with sv_delayed_spray_upload it sends with cl_dlmax fragment size -#define MAX_FILE_FRAGMENTS (CUSTOMIZATION_MAX_SIZE / FRAGMENT_C2S_MIN_SIZE) +#define MAX_FILE_FRAGMENTS (CUSTOMIZATION_MAX_SIZE / FRAGMENT_C2S_MIN_SIZE) -#define UDP_HEADER_SIZE 28 -#define MAX_RELIABLE_PAYLOAD 1200 +#define UDP_HEADER_SIZE 28 +#define MAX_RELIABLE_PAYLOAD 1200 -#define MAKE_FRAGID(id, count) (((id & 0xffff) << 16) | (count & 0xffff)) -#define FRAG_GETID(fragid) ((fragid >> 16) & 0xffff) -#define FRAG_GETCOUNT(fragid) (fragid & 0xffff) +#define MAKE_FRAGID(id, count) (((id & 0xffff) << 16) | (count & 0xffff)) +#define FRAG_GETID(fragid) ((fragid >> 16) & 0xffff) +#define FRAG_GETCOUNT(fragid) (fragid & 0xffff) // Max length of a reliable message -#define MAX_MSGLEN 3990 // 10 reserved for fragheader? -#define MAX_POSSIBLE_MSG 65536 +#define MAX_MSGLEN 3990 // 10 reserved for fragheader? +#define MAX_POSSIBLE_MSG 65536 -#define MAX_ROUTEABLE_PACKET 1400 -#define MIN_ROUTEABLE_PACKET 16 +#define MAX_ROUTEABLE_PACKET 1400 +#define MIN_ROUTEABLE_PACKET 16 -#define SPLIT_SIZE (MAX_ROUTEABLE_PACKET - sizeof(SPLITPACKET)) +#define SPLIT_SIZE (MAX_ROUTEABLE_PACKET - sizeof(SPLITPACKET)) // Pad this to next higher 16 byte boundary // This is the largest packet that can come in/out over the wire, before processing the header // bytes will be stripped by the networking channel layer // #define NET_MAX_MESSAGE PAD_NUMBER( ( MAX_MSGLEN + HEADER_BYTES ), 16 ) // This is currently used value in the engine. TODO: define above gives 4016, check it why. -#define NET_MAX_MESSAGE 4037 -#define NET_HEADER_FLAG_SPLITPACKET -2 +#define NET_MAX_MESSAGE 4037 +#define NET_HEADER_FLAG_SPLITPACKET -2 class IBaseSystem; @@ -125,7 +125,7 @@ public: typedef struct flowstats_s { int size; // Size of message sent/received - double time; // Time that message was sent/received + double time; // Time that message was sent/received } flowstats_t; typedef struct flow_s @@ -143,27 +143,25 @@ public: typedef struct fragbuf_s { struct fragbuf_s *next; // Next buffer in chain - int bufferId; // Id of this buffer - byte data[FRAGMENT_MAX_SIZE]; // The actual data sits here + int bufferId; // Id of this buffer + byte data[FRAGMENT_MAX_SIZE]; // The actual data sits here - int size; // Size of data to read at that offset - bool isfile; // Is this a file buffer? - bool isbuffer; // Is this file buffer from memory ( custom decal, etc. ). + int size; // Size of data to read at that offset + bool isfile; // Is this a file buffer? + bool isbuffer; // Is this file buffer from memory ( custom decal, etc. ). char fileName[MAX_PATH]; // Name of the file to save out on remote host - int fOffset; // Offset in file from which to read data + int fOffset; // Offset in file from which to read data } fragbuf_t; // Waiting list of fragbuf chains typedef struct fragbufwaiting_s { - struct fragbufwaiting_s *next; // Next chain in waiting list - int fragbufcount; // Number of buffers in this chain + struct fragbufwaiting_s *next; // Next chain in waiting list + int fragbufcount; // Number of buffers in this chain fragbuf_t *fragbufs; // The actual buffers } fragbufwaiting_t; -#ifdef REHLTV_FIXES - bool ValidateFragments(int pkt_size, qboolean *frag_message, unsigned int *fragid, int *frag_offset, int *frag_length); -#endif + bool ValidateFragments(BitBuffer &buf, bool *frag_message, unsigned int *fragid, int *frag_offset, int *frag_length); bool CreateFragmentsFromFile(char *fileName); bool CopyFileFragments(); void GetFlowStats(float *avgInKBSec, float *avgOutKBSec); @@ -191,25 +189,25 @@ public: NetAddress m_remote_address; // Address this channel is talking to. double m_last_received; double m_last_send; - double m_connect_time; // Time when channel was connected. + double m_connect_time; // Time when channel was connected. float m_timeout; int m_max_bandwidth_rate; double m_send_interval; - int m_updaterate; // Bandwidth choke, bytes per second - double m_cleartime; // If realtime > cleartime, free to send next packet + int m_updaterate; // Bandwidth choke, bytes per second + double m_cleartime; // If realtime > cleartime, free to send next packet bool m_keep_alive; bool m_crashed; bool m_connected; // Sequencing variables - int m_incoming_sequence; // Increasing count of sequence numbers - int m_incoming_acknowledged; // # of last outgoing message that has been ack'd. + int m_incoming_sequence; // Increasing count of sequence numbers + int m_incoming_acknowledged; // # of last outgoing message that has been ack'd. int m_incoming_reliable_acknowledged; // Toggles T/F as reliable messages are received. - int m_incoming_reliable_sequence; // single bit, maintained local - int m_outgoing_sequence; // Message we are sending to remote - int m_reliable_sequence; // Whether the message contains reliable payload, single bit - int m_last_reliable_sequence; // Outgoing sequence number of last send that had reliable data + int m_incoming_reliable_sequence; // single bit, maintained local + int m_outgoing_sequence; // Message we are sending to remote + int m_reliable_sequence; // Whether the message contains reliable payload, single bit + int m_last_reliable_sequence; // Outgoing sequence number of last send that had reliable data void *m_connection_status; @@ -228,11 +226,11 @@ public: int m_reliable_fragment[MAX_STREAMS]; // Is reliable waiting buf a fragment? size_t m_reliable_fragid[MAX_STREAMS]; // Buffer id for each waiting fragment - fragbuf_t *m_fragbufs[MAX_STREAMS]; // The current fragment being set - int m_fragbufcount[MAX_STREAMS]; // The total number of fragments in this stream + fragbuf_t *m_fragbufs[MAX_STREAMS]; // The current fragment being set + int m_fragbufcount[MAX_STREAMS]; // The total number of fragments in this stream - int16 m_frag_startpos[MAX_STREAMS]; // Position in outgoing buffer where frag data starts - int16 m_frag_length[MAX_STREAMS]; // Length of frag data in the buffer + int16 m_frag_startpos[MAX_STREAMS]; // Position in outgoing buffer where frag data starts + int16 m_frag_length[MAX_STREAMS]; // Length of frag data in the buffer fragbuf_t *m_incomingbufs[MAX_STREAMS]; // Incoming fragments are stored here From ec63e8f162aa467a401181499c94002b4e9d2f5d Mon Sep 17 00:00:00 2001 From: IgnacioFDM Date: Tue, 3 Apr 2018 14:27:19 -0300 Subject: [PATCH 3/7] Dummy assignment? --- rehlds/engine/pr_cmds.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/rehlds/engine/pr_cmds.cpp b/rehlds/engine/pr_cmds.cpp index 1d6daca..89f7587 100644 --- a/rehlds/engine/pr_cmds.cpp +++ b/rehlds/engine/pr_cmds.cpp @@ -356,7 +356,6 @@ void EXT_FUNC PF_traceline_DLL(const float *v1, const float *v2, int fNoMonsters void EXT_FUNC TraceHull(const float *v1, const float *v2, int fNoMonsters, int hullNumber, edict_t *pentToSkip, TraceResult *ptr) { - hullNumber = hullNumber; if (hullNumber < 0 || hullNumber > 3) hullNumber = 0; From f324df867f214c24b4e7f47fba38785aa5b922b6 Mon Sep 17 00:00:00 2001 From: theAsmodai Date: Fri, 4 May 2018 23:37:53 +0300 Subject: [PATCH 4/7] Info code refactoring (#604) * info.cpp refactoring * Update info unittests * Make _vgui_menus important and fix tests passing --- README.md | 2 +- rehlds/HLTV/common/InfoString.cpp | 4 +- rehlds/common/port.h | 2 +- rehlds/engine/host_cmd.cpp | 50 ++- rehlds/engine/info.cpp | 568 ++++++++++++++++++++++++++---- rehlds/engine/info.h | 9 +- rehlds/engine/sv_main.cpp | 54 ++- rehlds/public/strtools.h | 15 +- rehlds/unittests/info_tests.cpp | 216 ++++++++++-- 9 files changed, 758 insertions(+), 162 deletions(-) diff --git a/README.md b/README.md index 12ddc49..c5aabb9 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Bugfixed version of rehlds contains an additional cvars:
  • 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". 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/dreamstalker/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 diff --git a/rehlds/HLTV/common/InfoString.cpp b/rehlds/HLTV/common/InfoString.cpp index 3d61971..070206e 100644 --- a/rehlds/HLTV/common/InfoString.cpp +++ b/rehlds/HLTV/common/InfoString.cpp @@ -81,7 +81,7 @@ bool InfoString::SetString(char *string) return false; } - Q_strnlcpy(m_String, string, m_MaxSize); + Q_strlcpy(m_String, string, m_MaxSize); return true; } @@ -95,7 +95,7 @@ void InfoString::SetMaxSize(unsigned int maxSize) if (m_String) { if (maxSize > Q_strlen(m_String)) { - Q_strnlcpy(newBuffer, m_String, maxSize); + Q_strlcpy(newBuffer, m_String, maxSize); } Mem_Free(m_String); diff --git a/rehlds/common/port.h b/rehlds/common/port.h index fc12e70..835ca2b 100644 --- a/rehlds/common/port.h +++ b/rehlds/common/port.h @@ -105,7 +105,7 @@ extern char g_szEXEName[ 4096 ]; #define _snprintf snprintf - + #if defined(OSX) #define SO_ARCH_SUFFIX ".dylib" #else diff --git a/rehlds/engine/host_cmd.cpp b/rehlds/engine/host_cmd.cpp index 821955c..9cac571 100644 --- a/rehlds/engine/host_cmd.cpp +++ b/rehlds/engine/host_cmd.cpp @@ -2365,17 +2365,56 @@ void Host_Version_f(void) void Host_FullInfo_f(void) { - char key[512]; - char value[512]; - char *o; - char *s; - if (Cmd_Argc() != 2) { Con_Printf("fullinfo \n"); return; } +#ifdef REHLDS_FIXES + char copy[MAX_INFO_STRING]; + Q_strlcpy(copy, Cmd_Argv(1)); + + char* s = copy; + if (*s != '\\') + return; + + bool eos = false; + while (!eos) { + const char* key = ++s; + + // key + while (*s != '\\') + { + // key should end with a '\', not a NULL + if (*s == '\0') { + Con_Printf("MISSING VALUE\n"); + return; + } + + s++; + } + + *s = '\0'; + const char* value = ++s; + + // value + while (*s != '\\') { + if (*s == '\0') { + eos = true; + break; + } + + s++; + } + + *s = '\0'; +#else + char key[512]; + char value[512]; + char *o; + char *s; + s = (char *)Cmd_Argv(1); if (*s == '\\') s++; @@ -2400,6 +2439,7 @@ void Host_FullInfo_f(void) *o = 0; if (*s) s++; +#endif if (cmd_source == src_command) { diff --git a/rehlds/engine/info.cpp b/rehlds/engine/info.cpp index 8a60be6..0bd1cbd 100644 --- a/rehlds/engine/info.cpp +++ b/rehlds/engine/info.cpp @@ -31,10 +31,84 @@ // NOTE: This file contains a lot of fixes that are not covered by REHLDS_FIXES define. // TODO: Most of the Info_ functions can be speedup via removing unneded copy of key and values. +struct info_field_t +{ + char* name; + bool integer; +}; + +info_field_t g_info_important_fields[] = +{ + // name integer + { "name", false }, + { "model", false }, + + // model colors + { "topcolor", true }, + { "bottomcolor", true }, + + // network + { "rate", true }, + { "cl_updaterate", true }, + { "cl_lw", true }, + { "cl_lc", true }, + + // hltv flag + { "*hltv", true }, + + // avatars + { "*sid", false }, // transmit as string because it's int64 + + // gui/text menus + { "_vgui_menus", true } +}; + +std::vector g_info_transmitted_fields; + // Searches the string for the given // key and returns the associated value, or an empty string. -const char* EXT_FUNC Info_ValueForKey(const char *s, const char *key) +const char* EXT_FUNC Info_ValueForKey(const char *s, const char *lookup) { +#ifdef REHLDS_FIXES + static char valueBuf[INFO_MAX_BUFFER_VALUES][MAX_KV_LEN]; + static int valueIndex; + + while (*s == '\\') + { + // skip starting slash + const char* key = ++s; + + // skip key + while (*s != '\\') { + // Add some sanity checks because it's external function + if (*s == '\0') + return ""; + + s++; + } + + size_t keyLen = s - key; + const char* value = ++s; // skip separating slash + + // skip value + while (*s != '\\' && *s != '\0') + s++; + + size_t valueLen = Q_min(s - value, MAX_KV_LEN - 1); + + if (!Q_strncmp(key, lookup, keyLen)) + { + char* dest = valueBuf[valueIndex]; + Q_memcpy(dest, value, valueLen); + dest[valueLen] = '\0'; + + valueIndex = (valueIndex + 1) % INFO_MAX_BUFFER_VALUES; + return dest; + } + } + + return ""; +#else // use few (two?) buffers so compares work without stomping on each other static char value[INFO_MAX_BUFFER_VALUES][MAX_KV_LEN]; static int valueindex; @@ -69,7 +143,7 @@ const char* EXT_FUNC Info_ValueForKey(const char *s, const char *key) *c = 0; s++; // skip the slash - // Copy a value + // Copy a value nCount = 0; c = value[valueindex]; while (*s != '\\') @@ -88,7 +162,7 @@ const char* EXT_FUNC Info_ValueForKey(const char *s, const char *key) } *c = 0; - if (!Q_strcmp(key, pkey)) + if (!Q_strcmp(lookup, pkey)) { c = value[valueindex]; valueindex = (valueindex + 1) % INFO_MAX_BUFFER_VALUES; @@ -97,10 +171,47 @@ const char* EXT_FUNC Info_ValueForKey(const char *s, const char *key) } return ""; +#endif } -void Info_RemoveKey(char *s, const char *key) +void Info_RemoveKey(char *s, const char *lookup) { +#ifdef REHLDS_FIXES + size_t lookupLen = Q_strlen(lookup); + + while (*s == '\\') + { + char* start = s; + + // skip starting slash + const char* key = ++s; + + // skip key + while (*s != '\\') { + if (*s == '\0') + return; + + s++; + } + + size_t keyLen = s - key; + ++s; // skip separating slash + + // skip value + while (*s != '\\' && *s != '\0') + s++; + + if (keyLen != lookupLen) + continue; + + if (!Q_memcmp(key, lookup, lookupLen)) + { + // cut key and value + Q_memmove(start, s, Q_strlen(s) + 1); + break; + } + } +#else char pkey[MAX_KV_LEN]; char value[MAX_KV_LEN]; char *start; @@ -108,13 +219,13 @@ void Info_RemoveKey(char *s, const char *key) int cmpsize; int nCount; - if (Q_strstr(key, "\\")) + if (Q_strstr(lookup, "\\")) { Con_Printf("Can't use a key with a \\\n"); return; } - cmpsize = Q_strlen(key); + cmpsize = Q_strlen(lookup); if (cmpsize > MAX_KV_LEN - 1) cmpsize = MAX_KV_LEN - 1; @@ -168,16 +279,47 @@ void Info_RemoveKey(char *s, const char *key) *c = 0; // Compare keys - if (!Q_strncmp(key, pkey, cmpsize)) + if (!Q_strncmp(lookup, pkey, cmpsize)) { Q_strcpy_s(start, s); // remove this part s = start; // continue searching } } +#endif } void Info_RemovePrefixedKeys(char *s, const char prefix) { +#ifdef REHLDS_FIXES + while (*s == '\\') + { + char* start = s; + + // skip starting slash + const char* key = ++s; + + // skip key + while (*s != '\\') { + if (*s == '\0') + return; + + s++; + } + + // skip separating slash + ++s; + + // skip value + while (*s != '\\' && *s != '\0') + s++; + + if (key[0] == prefix) + { + Q_memmove(start, s, Q_strlen(s) + 1); + s = start; + } + } +#else char pkey[MAX_KV_LEN]; char value[MAX_KV_LEN]; char *start; @@ -239,12 +381,37 @@ void Info_RemovePrefixedKeys(char *s, const char prefix) s = start; // continue searching } } +#endif } +#ifdef REHLDS_FIXES qboolean Info_IsKeyImportant(const char *key) { if (key[0] == '*') return true; + + for (auto& field : g_info_important_fields) { + if (!Q_strcmp(key, field.name)) + return true; + } + + return false; +} + +qboolean Info_IsKeyImportant(const char *key, size_t keyLen) +{ + char copy[MAX_KV_LEN]; + keyLen = min(keyLen, sizeof(copy) - 1); + Q_memcpy(copy, key, keyLen); + copy[keyLen] = '\0'; + return Info_IsKeyImportant(copy); +} +#else +qboolean Info_IsKeyImportant(const char *key) +{ + if (key[0] == '*') + return true; + if (!Q_strcmp(key, "name")) return true; if (!Q_strcmp(key, "model")) @@ -261,19 +428,53 @@ qboolean Info_IsKeyImportant(const char *key) return true; if (!Q_strcmp(key, "cl_lc")) return true; -#ifndef REHLDS_FIXES - // keys starts from '*' already checked if (!Q_strcmp(key, "*hltv")) return true; if (!Q_strcmp(key, "*sid")) return true; -#endif return false; } +#endif -char *Info_FindLargestKey(char *s, int maxsize) +const char *Info_FindLargestKey(const char *s, int maxsize) { +#ifdef REHLDS_FIXES + static char largestKey[MAX_KV_LEN]; + size_t largestLen = 0; + + while (*s == '\\') + { + // skip starting slash + const char* key = ++s; + + // skip key + while (*s != '\\') { + if (*s == '\0') + return ""; + + s++; + } + + size_t keyLen = s - key; + const char* value = ++s; // skip separating slash + + // skip value + while (*s != '\\' && *s != '\0') + s++; + + size_t valueLen = s - value; + size_t totalLen = keyLen + valueLen; + + if (totalLen > largestLen && !Info_IsKeyImportant(key, keyLen)) { + largestLen = totalLen; + Q_memcpy(largestKey, key, keyLen); + largestKey[keyLen] = '\0'; + } + } + + return largestLen ? largestKey : ""; +#else static char largest_key[MAX_KV_LEN]; char key[MAX_KV_LEN]; char value[MAX_KV_LEN]; @@ -347,8 +548,104 @@ char *Info_FindLargestKey(char *s, int maxsize) } return largest_key; +#endif } +#ifdef REHLDS_FIXES +qboolean Info_SetValueForStarKey(char *s, const char *key, const char *value, size_t maxsize) +{ + char newArray[MAX_INFO_STRING], valueBuf[MAX_KV_LEN]; + + if (!key || !value) + { + Con_Printf("Keys and values can't be null\n"); + return FALSE; + } + + if (key[0] == '\0') + { + Con_Printf("Keys can't be an empty string\n"); + return FALSE; + } + + if (Q_strchr(key, '\\') || Q_strchr(value, '\\')) + { + Con_Printf("Can't use keys or values with a \\\n"); + return FALSE; + } + + if (Q_strchr(key, '\"') || Q_strchr(value, '\"')) + { + Con_Printf("Can't use keys or values with a \"\n"); + return FALSE; + } + + if (Q_strstr(key, "..") || Q_strstr(value, "..")) + { + Con_Printf("Can't use keys or values with a ..\n"); + return FALSE; + } + + int keyLen = Q_strlen(key); + int valueLen = Q_strlen(value); + + if (keyLen >= MAX_KV_LEN || valueLen >= MAX_KV_LEN) + { + Con_Printf("Keys and values must be < %i characters\n", MAX_KV_LEN); + return FALSE; + } + + if (!Q_UnicodeValidate(key) || !Q_UnicodeValidate(value)) + { + Con_Printf("Keys and values must be valid utf8 text\n"); + return FALSE; + } + + // Remove current key/value and return if we doesn't specified to set a value + Info_RemoveKey(s, key); + if (value[0] == '\0') + { + return TRUE; + } + + // auto lowercase team + if (!Q_strcmp(key, "team")) { + value = Q_strcpy(valueBuf, value); + Q_strlwr(valueBuf); + } + + // Create key/value pair + size_t neededLength = Q_snprintf(newArray, sizeof newArray, "\\%s\\%s", key, value); + + if (Q_strlen(s) + neededLength >= maxsize) + { + // no more room in the buffer to add key/value + if (!Info_IsKeyImportant(key)) + { + // no room to add setting + Con_Printf("Info string length exceeded\n"); + return FALSE; + } + + // keep removing the largest key/values until we have a room + do + { + const char* largekey = Info_FindLargestKey(s, maxsize); + if (largekey[0] == '\0') + { + // no room to add setting + Con_Printf("Info string length exceeded\n"); + return FALSE; + } + Info_RemoveKey(s, largekey); + } while ((int)Q_strlen(s) + neededLength >= maxsize); + } + + Q_strcat(s, newArray); + + return TRUE; +} +#else void Info_SetValueForStarKey(char *s, const char *key, const char *value, int maxsize) { char newArray[MAX_INFO_STRING]; @@ -424,7 +721,7 @@ void Info_SetValueForStarKey(char *s, const char *key, const char *value, int ma } // keep removing the largest key/values until we have a room - char *largekey; + const char *largekey; do { largekey = Info_FindLargestKey(s, maxsize); @@ -453,6 +750,7 @@ void Info_SetValueForStarKey(char *s, const char *key, const char *value, int ma } *s = 0; } +#endif void Info_SetValueForKey(char *s, const char *key, const char *value, int maxsize) { @@ -541,6 +839,89 @@ void Info_Print(const char *s) qboolean Info_IsValid(const char *s) { +#ifdef REHLDS_FIXES + struct { + const char* start; + size_t len; + } existingKeys[MAX_INFO_STRING * 2 / 4]; + size_t existingKeysNum = 0; + + auto isAlreadyExists = [&](const char* key, size_t len) + { + for (size_t i = 0; i < existingKeysNum; i++) { + if (len == existingKeys[i].len && !Q_memcmp(key, existingKeys[i].start, existingKeys[i].len)) + return true; + } + return false; + }; + + while (*s == '\\') + { + const char* key = ++s; + + // keys and values are separated by another slash + while (*s != '\\') + { + // key should end with a '\', not a NULL + if (*s == '\0') + return FALSE; + + // quotes are deprecated + if (*s == '"') + return FALSE; + + // ".." deprecated. don't know why. model path? + if (*s == '.' && *(s + 1) == '.') + return FALSE; + + s++; + } + + size_t keyLen = s - key; + if (keyLen == 0 || keyLen >= MAX_KV_LEN) + return FALSE; + + if (isAlreadyExists(key, keyLen)) + return FALSE; + + const char* value = ++s; // skip the slash + + // values should be ended by eos or slash + while (*s != '\\' && *s != '\0') + { + // quotes are deprecated + if (*s == '"') + return FALSE; + + // ".." deprecated + if (*s == '.' && *(s + 1) == '.') + return FALSE; + + s++; + } + + size_t valueLen = s - value; + if (valueLen == 0 || valueLen >= MAX_KV_LEN) + return FALSE; + + if (*s == '\0') + return TRUE; + + if (existingKeysNum == ARRAYSIZE(existingKeys)) + return FALSE; + + existingKeys[existingKeysNum].start = key; + existingKeys[existingKeysNum].len = keyLen; + existingKeysNum++; + } + + return FALSE; +#else + char key[MAX_KV_LEN]; + char value[MAX_KV_LEN]; + char *c; + int nCount; + while (*s) { if (*s == '\\') @@ -548,48 +929,46 @@ qboolean Info_IsValid(const char *s) s++; // skip the slash } - // Returns character count - // -1 - error - // 0 - string size is zero - enum class AllowNull { - Yes, - No, - }; - auto validate = [&s](AllowNull allowNull) -> int + // Copy a key + nCount = 0; + c = key; + while (*s != '\\') { - int nCount = 0; - - for(; *s != '\\'; nCount++, s++) + if (!*s) { - if (!*s) - { - return (allowNull == AllowNull::Yes) ? nCount : -1; - } - - if (nCount >= MAX_KV_LEN) - { - return -1; // string length should be less then MAX_KV_LEN - } - -#ifdef REHLDS_FIXES - if (*s == '\"') - { - return -1; // string should not contain " - } -#endif + return FALSE; // key should end with a \, not a NULL } - return nCount; - }; - - if (validate(AllowNull::No) == -1) - { - return FALSE; + if (nCount >= MAX_KV_LEN) + { + return FALSE; // key length should be less then MAX_KV_LEN + } + *c++ = *s++; + nCount++; } - s++; // Skip slash + *c = 0; + s++; // skip the slash - if (validate(AllowNull::Yes) <= 0) + // Copy a value + nCount = 0; + c = value; + while (*s != '\\') { - return FALSE; + if (!*s) + { + break; // allow value to be ended with NULL + } + if (nCount >= MAX_KV_LEN) + { + return FALSE; // value length should be less then MAX_KV_LEN + } + *c++ = *s++; + nCount++; + } + *c = 0; + + if (value[0] == 0) + { + return FALSE; // empty values are not valid } if (!*s) @@ -599,52 +978,89 @@ qboolean Info_IsValid(const char *s) } return FALSE; +#endif } #ifdef REHLDS_FIXES -void Info_CollectFields(char *destInfo, const char *srcInfo, const char *collectedKeysOfFields) +void Info_SetFieldsToTransmit() { - char keys[MAX_INFO_STRING]; - Q_strcpy(keys, collectedKeysOfFields); + // clean all + for (auto field : g_info_transmitted_fields) { + free(field->name); + delete field; + } + g_info_transmitted_fields.clear(); - size_t userInfoLength = 0; - for (const char *key = strtok(keys, "\\"); key; key = strtok(nullptr, "\\")) + char keys[512]; + Q_strlcpy(keys, sv_rehlds_userinfo_transmitted_fields.string); + + auto isIntegerField = [](const char* key) { - const char *value = Info_ValueForKey(srcInfo, key); + for (auto& x : g_info_important_fields) { + if (!Q_strcmp(key, x.name)) + return x.integer; + } + return false; + }; + for (char *key = Q_strtok(keys, "\\"); key; key = Q_strtok(nullptr, "\\")) + { + if (key[0] == '_') { + Con_Printf("%s: private key '%s' couldn't be transmitted.\n", __FUNCTION__, key); + continue; + } + + if (Q_strlen(key) >= MAX_KV_LEN) { + Con_Printf("%s: key '%s' is too long (should be < %i characters)\n", __FUNCTION__, key, MAX_KV_LEN); + continue; + } + + if (std::find_if(g_info_transmitted_fields.begin(), g_info_transmitted_fields.end(), [key](info_field_t* field) { return !Q_strcmp(key, field->name); }) == g_info_transmitted_fields.end()) { + auto field = new info_field_t; + field->name = Q_strdup(key); + field->integer = isIntegerField(key); + g_info_transmitted_fields.push_back(field); + } + } +} + +void Info_CollectFields(char *destInfo, const char *srcInfo, size_t maxsize) +{ + if (g_info_transmitted_fields.empty()) { + Q_strlcpy(destInfo, srcInfo, maxsize); + Info_RemovePrefixedKeys(destInfo, '_'); + return; + } + + char add[512], valueBuf[32]; + size_t userInfoLength = 0; + + for (auto field : g_info_transmitted_fields) + { + const char *value = Info_ValueForKey(srcInfo, field->name); if (value[0] == '\0') continue; - // Integer fields - if (!Q_strcmp(key, "*hltv") - || !Q_strcmp(key, "bottomcolor") - || !Q_strcmp(key, "topcolor")) + // clean garbage from integer fields + if (field->integer) { + // don't send zero fields int intValue = Q_atoi(value); - if (!intValue) continue; - destInfo[userInfoLength++] = '\\'; - Q_strcpy(&destInfo[userInfoLength], key); - userInfoLength += Q_strlen(key); - - destInfo[userInfoLength++] = '\\'; - userInfoLength += Q_sprintf(&destInfo[userInfoLength], "%d", intValue); - } - // String fields - else - { - destInfo[userInfoLength++] = '\\'; - Q_strcpy(&destInfo[userInfoLength], key); - userInfoLength += Q_strlen(key); - - destInfo[userInfoLength++] = '\\'; - Q_strcpy(&destInfo[userInfoLength], value); - userInfoLength += Q_strlen(value); + Q_sprintf(valueBuf, "%i", intValue); + value = valueBuf; } + // don't write truncated keys/values + size_t len = Q_sprintf(add, "\\%s\\%s", field->name, value); + if (userInfoLength + len < maxsize) { + Q_strcpy(destInfo + userInfoLength, add); + userInfoLength += len; + } } + destInfo[userInfoLength] = '\0'; } #endif // REHLDS_FIXES diff --git a/rehlds/engine/info.h b/rehlds/engine/info.h index 9ac4542..ce7736e 100644 --- a/rehlds/engine/info.h +++ b/rehlds/engine/info.h @@ -48,11 +48,16 @@ const char *Info_ValueForKey(const char *s, const char *key); void Info_RemoveKey(char *s, const char *key); void Info_RemovePrefixedKeys(char *s, const char prefix); qboolean Info_IsKeyImportant(const char *key); -char *Info_FindLargestKey(char *s, int maxsize); +const char *Info_FindLargestKey(const char *s, int maxsize); +#ifdef REHLDS_FIXES +qboolean Info_SetValueForStarKey(char *s, const char *key, const char *value, size_t maxsize); +#else void Info_SetValueForStarKey(char *s, const char *key, const char *value, int maxsize); +#endif void Info_SetValueForKey(char *s, const char *key, const char *value, int maxsize); void Info_Print(const char *s); qboolean Info_IsValid(const char *s); #ifdef REHLDS_FIXES -void Info_CollectFields(char *destInfo, const char *srcInfo, const char *collectedKeysOfFields); +void Info_SetFieldsToTransmit(); +void Info_CollectFields(char *destInfo, const char *srcInfo, size_t maxsize); #endif diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 21d7de9..1dd8a16 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -1927,7 +1927,7 @@ int EXT_FUNC SV_CheckKeyInfo_internal(netadr_t *adr, char *protinfo, unsigned sh s = Info_ValueForKey(protinfo, "raw"); - if (s[0] == 0 || (nAuthProtocol == 2 && Q_strlen(s) != 32)) + if (s[0] == '\0' || (nAuthProtocol == 2 && Q_strlen(s) != 32)) { SV_RejectConnection(adr, "Invalid authentication certificate length.\n"); return 0; @@ -2051,7 +2051,6 @@ int SV_CheckUserInfo(netadr_t *adr, char *userinfo, qboolean bIsReconnecting, in const char *s; char newname[MAX_NAME]; int proxies; - int i; if (!NET_IsLocalAddress(*adr)) { @@ -2076,13 +2075,15 @@ int SV_CheckUserInfo(netadr_t *adr, char *userinfo, qboolean bIsReconnecting, in } } - i = Q_strlen(userinfo); +#ifndef REHLDS_FIXES + int i = Q_strlen(userinfo); if (i <= 4 || Q_strstr(userinfo, "\\\\") || userinfo[i - 1] == '\\') { SV_RejectConnection(adr, "Unknown HLTV client type.\n"); return 0; } +#endif Info_RemoveKey(userinfo, "password"); @@ -2122,9 +2123,9 @@ int SV_CheckUserInfo(netadr_t *adr, char *userinfo, qboolean bIsReconnecting, in #endif #ifdef REHLDS_FIXES - if (name[0] == 0 || !Q_stricmp(name, "console") || Q_strstr(name, "..") || Q_strstr(name, "\"") || Q_strstr(name, "\\")) + if (name[0] == '\0' || !Q_stricmp(name, "console")) #else // REHLDS_FIXES - if (name[0] == 0 || !Q_stricmp(name, "console") || Q_strstr(name, "..") != NULL) + if (name[0] == '\0' || !Q_stricmp(name, "console") || Q_strstr(name, "..") != NULL) #endif // REHLDS_FIXES { Info_SetValueForKey(userinfo, "name", "unnamed", MAX_INFO_STRING); @@ -2137,11 +2138,10 @@ int SV_CheckUserInfo(netadr_t *adr, char *userinfo, qboolean bIsReconnecting, in if (SV_CheckForDuplicateNames(userinfo, bIsReconnecting, nReconnectSlot)) { Q_strncpy(name, Info_ValueForKey(userinfo, "name"), MAX_NAME - 1); - name[MAX_NAME - 1] = 0; + name[MAX_NAME - 1] = '\0'; } s = Info_ValueForKey(userinfo, "*hltv"); - if (!s[0]) return 1; @@ -3750,17 +3750,12 @@ void SV_FullClientUpdate(client_t *cl, sizebuf_t *sb) char info[MAX_INFO_STRING]; #ifdef REHLDS_FIXES - if (sv_rehlds_userinfo_transmitted_fields.string[0] != '\0') - { - Info_CollectFields(info, cl->userinfo, sv_rehlds_userinfo_transmitted_fields.string); - } - else + Info_CollectFields(info, cl->userinfo, MAX_INFO_STRING); +#else // REHLDS_FIXES + Q_strncpy(info, cl->userinfo, sizeof(info) - 1); + info[sizeof(info) - 1] = '\0'; + Info_RemovePrefixedKeys(info, '_'); #endif // REHLDS_FIXES - { - Q_strncpy(info, cl->userinfo, sizeof(info) - 1); - info[sizeof(info) - 1] = 0; - Info_RemovePrefixedKeys(info, '_'); - } g_RehldsHookchains.m_SV_WriteFullClientUpdate.callChain(SV_WriteFullClientUpdate_internal, GetRehldsApiClient(cl), info, MAX_INFO_STRING, sb, GetRehldsApiClient((sb == &g_psv.reliable_datagram) ? nullptr : host_client)); } @@ -4904,12 +4899,7 @@ void SV_ExtractFromUserinfo(client_t *cl) Q_UnicodeRepair(newname); } - if (newname[0] == '\0' || !Q_stricmp(newname, "console") -#ifdef REHLDS_FIXES - || Q_strstr(newname, "..") || Q_strstr(newname, "\"") || Q_strstr(newname, "\\")) -#else // REHLDS_FIXES - ) -#endif // REHLDS_FIXES + if (newname[0] == '\0' || !Q_stricmp(newname, "console")) { Info_SetValueForKey(userinfo, "name", "unnamed", MAX_INFO_STRING); } @@ -4930,26 +4920,26 @@ void SV_ExtractFromUserinfo(client_t *cl) ISteamGameServer_BUpdateUserData(cl->network_userid.m_SteamID, cl->name, 0); val = Info_ValueForKey(userinfo, "rate"); - if (val[0] != 0) + if (val[0] != '\0') { i = Q_atoi(val); cl->netchan.rate = Q_clamp(float(i), MIN_RATE, MAX_RATE); } val = Info_ValueForKey(userinfo, "topcolor"); - if (val[0] != 0) + if (val[0] != '\0') cl->topcolor = Q_atoi(val); else Con_DPrintf("topcolor unchanged for %s\n", cl->name); val = Info_ValueForKey(userinfo, "bottomcolor"); - if (val[0] != 0) + if (val[0] != '\0') cl->bottomcolor = Q_atoi(val); else Con_DPrintf("bottomcolor unchanged for %s\n", cl->name); val = Info_ValueForKey(userinfo, "cl_updaterate"); - if (val[0] != 0) + if (val[0] != '\0') { i = Q_atoi(val); if (i >= 10) @@ -4959,13 +4949,13 @@ void SV_ExtractFromUserinfo(client_t *cl) } val = Info_ValueForKey(userinfo, "cl_lw"); - cl->lw = val[0] != 0 ? Q_atoi(val) != 0 : 0; + cl->lw = val[0] != '\0' ? Q_atoi(val) != 0 : 0; val = Info_ValueForKey(userinfo, "cl_lc"); - cl->lc = val[0] != 0 ? Q_atoi(val) != 0 : 0; + cl->lc = val[0] != '\0' ? Q_atoi(val) != 0 : 0; val = Info_ValueForKey(userinfo, "*hltv"); - cl->proxy = val[0] != 0 ? Q_atoi(val) == TYPE_PROXY : 0; + cl->proxy = val[0] != '\0' ? Q_atoi(val) == TYPE_PROXY : 0; SV_CheckUpdateRate(&cl->next_messageinterval); SV_CheckRate(cl); @@ -5803,6 +5793,10 @@ void EXT_FUNC SV_ActivateServer_internal(int runPhysics) Q_sprintf(szCommand, "exec %s\n", mapchangecfgfile.string); Cbuf_AddText(szCommand); } + +#ifdef REHLDS_FIXES + Info_SetFieldsToTransmit(); +#endif } void SV_ServerShutdown(void) diff --git a/rehlds/public/strtools.h b/rehlds/public/strtools.h index 8b414bf..5cdf8c4 100644 --- a/rehlds/public/strtools.h +++ b/rehlds/public/strtools.h @@ -80,6 +80,7 @@ inline char *_strlwr(char *start) #define Q_strstr A_strstr #define Q_strchr strchr #define Q_strrchr strrchr + #define Q_strtok strtok #define Q_strlwr A_strtolower #define Q_strupr A_strtoupper #define Q_sprintf sprintf @@ -120,6 +121,7 @@ inline char *_strlwr(char *start) #define Q_strstr strstr #define Q_strchr strchr #define Q_strrchr strrchr + #define Q_strtok strtok #define Q_strlwr _strlwr #define Q_strupr _strupr #define Q_sprintf sprintf @@ -144,18 +146,17 @@ inline char *_strlwr(char *start) #define Q_fmod fmod #endif // #if defined(ASMLIB_H) && defined(HAVE_OPT_STRTOOLS) -// a safe variant of strcpy that truncates the result to fit in the destination buffer -template -char *Q_strlcpy(char (&dest)[size], const char *src) { +// size - sizeof(buffer) +inline char *Q_strlcpy(char *dest, const char *src, size_t size) { Q_strncpy(dest, src, size - 1); dest[size - 1] = '\0'; return dest; } -inline char *Q_strnlcpy(char *dest, const char *src, size_t n) { - Q_strncpy(dest, src, n - 1); - dest[n - 1] = '\0'; - return dest; +// a safe variant of strcpy that truncates the result to fit in the destination buffer +template +char *Q_strlcpy(char (&dest)[size], const char *src) { + return Q_strlcpy(dest, src, size); } // safely concatenate two strings. diff --git a/rehlds/unittests/info_tests.cpp b/rehlds/unittests/info_tests.cpp index 164be30..8e176c8 100644 --- a/rehlds/unittests/info_tests.cpp +++ b/rehlds/unittests/info_tests.cpp @@ -3,8 +3,6 @@ #include "cppunitlite/TestHarness.h" TEST(PrefixedKeysRemove, Info, 1000) { - EngineInitializer engInitGuard; - struct testdata_t { const char* inData; const char* outData; @@ -12,9 +10,7 @@ TEST(PrefixedKeysRemove, Info, 1000) { testdata_t testdata[] = { { "", "" }, - { "key\\value", "key\\value" }, { "\\key\\value", "\\key\\value" }, - { "_key\\value", "" }, { "\\_key\\value", "" }, { "\\k\\v\\_u\\t\\y\\z", "\\k\\v\\y\\z" }, { "\\_k\\v\\u\\t\\y\\z", "\\u\\t\\y\\z" }, @@ -34,8 +30,6 @@ TEST(PrefixedKeysRemove, Info, 1000) { } TEST(SetValueForStarKey, Info, 1000) { - EngineInitializer engInitGuard; - struct testdata_t { const char* initialInfo; const char* key; @@ -45,20 +39,26 @@ TEST(SetValueForStarKey, Info, 1000) { testdata_t testdata[] = { // Behavior - { "", "a", "b", "\\a\\b" }, - { "\\a\\b\\c\\d", "a", "b", "\\c\\d\\a\\b" }, - { "a\\b\\c\\d", "a", "b", "\\c\\d\\a\\b" }, + { "\\a\\b", "c", "d", "\\a\\b\\c\\d" }, + { "\\a\\b\\c\\d", "b", "f", "\\a\\b\\c\\d\\b\\f" }, + { "\\a\\b\\c\\d", "c", "", "\\a\\b" }, + { "\\a\\b\\c\\d\\e\\f", "c", "", "\\a\\b\\e\\f" }, + { "\\a\\b\\c\\d\\e\\f", "z", "", "\\a\\b\\c\\d\\e\\f" }, + { "\\a\\b\\c\\d", "a", "e", "\\c\\d\\a\\e" }, { "\\a\\b\\c\\d", "e", "f", "\\a\\b\\c\\d\\e\\f" }, - { "a\\b\\c\\d", "e", "f", "a\\b\\c\\d\\e\\f" }, { "\\a\\b\\c\\d", "b", "c", "\\a\\b\\c\\d\\b\\c" }, - { "\\a\\b\\c\\d\\e\\f", "c", "q", "\\a\\b\\e\\f\\c\\q" }, - + { "\\a\\b\\c\\d", "team", "aBcD", "\\a\\b\\c\\d\\team\\abcd" }, - { "\\a\\b\\c", "e", "f", "\\a\\b\\c\\e\\f" }, - { "\\a\\b\\c\\", "e", "f", "\\a\\b\\c\\\\e\\f" }, - { "\\a\\b\\\\c", "e", "f", "\\a\\b\\\\c\\e\\f" }, + // Invalid keys/values + { "\\a\\b", "c", nullptr, "\\a\\b" }, + { "\\a\\b", nullptr, "c", "\\a\\b" }, + { "\\a\\b", "c", "d..", "\\a\\b" }, + { "\\a\\b", "..c", "d", "\\a\\b" }, + { "\\a\\b", "c\"", "d", "\\a\\b" }, + { "\\a\\b", "c", "d\"", "\\a\\b" }, + { "\\a\\b", "c\\", "d", "\\a\\b" }, + { "\\a\\b", "c", "d\\", "\\a\\b" }, - //limits { //do nothing since 'team' is not important key "\\cl_updaterate\\100\\topcolor\\60\\name\\abcdefghijklmnop\\*sid\\12332432525345\\_vgui_menus\\1\\model\\urban\\somelargeuselesskey\\12312321321323123123123213123123123123123123123123123123123123123dasdsad\\_cl_autowepswitch\\1", @@ -95,9 +95,44 @@ TEST(SetValueForStarKey, Info, 1000) { } } -TEST(RemoveKeyValue, Info, 1000) { - EngineInitializer engInitGuard; +#ifdef REHLDS_FIXES +TEST(SetValueForStarKeyResult, Info, 1000) { + struct testdata_t { + const char* initialInfo; + const char* key; + const char* value; + bool success; + }; + testdata_t testdata[] = { + // Behavior + { "\\a\\b", "c", "d", true }, + { "\\a\\b\\c\\d", "b", "f", true }, + { "\\a\\b\\c\\d", "b", "c", true }, + + // Invalid keys/values + { "\\a\\b", "c", nullptr, false }, + { "\\a\\b", nullptr, "c", false }, + { "\\a\\b", "c", "d..", false }, + { "\\a\\b", "..c", "d", false }, + { "\\a\\b", "c\"", "d", false }, + { "\\a\\b", "c", "d\"", false }, + { "\\a\\b", "c\\", "d", false }, + { "\\a\\b", "c", "d\\", false }, + }; + + for (int i = 0; i < ARRAYSIZE(testdata); i++) { + testdata_t* d = &testdata[i]; + char localInfo[256]; + strcpy(localInfo, d->initialInfo); + localInfo[255] = 0; + bool result = Info_SetValueForStarKey(localInfo, d->key, d->value, 256); + CHECK("Invalid info string", d->success == result); + } +} +#endif + +TEST(RemoveKeyValue, Info, 1000) { struct testdata_t { const char* initialInfo; const char* key; @@ -107,18 +142,14 @@ TEST(RemoveKeyValue, Info, 1000) { testdata_t testdata[] = { { "", "a", "" }, { "\\a\\b", "a", "" }, - { "\\a\\", "a", "" }, - { "\\a\\\\", "a", "\\" }, - { "\\a", "a", "" }, - { "a", "a", "" }, - { "a\\", "a", "" }, - { "a\\b", "a", "" }, - { "a\\b\\", "a", "\\" }, { "\\a\\b\\c\\d\\e\\f", "d", "\\a\\b\\c\\d\\e\\f" }, { "\\a\\b\\c\\d\\e\\f", "c", "\\a\\b\\e\\f" }, - { "a\\b\\c\\d\\e\\f", "d", "a\\b\\c\\d\\e\\f" }, - { "a\\b\\c\\d\\e\\f", "c", "a\\b\\e\\f" }, - { "a\\b\\c\\d\\e\\f", "a", "\\c\\d\\e\\f" }, +#ifdef REHLDS_FIXES + { "\\abc\\def\\x\\y\\ab\\cd", "ab", "\\abc\\def\\x\\y" }, +#else + { "\\abc\\def\\x\\y\\ab\\cd", "ab", "\\x\\y" }, +#endif + { "\\ab\\cd", "abc", "\\ab\\cd" } }; for (int i = 0; i < ARRAYSIZE(testdata); i++) { @@ -131,8 +162,6 @@ TEST(RemoveKeyValue, Info, 1000) { } TEST(GetKeyValue, Info, 1000) { - EngineInitializer engInitGuard; - struct testdata_t { const char* info; const char* key; @@ -145,16 +174,8 @@ TEST(GetKeyValue, Info, 1000) { { "\\a\\", "a", "" }, { "\\a\\\\", "a", "" }, { "\\a", "a", "" }, - { "a", "a", "" }, - { "a\\", "a", "" }, - { "a\\b", "a", "b" }, - { "a\\b\\", "a", "b" }, { "\\a\\b\\c\\d\\e\\f", "d", "" }, { "\\a\\b\\c\\d\\e\\f", "c", "d" }, - { "a\\b\\c\\d\\e\\f", "d", "" }, - { "a\\b\\c\\d\\e\\f", "c", "d" }, - - { "a\\b\\c\\d\\e\\f", "e", "f" }, { "\\a\\b\\c\\d\\e\\f", "e", "f" }, }; @@ -166,3 +187,122 @@ TEST(GetKeyValue, Info, 1000) { ZSTR_EQUAL("Invalid info value", d->result, res); } } + +TEST(FindLargestKey, Info, 1000) { + struct testdata_t { + const char* info; + const char* result; + }; + + testdata_t testdata[] = { + { "", "" }, + { "\\name\\a\\model\\b", "" }, + { "\\name\\a\\model\\b\\c\\d", "c" }, + { "\\name\\a\\1234567890abcdef\\b\\model\\c\\1234567890abcdefghi\\d\\rate\\1000", "1234567890abcdefghi" }, + { "\\name\\a\\1234567890abcdefghi\\b\\model\\c\\1234567890abcdef\\d\\rate\\1000", "1234567890abcdefghi" } + }; + + for (int i = 0; i < ARRAYSIZE(testdata); i++) { + testdata_t* d = &testdata[i]; + + const char* res = Info_FindLargestKey(d->info, MAX_INFO_STRING); + ZSTR_EQUAL("Invalid info value", d->result, res); + } +} + +TEST(InfoIsValid, Info, 1000) { + struct testdata_t { + const char* info; + qboolean result; + }; + + testdata_t testdata[] = { + { "", false }, // by original design +#ifdef REHLDS_FIXES + { "a\\b", false }, +#endif + { "\\a\\b", true }, + { "\\a\\b\\c", false }, + { "\\a\\b\\c\\", false }, + { "\\a\\b\\c\\\\", false }, +#ifdef REHLDS_FIXES + { "\\a\\b\\\\d", false }, + { "\\a\\b\\\\d\\", false }, + { "\\a\\b..c", false }, + { "\\a\\b\"c", false }, + { "\\a..b\\c", false }, + { "\\a\"b\\c", false }, +#endif + { "\\a\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", false }, + { "\\bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\\c", false }, +#ifdef REHLDS_FIXES + { "\\a\\b\\c\\d\\a\\e", false }, +#endif + { "\\aaab\\b\\c\\d\\aaa\\e", true }, + { "\\aaa\\b\\c\\d\\aaab\\e", true } + }; + + for (int i = 0; i < ARRAYSIZE(testdata); i++) { + testdata_t* d = &testdata[i]; + + char error[256]; + snprintf(error, sizeof error, "Invalid result for string '%s'", d->info); + + qboolean res = Info_IsValid(d->info); + CHECK(error, d->result == res); + } +} + +#ifdef REHLDS_FIXES +TEST(InfoCollectFields, Info, 1000) +{ + struct testdata_t { + const char* info; + const char* cvar; + const char* result; + }; + + testdata_t testdata[] = { + { "\\cl_updaterate\\100\\topcolor\\60\\name\\abcdefghijklmnop\\*sid\\12332432525345\\_vgui_menus\\1\\model\\urban\\anykey\\1", "", "\\cl_updaterate\\100\\topcolor\\60\\name\\abcdefghijklmnop\\*sid\\12332432525345\\model\\urban\\anykey\\1" }, + { "\\cl_updaterate\\100\\topcolor\\60\\name\\abcdefghijklmnop\\*sid\\12332432525345\\_vgui_menus\\1\\model\\urban", "rate", "" }, + { "\\cl_updaterate\\100\\topcolor\\60\\name\\abcdefghijklmnop\\*sid\\12332432525345\\_vgui_menus\\1\\model\\urban", "topcolor\\*sid\\_vgui_menus", "\\topcolor\\60\\*sid\\12332432525345" }, + { "\\*hltv\\1dsgs", "*hltv", "\\*hltv\\1" }, + { "\\name\\player", "bottomcolor\\name", "\\name\\player" }, + }; + + for (int i = 0; i < ARRAYSIZE(testdata); i++) { + testdata_t* d = &testdata[i]; + + char destinfo[MAX_INFO_STRING]; + sv_rehlds_userinfo_transmitted_fields.string = (char *)d->cvar; + Info_SetFieldsToTransmit(); + Info_CollectFields(destinfo, d->info, MAX_INFO_STRING); + + ZSTR_EQUAL("Invalid info string", d->result, destinfo); + } +} +#endif + +TEST(Info_IsKeyImportant, Info, 1000) +{ + struct testdata_t { + const char* key; + bool result; + }; + + testdata_t testdata[] = { + { "bottomcolor", true }, + { "bot", false }, + { "*any", true }, + { "_any", false }, + { "model2", false } + }; + + for (int i = 0; i < ARRAYSIZE(testdata); i++) { + testdata_t* d = &testdata[i]; + + bool result = Info_IsKeyImportant(d->key); + + CHECK("wrong result", d->result == result); + } +} From 2fa76553709550c1eab8fe28423c26f725a32ba8 Mon Sep 17 00:00:00 2001 From: s1lent Date: Sun, 6 May 2018 13:33:38 +0700 Subject: [PATCH 5/7] Bugfix Info_ValueForKey: Fix wrong comparison --- rehlds/common/port.h | 2 +- rehlds/engine/info.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/rehlds/common/port.h b/rehlds/common/port.h index 835ca2b..fc12e70 100644 --- a/rehlds/common/port.h +++ b/rehlds/common/port.h @@ -105,7 +105,7 @@ extern char g_szEXEName[ 4096 ]; #define _snprintf snprintf - + #if defined(OSX) #define SO_ARCH_SUFFIX ".dylib" #else diff --git a/rehlds/engine/info.cpp b/rehlds/engine/info.cpp index 0bd1cbd..82791d1 100644 --- a/rehlds/engine/info.cpp +++ b/rehlds/engine/info.cpp @@ -73,6 +73,7 @@ const char* EXT_FUNC Info_ValueForKey(const char *s, const char *lookup) static char valueBuf[INFO_MAX_BUFFER_VALUES][MAX_KV_LEN]; static int valueIndex; + size_t lookupLen = Q_strlen(lookup); while (*s == '\\') { // skip starting slash @@ -87,7 +88,6 @@ const char* EXT_FUNC Info_ValueForKey(const char *s, const char *lookup) s++; } - size_t keyLen = s - key; const char* value = ++s; // skip separating slash // skip value @@ -96,7 +96,7 @@ const char* EXT_FUNC Info_ValueForKey(const char *s, const char *lookup) size_t valueLen = Q_min(s - value, MAX_KV_LEN - 1); - if (!Q_strncmp(key, lookup, keyLen)) + if (!Q_strncmp(key, lookup, lookupLen)) { char* dest = valueBuf[valueIndex]; Q_memcpy(dest, value, valueLen); @@ -865,7 +865,7 @@ qboolean Info_IsValid(const char *s) // key should end with a '\', not a NULL if (*s == '\0') return FALSE; - + // quotes are deprecated if (*s == '"') return FALSE; From 2e2b93131d2442f1eb77eb0efab9d6cd8b68f2af Mon Sep 17 00:00:00 2001 From: s1lent Date: Mon, 7 May 2018 01:44:37 +0700 Subject: [PATCH 6/7] Closes #611 --- rehlds/engine/info.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rehlds/engine/info.cpp b/rehlds/engine/info.cpp index 82791d1..6680d35 100644 --- a/rehlds/engine/info.cpp +++ b/rehlds/engine/info.cpp @@ -88,6 +88,7 @@ const char* EXT_FUNC Info_ValueForKey(const char *s, const char *lookup) s++; } + size_t keyLen = s - key; const char* value = ++s; // skip separating slash // skip value @@ -96,7 +97,7 @@ const char* EXT_FUNC Info_ValueForKey(const char *s, const char *lookup) size_t valueLen = Q_min(s - value, MAX_KV_LEN - 1); - if (!Q_strncmp(key, lookup, lookupLen)) + if (keyLen == lookupLen && !Q_strncmp(key, lookup, lookupLen)) { char* dest = valueBuf[valueIndex]; Q_memcpy(dest, value, valueLen); From 19c22d75380484150a2876eeba9ec35beaaf89d6 Mon Sep 17 00:00:00 2001 From: Dmitry Novikov Date: Mon, 7 May 2018 21:18:56 +0700 Subject: [PATCH 7/7] Skipping local model to pre-cached if no entities to transmit. (#612) * Skipping local model to pre-cached if no entities to transmit. --- rehlds/engine/pr_edict.cpp | 29 +++++++++++++++++++++++++++++ rehlds/engine/sv_main.cpp | 4 ++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/rehlds/engine/pr_edict.cpp b/rehlds/engine/pr_edict.cpp index c8d7dcc..94a02ac 100644 --- a/rehlds/engine/pr_edict.cpp +++ b/rehlds/engine/pr_edict.cpp @@ -265,6 +265,35 @@ char *ED_ParseEdict(char *data, edict_t *ent) Q_strcpy(keyname, "angles"); } +#ifdef REHLDS_FIXES + else if (!Q_strcmp(keyname, "model")) + { + // local model? + if (com_token[0] == '*') + { + // find empty slot + int i; + for (i = 0; i < MAX_MODELS; i++) + { + if (!g_psv.model_precache[i]) + break; + } + + int index = Q_atoi(com_token + 1); + + g_psv.model_precache[i] = localmodels[index]; + g_psv.models[i] = Mod_ForName(localmodels[index], FALSE, FALSE); + g_psv.model_precache_flags[i] |= RES_FATALIFMISSING; + +#ifdef REHLDS_OPT_PEDANTIC + { + int __itmp = i; + g_rehlds_sv.modelsMap.put(g_psv.model_precache[i], __itmp); + } +#endif + } + } +#endif kvd.szClassName = className; kvd.szKeyName = keyname; diff --git a/rehlds/engine/sv_main.cpp b/rehlds/engine/sv_main.cpp index 1dd8a16..5e1ef8a 100644 --- a/rehlds/engine/sv_main.cpp +++ b/rehlds/engine/sv_main.cpp @@ -6043,7 +6043,6 @@ int SV_SpawnServer(qboolean bIsDemo, char *server, char *startspot) g_psv.model_precache[0] = pr_strings; #ifndef REHLDS_FIXES g_psv.generic_precache[0] = pr_strings; -#endif // REHLDS_FIXES for (i = 1; i < g_psv.worldmodel->numsubmodels; i++) { @@ -6058,6 +6057,7 @@ int SV_SpawnServer(qboolean bIsDemo, char *server, char *startspot) } #endif } +#endif // REHLDS_FIXES Q_memset(&g_psv.edicts->v, 0, sizeof(entvars_t)); @@ -7798,7 +7798,7 @@ void SV_Init(void) Cvar_RegisterVariable(&sv_rehlds_attachedentities_playeranimationspeed_fix); Cvar_RegisterVariable(&sv_rehlds_local_gametime); Cvar_RegisterVariable(&sv_rehlds_send_mapcycle); - + Cvar_RegisterVariable(&sv_rollspeed); Cvar_RegisterVariable(&sv_rollangle); #endif