mirror of
https://github.com/rehlds/rehlds.git
synced 2025-01-14 15:48:04 +03:00
Info code refactoring (#604)
* info.cpp refactoring * Update info unittests * Make _vgui_menus important and fix tests passing
This commit is contained in:
parent
97868baf92
commit
f324df867f
@ -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
|
||||
</ul>
|
||||
|
||||
|
@ -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);
|
||||
|
@ -105,7 +105,7 @@
|
||||
extern char g_szEXEName[ 4096 ];
|
||||
|
||||
#define _snprintf snprintf
|
||||
|
||||
|
||||
#if defined(OSX)
|
||||
#define SO_ARCH_SUFFIX ".dylib"
|
||||
#else
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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<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;
|
||||
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user