2
0
mirror of https://github.com/rehlds/rehlds.git synced 2024-12-29 08:05:50 +03:00

Merge branch 'master' into ipconnlimit

This commit is contained in:
Dmitry Novikov 2018-05-07 23:07:02 +07:00 committed by GitHub
commit 1328b28304
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1027 additions and 280 deletions

View File

@ -52,7 +52,7 @@ Bugfixed version of rehlds contains an additional cvars:
<li>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
<li>sv_rehlds_stringcmdrate_max_burst // Max burst level of 'string' cmds for ban. Default: 400
<li>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
<li>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: ""
<li>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: ""
<li>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
<li>sv_rehlds_maxclients_from_single_ip // Limit number of connections from the single ip address. Default: 5
</ul>

View File

@ -159,7 +159,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HLTV;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -190,7 +190,7 @@
<Optimization>Disabled</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HLTV;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>

View File

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

View File

@ -77,7 +77,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;_DEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>
</SDLCheck>
<AdditionalIncludeDirectories>$(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
@ -112,7 +112,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;NDEBUG;_WINDOWS;_USRDLL;CORE_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>
</SDLCheck>
<AdditionalIncludeDirectories>$(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>

View File

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

View File

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

View File

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

View File

@ -125,7 +125,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;_DEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\game_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -155,7 +155,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitiHOOons)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;NDEBUG;_WINDOWS;_USRDLL;DEMOPLAYER_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitiHOOons)</PreprocessorDefinitions>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<AdditionalIncludeDirectories>$(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>

View File

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

View File

@ -76,7 +76,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;_DEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)\..\..\;$(ProjectDir)\..\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -107,7 +107,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;NDEBUG;_WINDOWS;_USRDLL;DIRECTOR_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)\..\..\;$(ProjectDir)\..\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>

View File

@ -16,11 +16,12 @@ project.ext.dep_bzip2 = project(':dep/bzip2')
void setupToolchain(NativeBinarySpec b) {
boolean useGcc = project.hasProperty("useGcc")
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(

View File

@ -384,7 +384,7 @@
<PrecompiledHeader>Use</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>HLTV;WIN32;_DEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;_DEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\Director\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
@ -418,7 +418,7 @@
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>HLTV;WIN32;NDEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>HLTV;HLTV_FIXES;WIN32;NDEBUG;_WINDOWS;_USRDLL;PROXY_MODULE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(ProjectDir)\..\src;$(ProjectDir)\..\..\;$(ProjectDir)\..\..\..\;$(ProjectDir)\..\..\Director\src;$(ProjectDir)\..\..\..\common;$(ProjectDir)\..\..\..\engine;$(ProjectDir)\..\..\..\public;$(ProjectDir)\..\..\..\public\rehlds;$(ProjectDir)\..\..\..\pm_shared;$(ProjectDir)\..\..\..\..\dep\bzip2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PrecompiledHeaderFile>precompiled.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>

View File

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

View File

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

View File

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

View File

@ -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,6 +658,46 @@ bool NetChannel::CheckForCompletion(int stream, int intotalbuffers)
return false;
}
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++)
{
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 + 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 buf.CurrentSize() for comparison
return false;
}
}
return true;
}
void NetChannel::ProcessIncoming(unsigned char *data, int size)
{
BitBuffer message(data, size);
@ -721,6 +766,11 @@ void NetChannel::ProcessIncoming(unsigned char *data, int size)
frag_length[i] = message.ReadShort();
}
}
#ifdef HLTV_FIXES
if (!ValidateFragments(message, frag_message, fragid, frag_offset, frag_length))
return;
#endif
}
sequence &= ~(1 << 31);
@ -758,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;
@ -1104,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];

View File

@ -51,32 +51,41 @@ enum
MAX_FLOWS
};
#define MAX_LATENT 32
#define FRAGMENT_MAX_SIZE 1400 // Size of fragmentation buffer internal buffers
#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 UDP_HEADER_SIZE 28
#define MAX_RELIABLE_PAYLOAD 1200
// Client sends normal fragments only while connecting
#define MAX_NORMAL_FRAGMENTS (MAX_POSSIBLE_MSG / CLIENT_FRAGMENT_SIZE_ONCONNECT)
#define MAKE_FRAGID(id, count) (((id & 0xffff) << 16) | (count & 0xffff))
#define FRAG_GETID(fragid) ((fragid >> 16) & 0xffff)
#define FRAG_GETCOUNT(fragid) (fragid & 0xffff)
// 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
#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;
@ -116,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
@ -134,24 +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;
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);
@ -179,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;
@ -216,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

View File

@ -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 <complete info string>\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)
{

View File

@ -31,10 +31,85 @@
// 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<info_field_t *> 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;
size_t lookupLen = Q_strlen(lookup);
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 (keyLen == lookupLen && !Q_strncmp(key, lookup, lookupLen))
{
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 +144,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 +163,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 +172,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 +220,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 +280,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 +382,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 +429,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 +549,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 +722,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 +751,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 +840,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 +930,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 +979,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

View File

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

View File

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

View File

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

View File

@ -1937,7 +1937,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;
@ -2061,7 +2061,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))
{
@ -2086,13 +2085,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");
@ -2132,9 +2133,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);
@ -2147,11 +2148,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;
@ -3760,17 +3760,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));
}
@ -4914,12 +4909,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);
}
@ -4940,26 +4930,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)
@ -4969,13 +4959,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);
@ -5813,6 +5803,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)
@ -6059,7 +6053,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++)
{
@ -6074,6 +6067,7 @@ int SV_SpawnServer(qboolean bIsDemo, char *server, char *startspot)
}
#endif
}
#endif // REHLDS_FIXES
Q_memset(&g_psv.edicts->v, 0, sizeof(entvars_t));

View File

@ -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 <size_t size>
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 <size_t size>
char *Q_strlcpy(char (&dest)[size], const char *src) {
return Q_strlcpy(dest, src, size);
}
// safely concatenate two strings.

View File

@ -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,19 +39,25 @@ 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", "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" },
{ "\\a\\b\\c\\d", "team", "aBcD", "\\a\\b\\c\\d\\team\\abcd" },
// 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
@ -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);
}
}