mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Fully implemented ConVar class so we could statically construct all SDK convars, this avoids a level of indirection, and allows for creating ConVar's everywhere in the project. This patch also removed the settings tab of the ImGui server browser, as it has threading issues, while it technically never caused a crash yet, it has been removed as there was no point keeping it vs the work required to make it thread save (it only managed 2 convars which are perfectly manageable through cfg's or the in-game console). Also temporarily disabled the creation of ConVar's in the mod system due to a memory leak, we would allocate and register a convar based on details parsed out of a mod file definition, but never unregister and free it.
1090 lines
31 KiB
C++
1090 lines
31 KiB
C++
//=============================================================================//
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================//
|
|
#include "tier0/tslist.h"
|
|
#include "tier1/convar.h"
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Statically constructed list of ConCommandBases,
|
|
// used for registering them with the ICVar interface
|
|
//-----------------------------------------------------------------------------
|
|
ConCommandBase* ConCommandBase::s_pConCommandBases = NULL;
|
|
IConCommandBaseAccessor* ConCommandBase::s_pAccessor = NULL;
|
|
|
|
static int s_nCVarFlag = 0;
|
|
|
|
// An unique identifier indicating which DLL this convar came from
|
|
static int s_nDLLIdentifier = -1;
|
|
static bool s_bRegistered = false;
|
|
|
|
class CDefaultAccessor : public IConCommandBaseAccessor
|
|
{
|
|
public:
|
|
virtual bool RegisterConCommandBase(ConCommandBase* const pVar)
|
|
{
|
|
// Link to engine's list instead
|
|
g_pCVar->RegisterConCommand(pVar);
|
|
return true;
|
|
}
|
|
};
|
|
|
|
static CDefaultAccessor s_DefaultAccessor;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Called by the framework to register ConCommandBases with the ICVar
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar_Register(int nCVarFlag, IConCommandBaseAccessor* pAccessor)
|
|
{
|
|
if (!g_pCVar || s_bRegistered)
|
|
return;
|
|
|
|
Assert(s_nDLLIdentifier < 0);
|
|
s_bRegistered = true;
|
|
s_nCVarFlag = nCVarFlag;
|
|
s_nDLLIdentifier = g_pCVar->AllocateDLLIdentifier();
|
|
|
|
ConCommandBase* pCur, * pNext;
|
|
|
|
ConCommandBase::s_pAccessor = pAccessor ? pAccessor : &s_DefaultAccessor;
|
|
pCur = ConCommandBase::s_pConCommandBases;
|
|
|
|
while (pCur)
|
|
{
|
|
pNext = pCur->m_pNext;
|
|
pCur->AddFlags(s_nCVarFlag);
|
|
pCur->Init();
|
|
pCur = pNext;
|
|
}
|
|
|
|
g_pCVar->ProcessQueuedMaterialThreadConVarSets();
|
|
|
|
ConCommandBase::s_pConCommandBases = NULL;
|
|
}
|
|
|
|
void ConVar_Unregister()
|
|
{
|
|
if (!g_pCVar || !s_bRegistered)
|
|
return;
|
|
|
|
Assert(s_nDLLIdentifier >= 0);
|
|
|
|
// Do this after unregister!!!
|
|
g_pCVar->UnregisterConCommands(s_nDLLIdentifier);
|
|
s_nDLLIdentifier = -1;
|
|
s_bRegistered = false;
|
|
}
|
|
|
|
ConCommandBase::~ConCommandBase(void)
|
|
{
|
|
if (m_pszCustomUsageString)
|
|
{
|
|
delete[] m_pszCustomUsageString;
|
|
m_pszCustomUsageString = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns true if this is a command
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommandBase::IsCommand(void) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Checks if ConCommand has requested flags.
|
|
// Input : flag -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommandBase::IsFlagSet(const int flag) const
|
|
{
|
|
return (flag & m_nFlags) ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flags -
|
|
//-----------------------------------------------------------------------------
|
|
void ConCommandBase::AddFlags(const int flags)
|
|
{
|
|
m_nFlags |= flags;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: removes specified flags
|
|
//-----------------------------------------------------------------------------
|
|
void ConCommandBase::RemoveFlags(const int flags)
|
|
{
|
|
m_nFlags &= ~flags;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: returns current flags
|
|
//-----------------------------------------------------------------------------
|
|
int ConCommandBase::GetFlags() const
|
|
{
|
|
return m_nFlags;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Return name of the command/var
|
|
// Output : const char
|
|
//-----------------------------------------------------------------------------
|
|
const char* ConCommandBase::GetName(void) const
|
|
{
|
|
return m_pszName;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : const char
|
|
//-----------------------------------------------------------------------------
|
|
const char* ConCommandBase::GetHelpText(void) const
|
|
{
|
|
return m_pszHelpString;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : const char
|
|
//-----------------------------------------------------------------------------
|
|
const char* ConCommandBase::GetUsageText(void) const
|
|
{
|
|
return m_pszStaticUsageString;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sets the ConCommandBase usage text.
|
|
//-----------------------------------------------------------------------------
|
|
void ConCommandBase::SetUsageText(const char* const usageText)
|
|
{
|
|
const char* const szCustomString = m_pszCustomUsageString;
|
|
|
|
// If a custom usage string has been set, return that instead
|
|
if (szCustomString)
|
|
delete[] szCustomString;
|
|
|
|
m_pszCustomUsageString = usageText
|
|
? V_strdup(usageText)
|
|
: nullptr;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Has this cvar been registered
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommandBase::IsRegistered(void) const
|
|
{
|
|
return m_bRegistered;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the DLL identifier
|
|
//-----------------------------------------------------------------------------
|
|
int ConCommandBase::GetDLLIdentifier(void) const
|
|
{
|
|
return s_nDLLIdentifier;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Used internally by OneTimeInit to initialize.
|
|
//-----------------------------------------------------------------------------
|
|
void ConCommandBase::Init()
|
|
{
|
|
if (s_pAccessor)
|
|
{
|
|
s_pAccessor->RegisterConCommandBase(this);
|
|
}
|
|
}
|
|
|
|
void ConCommandBase::Shutdown()
|
|
{
|
|
if (g_pCVar)
|
|
{
|
|
g_pCVar->UnregisterConCommand(this);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *pName -
|
|
// callback -
|
|
// *pHelpString -
|
|
// flags -
|
|
//-----------------------------------------------------------------------------
|
|
ConCommandBase* ConCommandBase::Create(const char* pName, const char* pHelpString, int flags, const char* pszUsageString)
|
|
{
|
|
m_bRegistered = false;
|
|
|
|
// Name should be static data
|
|
Assert(pName);
|
|
m_pszName = pName;
|
|
|
|
m_pszHelpString = pHelpString ? pHelpString : "";
|
|
m_pszStaticUsageString = pszUsageString ? pszUsageString : "";
|
|
|
|
m_pszCustomUsageString = nullptr;
|
|
|
|
m_nFlags = flags;
|
|
|
|
const int releaseMask = FCVAR_ARCHIVE|FCVAR_USERINFO|FCVAR_CHEAT|
|
|
FCVAR_RELEASE|FCVAR_ARCHIVE_PLAYERPROFILE|FCVAR_CLIENTCMD_CAN_EXECUTE;
|
|
|
|
if (!(m_nFlags & releaseMask))
|
|
{
|
|
m_nFlags |= FCVAR_DEVELOPMENTONLY;
|
|
}
|
|
|
|
if (!(m_nFlags & FCVAR_UNREGISTERED))
|
|
{
|
|
m_pNext = s_pConCommandBases;
|
|
s_pConCommandBases = this;
|
|
}
|
|
else
|
|
{
|
|
// It's unregistered
|
|
m_pNext = NULL;
|
|
}
|
|
|
|
// If s_pAccessor is already set (this ConVar is not a global variable),
|
|
// register it.
|
|
if (s_pAccessor)
|
|
{
|
|
Init();
|
|
}
|
|
|
|
return m_pNext;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : const ConCommandBase
|
|
//-----------------------------------------------------------------------------
|
|
ConCommandBase* ConCommandBase::GetNext(void) const
|
|
{
|
|
return m_pNext;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Copies string using local new/delete operators
|
|
// Input : *szFrom -
|
|
// Output : char
|
|
//-----------------------------------------------------------------------------
|
|
char* ConCommandBase::CopyString(const char* szFrom) const
|
|
{
|
|
size_t nLen;
|
|
char* szTo;
|
|
|
|
nLen = strlen(szFrom);
|
|
if (nLen <= 0)
|
|
{
|
|
szTo = new char[1];
|
|
szTo[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
szTo = new char[nLen + 1];
|
|
memmove(szTo, szFrom, nLen + 1);
|
|
}
|
|
return szTo;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Default do nothing function
|
|
//-----------------------------------------------------------------------------
|
|
void DefaultNullSub()
|
|
{
|
|
; /*DO NOTHING*/
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Default console command autocompletion function
|
|
//-----------------------------------------------------------------------------
|
|
int DefaultCompletionFunc(const char* partial, char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH])
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
ConCommand::ConCommand(const char* pName, FnCommandCallbackV1_t callback, const char* pHelpString /*= 0*/,
|
|
int flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/, const char* pszUsageString /*= 0*/)
|
|
{
|
|
m_nNullCallBack = DefaultNullSub;
|
|
m_pSubCallback = nullptr;
|
|
|
|
// Set the callback
|
|
m_fnCommandCallbackV1 = callback;
|
|
m_bUsingNewCommandCallback = false;
|
|
m_bUsingCommandCallbackInterface = false;
|
|
m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc;
|
|
m_bHasCompletionCallback = completionFunc != 0 ? true : false;
|
|
|
|
// Setup the rest
|
|
BaseClass::Create(pName, pHelpString, flags, pszUsageString);
|
|
}
|
|
|
|
ConCommand::ConCommand(const char* pName, FnCommandCallback_t callback, const char* pHelpString /*= 0*/,
|
|
int flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/, const char* pszUsageString /*= 0*/)
|
|
{
|
|
m_nNullCallBack = DefaultNullSub;
|
|
m_pSubCallback = nullptr;
|
|
|
|
// Set the callback
|
|
m_fnCommandCallback = callback;
|
|
m_bUsingNewCommandCallback = true;
|
|
m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc;
|
|
m_bHasCompletionCallback = completionFunc != 0 ? true : false;
|
|
m_bUsingCommandCallbackInterface = false;
|
|
|
|
// Setup the rest
|
|
BaseClass::Create(pName, pHelpString, flags, pszUsageString);
|
|
}
|
|
|
|
ConCommand::ConCommand(const char* pName, ICommandCallback* pCallback, const char* pHelpString /*= 0*/,
|
|
int flags /*= 0*/, ICommandCompletionCallback* pCompletionCallback /*= 0*/, const char* pszUsageString /*= 0*/)
|
|
{
|
|
m_nNullCallBack = DefaultNullSub;
|
|
m_pSubCallback = nullptr;
|
|
|
|
// Set the callback
|
|
m_pCommandCallback = pCallback;
|
|
m_bUsingNewCommandCallback = false;
|
|
m_pCommandCompletionCallback = pCompletionCallback;
|
|
m_bHasCompletionCallback = (pCompletionCallback != 0);
|
|
m_bUsingCommandCallbackInterface = true;
|
|
|
|
// Setup the rest
|
|
BaseClass::Create(pName, pHelpString, flags, pszUsageString);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: destructor
|
|
//-----------------------------------------------------------------------------
|
|
ConCommand::~ConCommand()
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns true if this is a command
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommand::IsCommand(void) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: calls the autocompletion method to get autocompletion suggestions
|
|
//-----------------------------------------------------------------------------
|
|
int ConCommand::AutoCompleteSuggest(const char* partial, CUtlVector< CUtlString >& commands)
|
|
{
|
|
if (m_bUsingCommandCallbackInterface)
|
|
{
|
|
if (!m_pCommandCompletionCallback)
|
|
return 0;
|
|
|
|
return m_pCommandCompletionCallback->CommandCompletionCallback(partial, commands);
|
|
}
|
|
|
|
Assert(m_fnCompletionCallback);
|
|
|
|
if (!m_fnCompletionCallback)
|
|
return 0;
|
|
|
|
char rgpchCommands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH];
|
|
const int iret = (m_fnCompletionCallback)(partial, rgpchCommands);
|
|
|
|
for (int i = 0; i < iret; ++i)
|
|
{
|
|
const CUtlString str = rgpchCommands[i];
|
|
commands.AddToTail(str);
|
|
}
|
|
|
|
return iret;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns true if the console command can autocomplete
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommand::CanAutoComplete(void) const
|
|
{
|
|
return m_bHasCompletionCallback;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: invoke the function if there is one
|
|
//-----------------------------------------------------------------------------
|
|
void ConCommand::Dispatch(const CCommand& command)
|
|
{
|
|
if (m_bUsingNewCommandCallback)
|
|
{
|
|
if (m_fnCommandCallback)
|
|
{
|
|
(*m_fnCommandCallback)(command);
|
|
return;
|
|
}
|
|
}
|
|
else if (m_bUsingCommandCallbackInterface)
|
|
{
|
|
if (m_pCommandCallback)
|
|
{
|
|
m_pCommandCallback->CommandCallback(command);
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_fnCommandCallbackV1)
|
|
{
|
|
(*m_fnCommandCallbackV1)();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Command without callback!!!
|
|
AssertMsg(0, "Encountered ConCommand '%s' without a callback!\n", GetName());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Various constructors
|
|
//-----------------------------------------------------------------------------
|
|
ConVar::ConVar(const char* pName, const char* pDefaultValue, int flags /* = 0 */)
|
|
{
|
|
Create(pName, pDefaultValue, flags);
|
|
}
|
|
|
|
ConVar::ConVar(const char* pName, const char* pDefaultValue, int flags, const char* pHelpString, const char* pUsageString)
|
|
{
|
|
Create(pName, pDefaultValue, flags, pHelpString, false, 0.0, false, 0.0, nullptr, pUsageString);
|
|
}
|
|
|
|
ConVar::ConVar(const char* pName, const char* pDefaultValue, int flags, const char* pHelpString,
|
|
bool bMin, float fMin, bool bMax, float fMax, const char* pUsageString)
|
|
{
|
|
Create(pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax, nullptr, pUsageString);
|
|
}
|
|
|
|
ConVar::ConVar(const char* pName, const char* pDefaultValue, int flags, const char* pHelpString,
|
|
FnChangeCallback_t callback, const char* pUsageString)
|
|
{
|
|
Create(pName, pDefaultValue, flags, pHelpString, false, 0.0, false, 0.0, callback, pUsageString);
|
|
}
|
|
|
|
ConVar::ConVar(const char* pName, const char* pDefaultValue, int flags, const char* pHelpString,
|
|
bool bMin, float fMin, bool bMax, float fMax, FnChangeCallback_t callback, const char* pUsageString)
|
|
{
|
|
Create(pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax, callback, pUsageString);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: destructor
|
|
//-----------------------------------------------------------------------------
|
|
ConVar::~ConVar(void)
|
|
{
|
|
if (m_Value.m_pszString)
|
|
{
|
|
delete[] m_Value.m_pszString;
|
|
m_Value.m_pszString = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::IsCommand(void) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool ConVar::IsFlagSet(int flag) const
|
|
{
|
|
return (flag & m_pParent->m_nFlags) ? true : false;
|
|
}
|
|
|
|
void ConVar::AddFlags(int flags)
|
|
{
|
|
m_pParent->m_nFlags |= flags;
|
|
}
|
|
|
|
int ConVar::GetFlags() const
|
|
{
|
|
return m_pParent->m_nFlags;
|
|
}
|
|
|
|
const char* ConVar::GetName(void) const
|
|
{
|
|
return m_pParent->m_pszName;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the base ConVar name.
|
|
// Output : const char*
|
|
//-----------------------------------------------------------------------------
|
|
const char* ConVar::GetBaseName(void) const
|
|
{
|
|
return m_pParent->m_pszName;
|
|
}
|
|
|
|
int ConVar::GetSplitScreenPlayerSlot(void) const
|
|
{
|
|
// Default implementation (certain FCVAR_USERINFO derive a new type of convar and set this)
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the ConVar help text.
|
|
// Output : const char*
|
|
//-----------------------------------------------------------------------------
|
|
const char* ConVar::GetHelpText(void) const
|
|
{
|
|
return m_pParent->m_pszHelpString;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns the ConVar usage text.
|
|
// Output : const char*
|
|
//-----------------------------------------------------------------------------
|
|
const char* ConVar::GetUsageText(void) const
|
|
{
|
|
const char* const szCustomString = m_pParent->m_pszCustomUsageString;
|
|
|
|
// If a custom usage string has been set, return that instead
|
|
if (szCustomString)
|
|
return szCustomString;
|
|
|
|
return m_pParent->m_pszStaticUsageString;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sets the ConVar usage text.
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetUsageText(const char* const usageText)
|
|
{
|
|
const char* const szCustomString = m_pParent->m_pszCustomUsageString;
|
|
|
|
// If a custom usage string has been set, return that instead
|
|
if (szCustomString)
|
|
delete[] szCustomString;
|
|
|
|
m_pParent->m_pszCustomUsageString = usageText
|
|
? V_strdup(usageText)
|
|
: nullptr;
|
|
}
|
|
|
|
bool ConVar::IsRegistered(void) const
|
|
{
|
|
return m_pParent->m_bRegistered;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sets the ConVar color value from string.
|
|
// Input : *pszValue -
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::InternalSetColorFromString(const char* value)
|
|
{
|
|
bool bColor = false;
|
|
|
|
// Try pulling RGBA color values out of the string
|
|
int nRGBA[4];
|
|
int nParamsRead = sscanf(value, "%i %i %i %i",
|
|
&(nRGBA[0]), &(nRGBA[1]), &(nRGBA[2]), &(nRGBA[3]));
|
|
|
|
if (nParamsRead >= 3)
|
|
{
|
|
// This is probably a color!
|
|
if (nParamsRead == 3)
|
|
{
|
|
// Assume they wanted full alpha
|
|
nRGBA[3] = 255;
|
|
}
|
|
|
|
if (nRGBA[0] >= 0 && nRGBA[0] <= 255 &&
|
|
nRGBA[1] >= 0 && nRGBA[1] <= 255 &&
|
|
nRGBA[2] >= 0 && nRGBA[2] <= 255 &&
|
|
nRGBA[3] >= 0 && nRGBA[3] <= 255)
|
|
{
|
|
// This is definitely a color!
|
|
bColor = true;
|
|
|
|
// Stuff all the values into each byte of our int
|
|
unsigned char* pColorElement = ((unsigned char*)&m_Value.m_nValue);
|
|
pColorElement[0] = (unsigned char)nRGBA[0];
|
|
pColorElement[1] = (unsigned char)nRGBA[1];
|
|
pColorElement[2] = (unsigned char)nRGBA[2];
|
|
pColorElement[3] = (unsigned char)nRGBA[3];
|
|
|
|
// Copy that value into a float (even though this has little meaning)
|
|
m_Value.m_fValue = (float)(m_Value.m_nValue);
|
|
}
|
|
}
|
|
|
|
return bColor;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::InternalSetValue(const char* value)
|
|
{
|
|
if (IsFlagSet(FCVAR_MATERIAL_THREAD_MASK))
|
|
{
|
|
if (g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed())
|
|
{
|
|
g_pCVar->QueueMaterialThreadSetValue(this, value);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Assert(m_pParent == this); // Only valid for root convars.
|
|
|
|
char tempVal[32];
|
|
const char* newVal = value;
|
|
|
|
if (!newVal)
|
|
newVal = "";
|
|
|
|
if (!InternalSetColorFromString(value))
|
|
{
|
|
// Not a color, do the standard thing
|
|
float fNewValue = (float)atof(value);
|
|
if (!IsFinite(fNewValue))
|
|
{
|
|
DevWarning(eDLL_T::COMMON, "Warning: %s = '%s' is infinite, clamping value.\n", GetName(), value);
|
|
fNewValue = FLT_MAX;
|
|
}
|
|
|
|
if (ClampValue(fNewValue))
|
|
{
|
|
V_snprintf(tempVal, sizeof(tempVal), "%f", fNewValue);
|
|
newVal = tempVal;
|
|
}
|
|
|
|
// Redetermine value
|
|
m_Value.m_fValue = fNewValue;
|
|
m_Value.m_nValue = (int)(m_Value.m_fValue);
|
|
}
|
|
|
|
if (!(m_nFlags & FCVAR_NEVER_AS_STRING))
|
|
{
|
|
ChangeStringValue(newVal);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::InternalSetFloatValue(float fNewValue)
|
|
{
|
|
if (fNewValue == m_Value.m_fValue)
|
|
return;
|
|
|
|
if (IsFlagSet(FCVAR_MATERIAL_THREAD_MASK))
|
|
{
|
|
if (g_pCVar && !g_pCVar->IsMaterialThreadSetAllowed())
|
|
{
|
|
g_pCVar->QueueMaterialThreadSetValue(this, fNewValue);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Assert(m_pParent == this); // Only valid for root convars.
|
|
|
|
// Check bounds
|
|
ClampValue(fNewValue);
|
|
|
|
// Redetermine value
|
|
m_Value.m_fValue = fNewValue;
|
|
m_Value.m_nValue = (int)m_Value.m_fValue;
|
|
|
|
if (!(m_nFlags & FCVAR_NEVER_AS_STRING))
|
|
{
|
|
char tempVal[32];
|
|
V_snprintf(tempVal, sizeof(tempVal), "%f", m_Value.m_fValue);
|
|
ChangeStringValue(tempVal);
|
|
}
|
|
else
|
|
{
|
|
Assert(m_fnChangeCallbacks.Count() == 0);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::InternalSetIntValue(int nValue)
|
|
{
|
|
if (nValue == m_Value.m_nValue)
|
|
return;
|
|
|
|
if (IsFlagSet(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.
|
|
|
|
float fValue = (float)nValue;
|
|
if (ClampValue(fValue))
|
|
{
|
|
nValue = (int)(fValue);
|
|
}
|
|
|
|
// Redetermine value
|
|
m_Value.m_fValue = fValue;
|
|
m_Value.m_nValue = nValue;
|
|
|
|
if (!(m_nFlags & FCVAR_NEVER_AS_STRING))
|
|
{
|
|
char tempVal[32];
|
|
V_snprintf(tempVal, sizeof(tempVal), "%d", m_Value.m_nValue);
|
|
ChangeStringValue(tempVal);
|
|
}
|
|
else
|
|
{
|
|
Assert(m_fnChangeCallbacks.Count() == 0);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flMaxVal -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetMax(float flMaxVal)
|
|
{
|
|
m_pParent->m_fMaxVal = flMaxVal;
|
|
m_pParent->m_bHasMax = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flMinVal -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetMin(float flMinVal)
|
|
{
|
|
m_pParent->m_fMinVal = flMinVal;
|
|
m_pParent->m_bHasMin = true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flMinVal -
|
|
// Output : true if there is a min set.
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::GetMin(float& flMinVal) const
|
|
{
|
|
flMinVal = m_pParent->m_fMinVal;
|
|
return m_pParent->m_bHasMin;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flMaxVal -
|
|
// Output : true if there is a max set.
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::GetMax(float& flMaxVal) const
|
|
{
|
|
flMaxVal = m_pParent->m_fMaxVal;
|
|
return m_pParent->m_bHasMax;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: returns the min value.
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float ConVar::GetMinValue(void) const
|
|
{
|
|
return m_pParent->m_fMinVal;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: returns the max value.
|
|
// Output : float
|
|
//-----------------------------------------------------------------------------
|
|
float ConVar::GetMaxValue(void) const
|
|
{
|
|
return m_pParent->m_fMaxVal;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: checks if ConVar has min value.
|
|
// Output : bool
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::HasMin(void) const
|
|
{
|
|
return m_pParent->m_bHasMin;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: checks if ConVar has max value.
|
|
// Output : bool
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::HasMax(void) const
|
|
{
|
|
return m_pParent->m_bHasMax;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sets the ConVar int value.
|
|
// Input : nValue -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetValue(int nValue)
|
|
{
|
|
ConVar* pCVar = reinterpret_cast<ConVar*>(m_pParent);
|
|
pCVar->InternalSetIntValue(nValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sets the ConVar float value.
|
|
// Input : flValue -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetValue(float flValue)
|
|
{
|
|
ConVar* pCVar = reinterpret_cast<ConVar*>(m_pParent);
|
|
pCVar->InternalSetFloatValue(flValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sets the ConVar string value.
|
|
// Input : *szValue -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetValue(const char* pszValue)
|
|
{
|
|
ConVar* pCVar = reinterpret_cast<ConVar*>(m_pParent);
|
|
pCVar->InternalSetValue(pszValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sets the ConVar color value.
|
|
// Input : value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetValue(Color value)
|
|
{
|
|
ConVar* pCVar = reinterpret_cast<ConVar*>(m_pParent);
|
|
pCVar->InternalSetColorValue(value);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::InternalSetColorValue(Color value)
|
|
{
|
|
// Stuff color values into an int
|
|
int nValue = 0;
|
|
|
|
unsigned char* pColorElement = (reinterpret_cast<unsigned char*>(&nValue));
|
|
pColorElement[0] = value[0];
|
|
pColorElement[1] = value[1];
|
|
pColorElement[2] = value[2];
|
|
pColorElement[3] = value[3];
|
|
|
|
// Call the int internal set
|
|
InternalSetIntValue(nValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Check whether to clamp and then perform clamp
|
|
// Input : value -
|
|
// Output : Returns true if value changed
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::ClampValue(float& value)
|
|
{
|
|
if (m_bHasMin && (value < m_fMinVal))
|
|
{
|
|
value = m_fMinVal;
|
|
return true;
|
|
}
|
|
|
|
if (m_bHasMax && (value > m_fMaxVal))
|
|
{
|
|
value = m_fMaxVal;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *tempVal -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::ChangeStringValue(const char* tempVal)
|
|
{
|
|
Assert(!(m_nFlags & FCVAR_NEVER_AS_STRING));
|
|
|
|
char* pszOldValue = (char*)stackalloc(m_Value.m_iStringLength);
|
|
memcpy(pszOldValue, m_Value.m_pszString, m_Value.m_iStringLength);
|
|
|
|
const size_t len = V_strlen(tempVal) + 1;
|
|
|
|
if (len > m_Value.m_iStringLength)
|
|
{
|
|
if (m_Value.m_pszString)
|
|
{
|
|
delete[] m_Value.m_pszString;
|
|
}
|
|
|
|
m_Value.m_pszString = new char[len];
|
|
m_Value.m_iStringLength = len;
|
|
}
|
|
|
|
memcpy(m_Value.m_pszString, tempVal, len);
|
|
|
|
// Invoke any necessary callback function
|
|
for (int i = 0; i < m_fnChangeCallbacks.Count(); ++i)
|
|
{
|
|
m_fnChangeCallbacks[i](this, pszOldValue);
|
|
}
|
|
|
|
if (g_pCVar)
|
|
{
|
|
g_pCVar->CallGlobalChangeCallbacks(this, pszOldValue);
|
|
}
|
|
|
|
stackfree(pszOldValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Private creation
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::Create(const char* pName, const char* pDefaultValue, int flags /*= 0*/,
|
|
const char* pHelpString /*= NULL*/, bool bMin /*= false*/, float fMin /*= 0.0*/,
|
|
bool bMax /*= false*/, float fMax /*= false*/, FnChangeCallback_t callback /*= NULL*/,
|
|
const char* pszUsageString /*= NULL*/)
|
|
{
|
|
// Name should be static data
|
|
m_pszDefaultValue = pDefaultValue ? pDefaultValue : "";
|
|
Assert(m_pszDefaultValue);
|
|
|
|
m_bHasMin = bMin;
|
|
m_fMinVal = fMin;
|
|
m_bHasMax = bMax;
|
|
m_fMaxVal = fMax;
|
|
|
|
m_pParent = this;
|
|
|
|
if (callback)
|
|
{
|
|
m_fnChangeCallbacks.AddToTail(callback);
|
|
}
|
|
|
|
m_Value.m_iStringLength = strlen(m_pszDefaultValue) + 1;
|
|
m_Value.m_pszString = new char[m_Value.m_iStringLength];
|
|
memcpy(m_Value.m_pszString, m_pszDefaultValue, m_Value.m_iStringLength);
|
|
|
|
if (!InternalSetColorFromString(m_Value.m_pszString))
|
|
{
|
|
m_Value.m_fValue = (float)atof(m_Value.m_pszString);
|
|
if (!IsFinite(m_Value.m_fValue))
|
|
{
|
|
DevWarning(eDLL_T::COMMON, "ConVar(%s) defined with infinite float value (%s).\n", pName, m_Value.m_pszString);
|
|
m_Value.m_fValue = FLT_MAX;
|
|
Assert(0);
|
|
}
|
|
|
|
// Bounds Check, should never happen, if it does, no big deal
|
|
if (m_bHasMin && (m_Value.m_fValue < m_fMinVal))
|
|
{
|
|
Assert(0);
|
|
}
|
|
|
|
if (m_bHasMax && (m_Value.m_fValue > m_fMaxVal))
|
|
{
|
|
Assert(0);
|
|
}
|
|
|
|
m_Value.m_nValue = (int)m_Value.m_fValue;
|
|
}
|
|
|
|
|
|
TrackDefaultValue(m_Value.m_pszString);
|
|
|
|
// Only 1 of the 2 can be set on a ConVar, both means there is a bug in
|
|
// your code, fix it!
|
|
const int nFlagsToCheck = (FCVAR_ARCHIVE | FCVAR_ARCHIVE_PLAYERPROFILE);
|
|
if ((m_nFlags & nFlagsToCheck) == nFlagsToCheck)
|
|
{
|
|
Error(eDLL_T::COMMON, EXIT_FAILURE,
|
|
"Convar '%s' is flagged as both FCVAR_ARCHIVE and FCVAR_ARCHIVE_PLAYERPROFILE.\n", pName);
|
|
}
|
|
|
|
BaseClass::Create(pName, pHelpString, flags, pszUsageString);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Reset to default value.
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::Revert(void)
|
|
{
|
|
SetValue(m_pszDefaultValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: returns the default ConVar value.
|
|
// Output : const char
|
|
//-----------------------------------------------------------------------------
|
|
const char* ConVar::GetDefault(void) const
|
|
{
|
|
return m_pParent->m_pszDefaultValue;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: sets the default ConVar value.
|
|
// Input : *pszDefault -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetDefault(const char* pszDefault)
|
|
{
|
|
static const char* pszEmpty = "";
|
|
m_pszDefaultValue = pszDefault ? pszDefault : pszEmpty;
|
|
assert(m_pszDefaultValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Install a change callback (there shouldn't already be one....)
|
|
// Input : callback -
|
|
// bInvoke -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::InstallChangeCallback(FnChangeCallback_t callback, bool bInvoke /*=true*/)
|
|
{
|
|
if (!callback)
|
|
{
|
|
Warning(eDLL_T::COMMON, "%s: Called with NULL callback; ignoring!!!\n",
|
|
__FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
if (m_pParent->m_fnChangeCallbacks.Find(callback)
|
|
!= m_pParent->m_fnChangeCallbacks.InvalidIndex())
|
|
{
|
|
// Same ptr added twice, sigh...
|
|
Warning(eDLL_T::COMMON, "%s: Ignoring duplicate change callback!!!\n",
|
|
__FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
m_pParent->m_fnChangeCallbacks.AddToTail(callback);
|
|
|
|
// Call it immediately to set the initial value...
|
|
if (bInvoke)
|
|
{
|
|
callback(this, m_Value.m_pszString);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Install a change callback (there shouldn't already be one....)
|
|
// Input : callback -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::RemoveChangeCallback(FnChangeCallback_t callback)
|
|
{
|
|
m_pParent->m_fnChangeCallbacks.FindAndRemove(callback);
|
|
}
|