Fix crash cases when setting ConVar string using SetValue

Added IsMaterialThreadSetAllowed and QueueMaterialThreadSetValue checks.
This commit is contained in:
Kawe Mazidjatari 2022-08-04 00:21:48 +02:00
parent 9ba5e63ada
commit 0fe554b3d1
6 changed files with 115 additions and 54 deletions

View File

@ -483,12 +483,15 @@ void CRConServer::ProcessMessage(const cl_rcon::request& cl_request)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void CRConServer::Execute(const cl_rcon::request& cl_request, bool bConVar) const void CRConServer::Execute(const cl_rcon::request& cl_request, bool bConVar) const
{ {
ConVar* pConVar = g_pCVar->FindVar(cl_request.requestbuf().c_str()); if (bConVar)
if (pConVar) // Set value without running the callback.
{ {
pConVar->SetValue(cl_request.requestval().c_str()); ConVar* pConVar = g_pCVar->FindVar(cl_request.requestbuf().c_str());
if (pConVar) // Set value without running the callback.
{
pConVar->SetValue(cl_request.requestval().c_str());
}
} }
else if (!bConVar) // Execute command with "<val>". else // Execute command with "<val>".
{ {
Cbuf_AddText(Cbuf_GetCurrentPlayer(), cl_request.requestbuf().c_str(), cmd_source_t::kCommandSrcCode); Cbuf_AddText(Cbuf_GetCurrentPlayer(), cl_request.requestbuf().c_str(), cmd_source_t::kCommandSrcCode);
Cbuf_Execute(); Cbuf_Execute();

View File

@ -56,6 +56,7 @@ class CCommand;
#define FCVAR_SERVER_CANNOT_QUERY (1<<29) // If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue). #define FCVAR_SERVER_CANNOT_QUERY (1<<29) // If this is set, then the server is not allowed to query this cvar's value (via IServerPluginHelpers::StartQueryCvarValue).
#define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command. #define FCVAR_CLIENTCMD_CAN_EXECUTE (1<<30) // IVEngineClient::ClientCmd is allowed to execute this command.
#define FCVAR_MATERIAL_THREAD_MASK ( FCVAR_RELOAD_MATERIALS | FCVAR_RELOAD_TEXTURES | FCVAR_MATERIAL_SYSTEM_THREAD )
/* /*
class ConVar : ConCommandBase, IConVar; [MI] (#classinformer) class ConVar : ConCommandBase, IConVar; [MI] (#classinformer)
dq offset ? ? _R4ConVar@@6B@; const ConVar::`RTTI Complete Object Locator' dq offset ? ? _R4ConVar@@6B@; const ConVar::`RTTI Complete Object Locator'

View File

@ -279,7 +279,7 @@ void ConVar::PurgeHostNames(void) const
{ {
if (ConVar* pCVar = g_pCVar->FindVar(pszHostNames[i])) if (ConVar* pCVar = g_pCVar->FindVar(pszHostNames[i]))
{ {
pCVar->ChangeStringValue("0.0.0.0"); pCVar->SetValue("0.0.0.0");
} }
} }
} }
@ -512,14 +512,17 @@ void ConVar::SetValue(Color value)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ConVar::InternalSetValue(const char* pszValue) void ConVar::InternalSetValue(const char* pszValue)
{ {
if (strcmp(this->m_pParent->m_Value.m_pszString, pszValue) == 0) if (IsFlagSet(this, FCVAR_MATERIAL_THREAD_MASK))
{ {
return; if (g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed())
{
g_pCVar->QueueMaterialThreadSetValue(this, pszValue);
return;
}
} }
this->m_pParent->m_Value.m_pszString = pszValue;
char szTempValue[32]{}; char szTempValue[32];
const char* pszNewValue{}; const char* pszNewValue;
// Only valid for root convars. // Only valid for root convars.
assert(m_pParent == this); assert(m_pParent == this);
@ -533,7 +536,9 @@ void ConVar::InternalSetValue(const char* pszValue)
if (!SetColorFromString(pszValue)) if (!SetColorFromString(pszValue))
{ {
// Not a color, do the standard thing // Not a color, do the standard thing
float flNewValue = static_cast<float>(atof(pszValue)); double dblValue = atof(pszValue); // Use double to avoid 24-bit restriction on integers and allow storing timestamps or dates in convars
float flNewValue = static_cast<float>(dblValue);
if (!IsFinite(flNewValue)) if (!IsFinite(flNewValue))
{ {
Warning(eDLL_T::ENGINE, "Warning: ConVar '%s' = '%s' is infinite, clamping value.\n", GetBaseName(), pszValue); Warning(eDLL_T::ENGINE, "Warning: ConVar '%s' = '%s' is infinite, clamping value.\n", GetBaseName(), pszValue);
@ -566,6 +571,15 @@ void ConVar::InternalSetIntValue(int nValue)
if (nValue == m_Value.m_nValue) if (nValue == m_Value.m_nValue)
return; return;
if (IsFlagSet(this, FCVAR_MATERIAL_THREAD_MASK))
{
if (g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed())
{
g_pCVar->QueueMaterialThreadSetValue(this, nValue);
return;
}
}
assert(m_pParent == this); // Only valid for root convars. assert(m_pParent == this); // Only valid for root convars.
float fValue = static_cast<float>(nValue); float fValue = static_cast<float>(nValue);
@ -595,6 +609,15 @@ void ConVar::InternalSetFloatValue(float flValue)
if (flValue == m_Value.m_fValue) if (flValue == m_Value.m_fValue)
return; return;
if (IsFlagSet(this, FCVAR_MATERIAL_THREAD_MASK))
{
if (g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed())
{
g_pCVar->QueueMaterialThreadSetValue(this, flValue);
return;
}
}
assert(m_pParent == this); // Only valid for root convars. assert(m_pParent == this); // Only valid for root convars.
// Check bounds // Check bounds
@ -742,52 +765,57 @@ bool ConVar::SetColorFromString(const char* pszValue)
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ConVar::ChangeStringValue(const char* pszTempVal) void ConVar::ChangeStringValue(const char* pszTempVal)
{ {
ConVar_ChangeStringValue(this, pszTempVal); assert(!(m_nFlags & FCVAR_NEVER_AS_STRING));
//assert(!(m_nFlags & FCVAR_NEVER_AS_STRING)); char* pszOldValue = reinterpret_cast<char*>(_malloca(m_Value.m_iStringLength));
if (pszOldValue != nullptr)
{
memcpy(pszOldValue, m_Value.m_pszString, m_Value.m_iStringLength);
}
//char* pszOldValue = reinterpret_cast<char*>(_malloca(m_Value.m_iStringLength)); if (pszTempVal)
//if (pszOldValue != nullptr) {
//{ size_t len = strlen(pszTempVal) + 1;
// memcpy(pszOldValue, m_Value.m_pszString, m_Value.m_iStringLength); if (len > m_Value.m_iStringLength)
//} {
if (m_Value.m_pszString)
{
MemAllocSingleton()->Free(m_Value.m_pszString);
}
//if (pszTempVal) m_Value.m_pszString = MemAllocSingleton()->Alloc<char>(len);
//{ m_Value.m_iStringLength = len;
// size_t len = strlen(pszTempVal) + 1; }
else if (!m_Value.m_pszString)
{
m_Value.m_pszString = MemAllocSingleton()->Alloc<char>(len);
m_Value.m_iStringLength = len;
}
memmove(m_Value.m_pszString, pszTempVal, len);
// if (len > m_Value.m_iStringLength) /*****
// { !FIXME:
// if (m_Value.m_pszString) Respawn put additional code here which
// { seems to itterate over a 64bit integer
// MemAllocSingleton()->Free(m_Value.m_pszString); to call the callback several times (as many times as m_iCallbackCount?).
// } ******/
}
else
{
m_Value.m_pszString = nullptr;
}
// m_Value.m_pszString = MemAllocSingleton()->Alloc<const char>(len); pszOldValue = nullptr;
// m_Value.m_iStringLength = len;
// }
// else if (!m_Value.m_pszString)
// {
// m_Value.m_pszString = MemAllocSingleton()->Alloc<const char>(len);
// m_Value.m_iStringLength = len;
// }
// memmove(const_cast<char*>(m_Value.m_pszString), pszTempVal, len);
//}
//else
//{
// m_Value.m_pszString = nullptr;
//}
//pszOldValue = nullptr;
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Purpose: changes the ConVar string value (only use if the new string is equal or lower than this->m_iStringLength). // Purpose: changes the ConVar string value (this is faster than ChangeStringValue,
// only use if the new string is equal or lower than this->m_iStringLength).
// Input : *pszTempVal - flOldValue // Input : *pszTempVal - flOldValue
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void ConVar::ChangeStringValueUnsafe(const char* pszNewValue) void ConVar::ChangeStringValueUnsafe(const char* pszNewValue)
{ {
m_Value.m_pszString = pszNewValue; m_Value.m_pszString = const_cast<char*>(pszNewValue);
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -70,17 +70,17 @@ public:
struct CVValue_t struct CVValue_t
{ {
const char* m_pszString; char* m_pszString;
size_t m_iStringLength; size_t m_iStringLength;
float m_fValue; float m_fValue;
int m_nValue; int m_nValue;
}; };
struct CVCallback_t struct CVCallback_t
{ {
void** m_ppCallback; void** m_ppCallback;
int64_t m_iFlags; int64_t m_iFlags;
char m_Pad[8]; char m_Pad[8];
int64_t m_iTimesChanged; int64_t m_iCallbackCount;
}; };
IConVar* m_pIConVarVFTable{}; //0x0040 IConVar* m_pIConVarVFTable{}; //0x0040
@ -91,7 +91,7 @@ public:
float m_fMinVal {}; //0x0074 float m_fMinVal {}; //0x0074
bool m_bHasMax {}; //0x0078 bool m_bHasMax {}; //0x0078
float m_fMaxVal {}; //0x007C float m_fMaxVal {}; //0x007C
CVCallback_t m_Callback {}; //0x0080 CVCallback_t m_Callback {}; //0x0080 // <-- !FIXME: 'CUtlVector< FnChangeCallback_t > m_fnChangeCallbacks;'
}; //Size: 0x00A0 }; //Size: 0x00A0
/* ==== ICONVAR ========================================================================================================================================================= */ /* ==== ICONVAR ========================================================================================================================================================= */

View File

@ -243,5 +243,29 @@ unordered_map<string, ConCommandBase*> CCVar::DumpToMap(void)
return allConVars; return allConVars;
} }
//-----------------------------------------------------------------------------
// Purpose: deal with queued material system ConVars
//-----------------------------------------------------------------------------
bool CCVar::IsMaterialThreadSetAllowed(void)
{
const int index = 280;
return CallVFunc<bool>(index, this);
}
void CCVar::QueueMaterialThreadSetValue(ConVar* pConVar, float flValue)
{
const int index = 288;
CallVFunc<void>(index, this, pConVar, flValue);
}
void CCVar::QueueMaterialThreadSetValue(ConVar* pConVar, int nValue)
{
const int index = 296;
CallVFunc<void>(index, this, pConVar, nValue);
}
void CCVar::QueueMaterialThreadSetValue(ConVar* pConVar, const char* pValue)
{
const int index = 304;
CallVFunc<void>(index, this, pConVar, pValue);
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
CCVar* g_pCVar = nullptr; CCVar* g_pCVar = nullptr;

View File

@ -182,6 +182,11 @@ public:
ConCommand* FindCommand(const char* pszCommandName); ConCommand* FindCommand(const char* pszCommandName);
CCVarIteratorInternal* FactoryInternalIterator(void); CCVarIteratorInternal* FactoryInternalIterator(void);
unordered_map<string, ConCommandBase*> DumpToMap(void); unordered_map<string, ConCommandBase*> DumpToMap(void);
bool IsMaterialThreadSetAllowed(void);
void QueueMaterialThreadSetValue(ConVar* pConVar, float flValue);
void QueueMaterialThreadSetValue(ConVar* pConVar, int nValue);
void QueueMaterialThreadSetValue(ConVar* pConVar, const char* pValue);
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////