1326 lines
36 KiB
C++
1326 lines
36 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//===========================================================================//
|
|
|
|
#include "fixsdk.hpp"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "basetypes.h"
|
|
#include "tier1/convar.h"
|
|
#include "tier1/strtools.h"
|
|
#include "tier1/characterset.h"
|
|
#include "tier1/utlbuffer.h"
|
|
#include "tier1/tier1.h"
|
|
#include "tier1/convar_serverbounded.h"
|
|
#include "icvar.h"
|
|
#include "tier0/dbg.h"
|
|
#include "Color.h"
|
|
#if defined(_X360)
|
|
#include "xbox/xbox_console.h"
|
|
#endif
|
|
#include "tier0/memdbgon.h"
|
|
|
|
#ifndef NDEBUG
|
|
// Comment this out when we release.
|
|
#define ALLOW_DEVELOPMENT_CVARS
|
|
#endif
|
|
|
|
#define Q_snprintf snprintf
|
|
#include <logging.hpp>
|
|
#include <interfaces.hpp>
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
static int s_nDLLIdentifier =
|
|
-1; // A unique identifier indicating which DLL this convar came from
|
|
static bool s_bRegistered = false;
|
|
|
|
void SetCVarInterface(ICvar *iface)
|
|
{
|
|
g_pCVar = iface;
|
|
logging::Info("Set interface to 0x%08x", iface);
|
|
}
|
|
|
|
class CDefaultAccessor : public IConCommandBaseAccessor
|
|
{
|
|
public:
|
|
virtual bool RegisterConCommandBase(ConCommandBase *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);
|
|
g_pCVar->UnregisterConCommands(s_nDLLIdentifier);
|
|
s_nDLLIdentifier = -1;
|
|
s_bRegistered = false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Default constructor
|
|
//-----------------------------------------------------------------------------
|
|
ConCommandBase::ConCommandBase(void)
|
|
{
|
|
m_bRegistered = false;
|
|
m_pszName = NULL;
|
|
m_pszHelpString = NULL;
|
|
|
|
m_nFlags = 0;
|
|
m_pNext = NULL;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: The base console invoked command/cvar interface
|
|
// Input : *pName - name of variable/command
|
|
// *pHelpString - help text
|
|
// flags - flags
|
|
//-----------------------------------------------------------------------------
|
|
ConCommandBase::ConCommandBase(const char *pName,
|
|
const char *pHelpString /*=0*/,
|
|
int flags /*= 0*/)
|
|
{
|
|
CreateBase(pName, pHelpString, flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
ConCommandBase::~ConCommandBase(void)
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommandBase::IsCommand(void) const
|
|
{
|
|
// Assert( 0 ); This can't assert. . causes a recursive assert in
|
|
// Sys_Printf, etc.
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the DLL identifier
|
|
//-----------------------------------------------------------------------------
|
|
CVarDLLIdentifier_t ConCommandBase::GetDLLIdentifier() const
|
|
{
|
|
return s_nDLLIdentifier;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *pName -
|
|
// callback -
|
|
// *pHelpString -
|
|
// flags -
|
|
//-----------------------------------------------------------------------------
|
|
void ConCommandBase::CreateBase(const char *pName,
|
|
const char *pHelpString /*= 0*/,
|
|
int flags /*= 0*/)
|
|
{
|
|
m_bRegistered = false;
|
|
|
|
// Name should be static data
|
|
Assert(pName);
|
|
m_pszName = pName;
|
|
m_pszHelpString = pHelpString ? pHelpString : "";
|
|
|
|
m_nFlags = flags;
|
|
|
|
#ifdef ALLOW_DEVELOPMENT_CVARS
|
|
m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
|
|
#endif
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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: Return name of the command/var
|
|
// Output : const char
|
|
//-----------------------------------------------------------------------------
|
|
const char *ConCommandBase::GetName(void) const
|
|
{
|
|
return m_pszName;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flag -
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommandBase::IsFlagSet(int flag) const
|
|
{
|
|
return (flag & m_nFlags) ? true : false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : flags -
|
|
//-----------------------------------------------------------------------------
|
|
void ConCommandBase::AddFlags(int flags)
|
|
{
|
|
m_nFlags |= flags;
|
|
|
|
#ifdef ALLOW_DEVELOPMENT_CVARS
|
|
m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
|
|
#endif
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : const ConCommandBase
|
|
//-----------------------------------------------------------------------------
|
|
const ConCommandBase *ConCommandBase::GetNext(void) const
|
|
{
|
|
return m_pNext;
|
|
}
|
|
|
|
ConCommandBase *ConCommandBase::GetNext(void)
|
|
{
|
|
return m_pNext;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Copies string using local new/delete operators
|
|
// Input : *from -
|
|
// Output : char
|
|
//-----------------------------------------------------------------------------
|
|
char *ConCommandBase::CopyString(const char *from)
|
|
{
|
|
int len;
|
|
char *to;
|
|
|
|
len = V_strlen(from);
|
|
if (len <= 0)
|
|
{
|
|
to = new char[1];
|
|
to[0] = 0;
|
|
}
|
|
else
|
|
{
|
|
to = new char[len + 1];
|
|
Q_strncpy(to, from, len + 1);
|
|
}
|
|
return to;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : const char
|
|
//-----------------------------------------------------------------------------
|
|
const char *ConCommandBase::GetHelpText(void) const
|
|
{
|
|
return m_pszHelpString;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Has this cvar been registered
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommandBase::IsRegistered(void) const
|
|
{
|
|
return m_bRegistered;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Con Commands start here
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Global methods
|
|
//-----------------------------------------------------------------------------
|
|
static characterset_t s_BreakSet;
|
|
static bool s_bBuiltBreakSet = false;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Tokenizer class
|
|
//-----------------------------------------------------------------------------
|
|
CCommand::CCommand()
|
|
{
|
|
if (!s_bBuiltBreakSet)
|
|
{
|
|
s_bBuiltBreakSet = true;
|
|
CharacterSetBuild(&s_BreakSet, "{}()':");
|
|
}
|
|
|
|
Reset();
|
|
}
|
|
|
|
CCommand::CCommand(int nArgC, const char **ppArgV)
|
|
{
|
|
Assert(nArgC > 0);
|
|
|
|
if (!s_bBuiltBreakSet)
|
|
{
|
|
s_bBuiltBreakSet = true;
|
|
CharacterSetBuild(&s_BreakSet, "{}()':");
|
|
}
|
|
|
|
Reset();
|
|
|
|
char *pBuf = m_pArgvBuffer;
|
|
char *pSBuf = m_pArgSBuffer;
|
|
m_nArgc = nArgC;
|
|
for (int i = 0; i < nArgC; ++i)
|
|
{
|
|
m_ppArgv[i] = pBuf;
|
|
int nLen = Q_strlen(ppArgV[i]);
|
|
memcpy(pBuf, ppArgV[i], nLen + 1);
|
|
if (i == 0)
|
|
{
|
|
m_nArgv0Size = nLen;
|
|
}
|
|
pBuf += nLen + 1;
|
|
|
|
bool bContainsSpace = strchr(ppArgV[i], ' ') != NULL;
|
|
if (bContainsSpace)
|
|
{
|
|
*pSBuf++ = '\"';
|
|
}
|
|
memcpy(pSBuf, ppArgV[i], nLen);
|
|
pSBuf += nLen;
|
|
if (bContainsSpace)
|
|
{
|
|
*pSBuf++ = '\"';
|
|
}
|
|
|
|
if (i != nArgC - 1)
|
|
{
|
|
*pSBuf++ = ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
void CCommand::Reset()
|
|
{
|
|
m_nArgc = 0;
|
|
m_nArgv0Size = 0;
|
|
m_pArgSBuffer[0] = 0;
|
|
}
|
|
|
|
characterset_t *CCommand::DefaultBreakSet()
|
|
{
|
|
return &s_BreakSet;
|
|
}
|
|
|
|
bool CCommand::Tokenize(const char *pCommand, characterset_t *pBreakSet)
|
|
{
|
|
Reset();
|
|
if (!pCommand)
|
|
return false;
|
|
|
|
// Use default break set
|
|
if (!pBreakSet)
|
|
{
|
|
pBreakSet = &s_BreakSet;
|
|
}
|
|
|
|
// Copy the current command into a temp buffer
|
|
// NOTE: This is here to avoid the pointers returned by DequeueNextCommand
|
|
// to become invalid by calling AddText. Is there a way we can avoid the
|
|
// memcpy?
|
|
int nLen = Q_strlen(pCommand);
|
|
if (nLen >= COMMAND_MAX_LENGTH - 1)
|
|
{
|
|
Warning("CCommand::Tokenize: Encountered command which overflows the "
|
|
"tokenizer buffer.. Skipping!\n");
|
|
return false;
|
|
}
|
|
|
|
memcpy(m_pArgSBuffer, pCommand, nLen + 1);
|
|
|
|
// Parse the current command into the current command buffer
|
|
CUtlBuffer bufParse(m_pArgSBuffer, nLen,
|
|
CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY);
|
|
int nArgvBufferSize = 0;
|
|
while (bufParse.IsValid() && (m_nArgc < COMMAND_MAX_ARGC))
|
|
{
|
|
char *pArgvBuf = &m_pArgvBuffer[nArgvBufferSize];
|
|
int nMaxLen = COMMAND_MAX_LENGTH - nArgvBufferSize;
|
|
int nStartGet = bufParse.TellGet();
|
|
int nSize = bufParse.ParseToken(pBreakSet, pArgvBuf, nMaxLen);
|
|
if (nSize < 0)
|
|
break;
|
|
|
|
// Check for overflow condition
|
|
if (nMaxLen == nSize)
|
|
{
|
|
Reset();
|
|
return false;
|
|
}
|
|
|
|
if (m_nArgc == 1)
|
|
{
|
|
// Deal with the case where the arguments were quoted
|
|
m_nArgv0Size = bufParse.TellGet();
|
|
bool bFoundEndQuote = m_pArgSBuffer[m_nArgv0Size - 1] == '\"';
|
|
if (bFoundEndQuote)
|
|
{
|
|
--m_nArgv0Size;
|
|
}
|
|
m_nArgv0Size -= nSize;
|
|
Assert(m_nArgv0Size != 0);
|
|
|
|
// The StartGet check is to handle this case: "foo"bar
|
|
// which will parse into 2 different args. ArgS should point to bar.
|
|
bool bFoundStartQuote = (m_nArgv0Size > nStartGet) &&
|
|
(m_pArgSBuffer[m_nArgv0Size - 1] == '\"');
|
|
Assert(bFoundEndQuote == bFoundStartQuote);
|
|
if (bFoundStartQuote)
|
|
{
|
|
--m_nArgv0Size;
|
|
}
|
|
}
|
|
|
|
m_ppArgv[m_nArgc++] = pArgvBuf;
|
|
if (m_nArgc >= COMMAND_MAX_ARGC)
|
|
{
|
|
Warning("CCommand::Tokenize: Encountered command which overflows "
|
|
"the argument buffer.. Clamped!\n");
|
|
}
|
|
|
|
nArgvBufferSize += nSize + 1;
|
|
Assert(nArgvBufferSize <= COMMAND_MAX_LENGTH);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Helper function to parse arguments to commands.
|
|
//-----------------------------------------------------------------------------
|
|
const char *CCommand::FindArg(const char *pName) const
|
|
{
|
|
int nArgC = ArgC();
|
|
for (int i = 1; i < nArgC; i++)
|
|
{
|
|
if (!Q_stricmp(Arg(i), pName))
|
|
return (i + 1) < nArgC ? Arg(i + 1) : "";
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int CCommand::FindArgInt(const char *pName, int nDefaultVal) const
|
|
{
|
|
const char *pVal = FindArg(pName);
|
|
if (pVal)
|
|
return atoi(pVal);
|
|
else
|
|
return nDefaultVal;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Default console command autocompletion function
|
|
//-----------------------------------------------------------------------------
|
|
int DefaultCompletionFunc(
|
|
const char *partial,
|
|
char commands[COMMAND_COMPLETION_MAXITEMS][COMMAND_COMPLETION_ITEM_LENGTH])
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Constructs a console command
|
|
//-----------------------------------------------------------------------------
|
|
// ConCommand::ConCommand()
|
|
//{
|
|
// m_bIsNewConCommand = true;
|
|
//}
|
|
|
|
ConCommand::ConCommand(const char *pName, FnCommandCallbackVoid_t callback,
|
|
const char *pHelpString /*= 0*/, int flags /*= 0*/,
|
|
FnCommandCompletionCallback completionFunc /*= 0*/)
|
|
{
|
|
// 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::CreateBase(pName, pHelpString, flags);
|
|
}
|
|
|
|
ConCommand::ConCommand(const char *pName, FnCommandCallback_t callback,
|
|
const char *pHelpString /*= 0*/, int flags /*= 0*/,
|
|
FnCommandCompletionCallback completionFunc /*= 0*/)
|
|
{
|
|
// 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::CreateBase(pName, pHelpString, flags);
|
|
}
|
|
|
|
ConCommand::ConCommand(const char *pName, ICommandCallback *pCallback,
|
|
const char *pHelpString /*= 0*/, int flags /*= 0*/,
|
|
ICommandCompletionCallback *pCompletionCallback /*= 0*/)
|
|
{
|
|
// Set the callback
|
|
m_pCommandCallback = pCallback;
|
|
m_bUsingNewCommandCallback = false;
|
|
m_pCommandCompletionCallback = pCompletionCallback;
|
|
m_bHasCompletionCallback = (pCompletionCallback != 0);
|
|
m_bUsingCommandCallbackInterface = true;
|
|
|
|
// Setup the rest
|
|
BaseClass::CreateBase(pName, pHelpString, flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Destructor
|
|
//-----------------------------------------------------------------------------
|
|
ConCommand::~ConCommand(void)
|
|
{
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Returns true if this is a command
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommand::IsCommand(void) const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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());
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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];
|
|
int iret = (m_fnCompletionCallback)(partial, rgpchCommands);
|
|
for (int i = 0; i < iret; ++i)
|
|
{
|
|
CUtlString str = rgpchCommands[i];
|
|
commands.AddToTail(str);
|
|
}
|
|
return iret;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns true if the console command can autocomplete
|
|
//-----------------------------------------------------------------------------
|
|
bool ConCommand::CanAutoComplete(void)
|
|
{
|
|
return m_bHasCompletionCallback;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Console Variables
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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)
|
|
{
|
|
Create(pName, pDefaultValue, flags, pHelpString);
|
|
}
|
|
|
|
ConVar::ConVar(const char *pName, const char *pDefaultValue, int flags,
|
|
const char *pHelpString, bool bMin, float fMin, bool bMax,
|
|
float fMax)
|
|
{
|
|
Create(pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax);
|
|
}
|
|
|
|
ConVar::ConVar(const char *pName, const char *pDefaultValue, int flags,
|
|
const char *pHelpString, FnChangeCallback_t callback)
|
|
{
|
|
Create(pName, pDefaultValue, flags, pHelpString, false, 0.0, false, 0.0,
|
|
callback);
|
|
}
|
|
|
|
ConVar::ConVar(const char *pName, const char *pDefaultValue, int flags,
|
|
const char *pHelpString, bool bMin, float fMin, bool bMax,
|
|
float fMax, FnChangeCallback_t callback)
|
|
{
|
|
Create(pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax,
|
|
callback);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Destructor
|
|
//-----------------------------------------------------------------------------
|
|
ConVar::~ConVar(void)
|
|
{
|
|
if (m_pszString)
|
|
{
|
|
delete[] m_pszString;
|
|
m_pszString = NULL;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Install a change callback (there shouldn't already be one....)
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::InstallChangeCallback(FnChangeCallback_t callback)
|
|
{
|
|
Assert(!m_pParent->m_fnChangeCallback || !callback);
|
|
m_pParent->m_fnChangeCallback = callback;
|
|
|
|
if (m_pParent->m_fnChangeCallback)
|
|
{
|
|
// Call it immediately to set the initial value...
|
|
m_pParent->m_fnChangeCallback(this, m_pszString, m_fValue);
|
|
}
|
|
}
|
|
|
|
bool ConVar::IsFlagSet(int flag) const
|
|
{
|
|
return (flag & m_pParent->m_nFlags) ? true : false;
|
|
}
|
|
|
|
const char *ConVar::GetHelpText(void) const
|
|
{
|
|
return m_pParent->m_pszHelpString;
|
|
}
|
|
|
|
void ConVar::AddFlags(int flags)
|
|
{
|
|
m_pParent->m_nFlags |= flags;
|
|
|
|
#ifdef ALLOW_DEVELOPMENT_CVARS
|
|
m_pParent->m_nFlags &= ~FCVAR_DEVELOPMENTONLY;
|
|
#endif
|
|
}
|
|
|
|
bool ConVar::IsRegistered(void) const
|
|
{
|
|
return m_pParent->m_bRegistered;
|
|
}
|
|
|
|
const char *ConVar::GetName(void) const
|
|
{
|
|
return m_pParent->m_pszName;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : Returns true on success, false on failure.
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::IsCommand(void) const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input :
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::Init()
|
|
{
|
|
BaseClass::Init();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
float fNewValue;
|
|
char tempVal[32];
|
|
char *val;
|
|
|
|
Assert(m_pParent == this); // Only valid for root convars.
|
|
|
|
float flOldValue = m_fValue;
|
|
|
|
val = (char *) value;
|
|
if (!value)
|
|
fNewValue = 0.0f;
|
|
else
|
|
fNewValue = (float) atof(value);
|
|
|
|
if (ClampValue(fNewValue))
|
|
{
|
|
Q_snprintf(tempVal, sizeof(tempVal), "%f", fNewValue);
|
|
val = tempVal;
|
|
}
|
|
|
|
// Redetermine value
|
|
m_fValue = fNewValue;
|
|
m_nValue = (int) (fNewValue);
|
|
|
|
if (!(m_nFlags & FCVAR_NEVER_AS_STRING))
|
|
{
|
|
ChangeStringValue(val, flOldValue);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *tempVal -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::ChangeStringValue(const char *tempVal, float flOldValue)
|
|
{
|
|
Assert(!(m_nFlags & FCVAR_NEVER_AS_STRING));
|
|
|
|
char *pszOldValue = (char *) stackalloc(m_StringLength);
|
|
memcpy(pszOldValue, m_pszString, m_StringLength);
|
|
|
|
if (tempVal)
|
|
{
|
|
int len = Q_strlen(tempVal) + 1;
|
|
|
|
if (len > m_StringLength)
|
|
{
|
|
if (m_pszString)
|
|
{
|
|
delete[] m_pszString;
|
|
}
|
|
|
|
m_pszString = new char[len];
|
|
m_StringLength = len;
|
|
}
|
|
|
|
memcpy(m_pszString, tempVal, len);
|
|
}
|
|
else
|
|
{
|
|
*m_pszString = 0;
|
|
}
|
|
|
|
// If nothing has changed, don't do the callbacks.
|
|
if (V_strcmp(pszOldValue, m_pszString) != 0)
|
|
{
|
|
// Invoke any necessary callback function
|
|
if (m_fnChangeCallback)
|
|
{
|
|
m_fnChangeCallback(this, pszOldValue, flOldValue);
|
|
}
|
|
/* URAN */
|
|
// logging::Info("Calling 0x%08x callbacks", g_pCVar);
|
|
g_pCVar->CallGlobalChangeCallbacks(this, pszOldValue, flOldValue);
|
|
}
|
|
|
|
stackfree(pszOldValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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 : *value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::InternalSetFloatValue(float fNewValue)
|
|
{
|
|
if (fNewValue == 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
|
|
float flOldValue = m_fValue;
|
|
m_fValue = fNewValue;
|
|
m_nValue = (int) m_fValue;
|
|
|
|
if (!(m_nFlags & FCVAR_NEVER_AS_STRING))
|
|
{
|
|
char tempVal[32];
|
|
Q_snprintf(tempVal, sizeof(tempVal), "%f", m_fValue);
|
|
ChangeStringValue(tempVal, flOldValue);
|
|
}
|
|
else
|
|
{
|
|
Assert(!m_fnChangeCallback);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::InternalSetIntValue(int nValue)
|
|
{
|
|
if (nValue == 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
|
|
float flOldValue = m_fValue;
|
|
m_fValue = fValue;
|
|
m_nValue = nValue;
|
|
|
|
if (!(m_nFlags & FCVAR_NEVER_AS_STRING))
|
|
{
|
|
char tempVal[32];
|
|
snprintf(tempVal, sizeof(tempVal), "%d", m_nValue);
|
|
ChangeStringValue(tempVal, flOldValue);
|
|
}
|
|
else
|
|
{
|
|
Assert(!m_fnChangeCallback);
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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*/)
|
|
{
|
|
m_pParent = this;
|
|
|
|
// Name should be static data
|
|
SetDefault(pDefaultValue);
|
|
|
|
m_StringLength = V_strlen(m_pszDefaultValue) + 1;
|
|
m_pszString = new char[m_StringLength];
|
|
memcpy(m_pszString, m_pszDefaultValue, m_StringLength);
|
|
|
|
m_bHasMin = bMin;
|
|
m_fMinVal = fMin;
|
|
m_bHasMax = bMax;
|
|
m_fMaxVal = fMax;
|
|
|
|
m_fnChangeCallback = callback;
|
|
|
|
m_fValue = (float) atof(m_pszString);
|
|
m_nValue =
|
|
atoi(m_pszString); // dont convert from float to int and lose bits
|
|
|
|
// Bounds Check, should never happen, if it does, no big deal
|
|
if (m_bHasMin && (m_fValue < m_fMinVal))
|
|
{
|
|
Assert(0);
|
|
}
|
|
|
|
if (m_bHasMax && (m_fValue > m_fMaxVal))
|
|
{
|
|
Assert(0);
|
|
}
|
|
|
|
BaseClass::CreateBase(pName, pHelpString, flags);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetValue(const char *value)
|
|
{
|
|
ConVar *var = (ConVar *) m_pParent;
|
|
var->InternalSetValue(value);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetValue(float value)
|
|
{
|
|
ConVar *var = (ConVar *) m_pParent;
|
|
var->InternalSetFloatValue(value);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : value -
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::SetValue(int value)
|
|
{
|
|
ConVar *var = (ConVar *) m_pParent;
|
|
var->InternalSetIntValue(value);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Reset to default value
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar::Revert(void)
|
|
{
|
|
// Force default value again
|
|
ConVar *var = (ConVar *) m_pParent;
|
|
var->SetValue(var->m_pszDefaultValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : minVal -
|
|
// Output : true if there is a min set
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::GetMin(float &minVal) const
|
|
{
|
|
minVal = m_pParent->m_fMinVal;
|
|
return m_pParent->m_bHasMin;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : maxVal -
|
|
//-----------------------------------------------------------------------------
|
|
bool ConVar::GetMax(float &maxVal) const
|
|
{
|
|
maxVal = m_pParent->m_fMaxVal;
|
|
return m_pParent->m_bHasMax;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Output : const char
|
|
//-----------------------------------------------------------------------------
|
|
const char *ConVar::GetDefault(void) const
|
|
{
|
|
return m_pParent->m_pszDefaultValue;
|
|
}
|
|
|
|
void ConVar::SetDefault(const char *pszDefault)
|
|
{
|
|
m_pszDefaultValue = pszDefault ? pszDefault : "";
|
|
Assert(m_pszDefaultValue);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// This version is simply used to make reading convars simpler.
|
|
// Writing convars isn't allowed in this mode
|
|
//-----------------------------------------------------------------------------
|
|
class CEmptyConVar : public ConVar
|
|
{
|
|
public:
|
|
CEmptyConVar() : ConVar("", "0")
|
|
{
|
|
}
|
|
// Used for optimal read access
|
|
virtual void SetValue(const char *pValue)
|
|
{
|
|
}
|
|
virtual void SetValue(float flValue)
|
|
{
|
|
}
|
|
virtual void SetValue(int nValue)
|
|
{
|
|
}
|
|
virtual const char *GetName(void) const
|
|
{
|
|
return "";
|
|
}
|
|
virtual bool IsFlagSet(int nFlags) const
|
|
{
|
|
return false;
|
|
}
|
|
};
|
|
|
|
static CEmptyConVar s_EmptyConVar;
|
|
|
|
ConVarRef::ConVarRef(const char *pName)
|
|
{
|
|
Init(pName, false);
|
|
}
|
|
|
|
ConVarRef::ConVarRef(const char *pName, bool bIgnoreMissing)
|
|
{
|
|
Init(pName, bIgnoreMissing);
|
|
}
|
|
|
|
void ConVarRef::Init(const char *pName, bool bIgnoreMissing)
|
|
{
|
|
m_pConVar = g_pCVar ? g_pCVar->FindVar(pName) : &s_EmptyConVar;
|
|
if (!m_pConVar)
|
|
{
|
|
m_pConVar = &s_EmptyConVar;
|
|
}
|
|
m_pConVarState = static_cast<ConVar *>(m_pConVar);
|
|
if (!IsValid())
|
|
{
|
|
static bool bFirst = true;
|
|
if (g_pCVar || bFirst)
|
|
{
|
|
if (!bIgnoreMissing)
|
|
{
|
|
Warning("ConVarRef %s doesn't point to an existing ConVar\n",
|
|
pName);
|
|
}
|
|
bFirst = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
ConVarRef::ConVarRef(IConVar *pConVar)
|
|
{
|
|
m_pConVar = pConVar ? pConVar : &s_EmptyConVar;
|
|
m_pConVarState = static_cast<ConVar *>(m_pConVar);
|
|
}
|
|
|
|
bool ConVarRef::IsValid() const
|
|
{
|
|
return m_pConVar != &s_EmptyConVar;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar_PrintFlags(const ConCommandBase *var)
|
|
{
|
|
bool any = false;
|
|
if (var->IsFlagSet(FCVAR_GAMEDLL))
|
|
{
|
|
ConMsg(" game");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_CLIENTDLL))
|
|
{
|
|
ConMsg(" client");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_ARCHIVE))
|
|
{
|
|
ConMsg(" archive");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_NOTIFY))
|
|
{
|
|
ConMsg(" notify");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_SPONLY))
|
|
{
|
|
ConMsg(" singleplayer");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_NOT_CONNECTED))
|
|
{
|
|
ConMsg(" notconnected");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_CHEAT))
|
|
{
|
|
ConMsg(" cheat");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_REPLICATED))
|
|
{
|
|
ConMsg(" replicated");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_SERVER_CAN_EXECUTE))
|
|
{
|
|
ConMsg(" server_can_execute");
|
|
any = true;
|
|
}
|
|
|
|
if (var->IsFlagSet(FCVAR_CLIENTCMD_CAN_EXECUTE))
|
|
{
|
|
ConMsg(" clientcmd_can_execute");
|
|
any = true;
|
|
}
|
|
|
|
if (any)
|
|
{
|
|
ConMsg("\n");
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
//-----------------------------------------------------------------------------
|
|
void ConVar_PrintDescription(const ConCommandBase *pVar)
|
|
{
|
|
bool bMin, bMax;
|
|
float fMin, fMax;
|
|
const char *pStr;
|
|
|
|
assert(pVar);
|
|
|
|
Color clr;
|
|
clr.SetColor(255, 100, 100, 255);
|
|
|
|
if (!pVar->IsCommand())
|
|
{
|
|
ConVar *var = (ConVar *) pVar;
|
|
const ConVar_ServerBounded *pBounded =
|
|
dynamic_cast<const ConVar_ServerBounded *>(var);
|
|
|
|
bMin = var->GetMin(fMin);
|
|
bMax = var->GetMax(fMax);
|
|
|
|
const char *value = NULL;
|
|
char tempVal[32];
|
|
|
|
if (pBounded || var->IsFlagSet(FCVAR_NEVER_AS_STRING))
|
|
{
|
|
value = tempVal;
|
|
|
|
int intVal = pBounded ? pBounded->GetInt() : var->GetInt();
|
|
float floatVal = pBounded ? pBounded->GetFloat() : var->GetFloat();
|
|
|
|
if (fabs((float) intVal - floatVal) < 0.000001)
|
|
{
|
|
Q_snprintf(tempVal, sizeof(tempVal), "%d", intVal);
|
|
}
|
|
else
|
|
{
|
|
Q_snprintf(tempVal, sizeof(tempVal), "%f", floatVal);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
value = var->GetString();
|
|
}
|
|
|
|
if (value)
|
|
{
|
|
ConColorMsg(clr, "\"%s\" = \"%s\"", var->GetName(), value);
|
|
|
|
if (stricmp(value, var->GetDefault()))
|
|
{
|
|
ConMsg(" ( def. \"%s\" )", var->GetDefault());
|
|
}
|
|
}
|
|
|
|
if (bMin)
|
|
{
|
|
ConMsg(" min. %f", fMin);
|
|
}
|
|
if (bMax)
|
|
{
|
|
ConMsg(" max. %f", fMax);
|
|
}
|
|
|
|
ConMsg("\n");
|
|
|
|
// Handled virtualized cvars.
|
|
if (pBounded && fabs(pBounded->GetFloat() - var->GetFloat()) > 0.0001f)
|
|
{
|
|
ConColorMsg(clr,
|
|
"** NOTE: The real value is %.3f but the server has "
|
|
"temporarily restricted it to %.3f **\n",
|
|
var->GetFloat(), pBounded->GetFloat());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ConCommand *var = (ConCommand *) pVar;
|
|
|
|
ConColorMsg(clr, "\"%s\"\n", var->GetName());
|
|
}
|
|
|
|
ConVar_PrintFlags(pVar);
|
|
|
|
pStr = pVar->GetHelpText();
|
|
if (pStr && pStr[0])
|
|
{
|
|
ConMsg(" - %s\n", pStr);
|
|
}
|
|
}
|