diff --git a/public/icvar.h b/public/icvar.h index b7c88d3f9..c70c4f499 100644 --- a/public/icvar.h +++ b/public/icvar.h @@ -1,4 +1,4 @@ -//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // @@ -11,21 +11,51 @@ #endif #include "appframework/IAppSystem.h" -#include "tier1/convar.h" #include "tier1/utlvector.h" +#include "tier1/characterset.h" +#include "utllinkedlist.h" +#include "utlhashtable.h" #include "tier0/memalloc.h" +#include "convar.h" +#include + +// Shorthand helper to iterate registered convars +// Example usage: +// FOR_EACH_CONVAR( iter ) +// { +// ConVarRefAbstract aref( iter ); +// Msg( "%s = %d\n", aref.GetName(), aref.GetInt() ); +// +// /* or use typed version, but make sure to check its validity after, +// since it would be invalid on type mismatch */ +// +// CConVarRef cref( iter ); +// if(cref.IsValidRef()) +// Msg( "%s = %d\n", cref.GetName(), cref.Get() ); +// } +#define FOR_EACH_CONVAR( iter ) for(ConVarRef iter = g_pCVar->FindFirstConVar(); iter.IsValidRef(); iter = g_pCVar->FindNextConVar( iter )) + +// Shorthand helper to iterate registered concommands +#define FOR_EACH_CONCOMMAND( iter ) for(ConCommandRef iter = icvar->FindFirstConCommand(); iter.IsValidRef(); iter = icvar->FindNextConCommand( iter )) + -class ConCommandBase; -class ConCommand; -class ConVar; -class Color; -class IConVarListener; -class CConVarDetail; struct ConVarSnapshot_t; -union CVValue_t; class KeyValues; -class ConVarRefAbstract; +//----------------------------------------------------------------------------- +// Called when a ConVar changes value +//----------------------------------------------------------------------------- +typedef void(*FnChangeCallbackGlobal_t)(ConVarRefAbstract* ref, CSplitScreenSlot nSlot, const char *pNewValue, const char *pOldValue); + +//----------------------------------------------------------------------------- +// ConVar & ConCommand creation listener callbacks +//----------------------------------------------------------------------------- +class ICVarListenerCallbacks +{ +public: + virtual void OnConVarCreated( ConVarRefAbstract *pNewCvar ) {}; + virtual void OnConCommandCreated( ConCommand *pNewCommand ) {}; +}; //----------------------------------------------------------------------------- // Purpose: DLL interface to ConVars/ConCommands @@ -33,57 +63,193 @@ class ConVarRefAbstract; abstract_class ICvar : public IAppSystem { public: - // bAllowDeveloper - Allows finding convars with FCVAR_DEVELOPMENTONLY flag - virtual ConVarHandle FindConVar( const char *name, bool bAllowDeveloper = false ) = 0; - virtual ConVarHandle FindFirstConVar() = 0; - virtual ConVarHandle FindNextConVar( ConVarHandle prev ) = 0; - virtual void SetConVarValue( ConVarHandle cvarid, CSplitScreenSlot nSlot, CVValue_t *pNewValue, CVValue_t *pOldValue ) = 0; + // allow_defensive - Allows finding convars with FCVAR_DEFENSIVE flag + virtual ConVarRef FindConVar( const char *name, bool allow_defensive = false ) = 0; + virtual ConVarRef FindFirstConVar() = 0; + virtual ConVarRef FindNextConVar( ConVarRef prev ) = 0; + virtual void CallChangeCallback( ConVarRef cvar, const CSplitScreenSlot nSlot, const CVValue_t* pNewValue, const CVValue_t* pOldValue ) = 0; - virtual ConCommandHandle FindCommand( const char *name ) = 0; - virtual ConCommandHandle FindFirstCommand() = 0; - virtual ConCommandHandle FindNextCommand( ConCommandHandle prev ) = 0; - virtual void DispatchConCommand( ConCommandHandle cmd, const CCommandContext &ctx, const CCommand &args ) = 0; + // allow_defensive - Allows finding commands with FCVAR_DEFENSIVE flag + virtual ConCommandRef FindConCommand( const char *name, bool allow_defensive = false ) = 0; + virtual ConCommandRef FindFirstConCommand() = 0; + virtual ConCommandRef FindNextConCommand( ConCommandRef prev ) = 0; + virtual void DispatchConCommand( ConCommandRef cmd, const CCommandContext &ctx, const CCommand &args ) = 0; // Install a global change callback (to be called when any convar changes) virtual void InstallGlobalChangeCallback( FnChangeCallbackGlobal_t callback ) = 0; virtual void RemoveGlobalChangeCallback( FnChangeCallbackGlobal_t callback ) = 0; - virtual void CallGlobalChangeCallbacks( ConVarRefAbstract *var, CSplitScreenSlot nSlot, const char *pOldString, float flOldValue ) = 0; + virtual void CallGlobalChangeCallbacks( ConVarRefAbstract* ref, CSplitScreenSlot nSlot, const char* newValue, const char* oldValue ) = 0; - // Reverts cvars which contain a specific flag - virtual void RevertFlaggedConVars( int nFlag ) = 0; + // Reverts cvars to default values which contain a specific flag, + // cvars with a flag FCVAR_COMMANDLINE_ENFORCED would be skipped + virtual void ResetConVarsToDefaultValuesByFlag( uint64 nFlag ) = 0; virtual void SetMaxSplitScreenSlots( int nSlots ) = 0; virtual int GetMaxSplitScreenSlots() const = 0; - virtual void RegisterCreationListeners( ICVarListenerCallbacks *callbacks ) = 0; - virtual void RemoveCreationListeners( ICVarListenerCallbacks *callbacks ) = 0; + virtual void RegisterCreationListeners( ICVarListenerCallbacks *callbacks ) = 0; + virtual void RemoveCreationListeners( ICVarListenerCallbacks *callbacks ) = 0; - virtual void unk1() = 0; + virtual void unk001() = 0; - virtual void ResetConVarsToDefaultValues( const char *pszPrefix ) = 0; + // Reverts cvars to default values which match pszPrefix string, + // ignores FCVAR_COMMANDLINE_ENFORCED + virtual void ResetConVarsToDefaultValuesByName( const char *pszPrefix ) = 0; virtual ConVarSnapshot_t *TakeConVarSnapshot( void ) = 0; virtual void ResetConVarsToSnapshot( ConVarSnapshot_t *pSnapshot ) = 0; - virtual void DestroyConVarSnapshot( ConVarSnapshot_t *pSnaoshot ) = 0; + virtual void DestroyConVarSnapshot( ConVarSnapshot_t *pSnapshot ) = 0; - virtual characterset_t GetCharacterSet( void ) = 0; - virtual void SetConVarsFromGameInfo( KeyValues *pKV ) = 0; + virtual characterset_t *GetCharacterSet( void ) = 0; + virtual void SetConVarsFromGameInfo( KeyValues *pKV ) = 0; - virtual void unk2() = 0; + // Removes FCVAR_DEVELOPMENTONLY | FCVAR_DEFENSIVE from all cvars and concommands + // that have FCVAR_DEFENSIVE set + virtual void StripDevelopmentFlags() = 0; // Register, unregister vars - virtual void RegisterConVar( ConVar *pConVar, int64 nAdditionalFlags, ConVarHandle &pCvarRef, ConVar &pCvar ) = 0; - virtual void UnregisterConVar( ConVarHandle handle ) = 0; - virtual ConVar* GetConVar( ConVarHandle handle ) = 0; + virtual void RegisterConVar( const ConVarCreation_t& setup, uint64 nAdditionalFlags, ConVarRef* pCvarRef, ConVarData** pCvarData ) = 0; + // Unregisters convar change callback, but leaves the convar in the lists, + // so all ConVarRefs would still be valid as well as searching for it. + // Expects ref to have registered index to be set (is set on convar creation) + virtual void UnregisterConVarCallbacks( ConVarRef cvar ) = 0; + // Returns convar data or nullptr if not found + virtual ConVarData* GetConVarData( ConVarRef cvar ) = 0; // Register, unregister commands - virtual ConCommandHandle RegisterConCommand( ConCommand *pCmd, int64 nAdditionalFlags = 0 ) = 0; - virtual void UnregisterConCommand( ConCommandHandle handle ) = 0; - virtual ConCommand* GetCommand( ConCommandHandle handle ) = 0; + virtual ConCommandRef RegisterConCommand( const ConCommandCreation_t& setup, uint64 nAdditionalFlags = 0 ) = 0; + // Unregisters concommand callbacks, but leaves the command in the lists, + // so all ConCommandRefs would still be valid as well as searching for it. + // Expects ref to have registered index to be set (is set on command creation) + virtual void UnregisterConCommandCallbacks( ConCommandRef cmd ) = 0; + // Returns command info or empty command struct if not found, never nullptr + virtual ConCommandData* GetConCommandData( ConCommandRef cmd ) = 0; + + // Queues up value (creates a copy of it) to be set when convar is ready to be edited + virtual void QueueThreadSetValue( ConVarRefAbstract* ref, CSplitScreenSlot nSlot, CVValue_t* value ) = 0; +}; + +#include "memdbgon.h" + +// AMNOTE: CCvar definition is mostly for reference and is reverse engineered +// You shouldn't be using this to directly register cvars/concommands, use its interface instead when possible +class CCvar : public ICvar +{ +public: + static const int kMemoryBufferChunkMaxSize = 8192; + static const int kStringBufferChunkMaxSize = 2048; + + // Allocates memory in its internal buffer, can't be freed + // (other than freeing internal buffers completely), so is leaky and thus use cautiously + // AMNOTE: Mostly used for allocating CVValue_t and ConVarData/ConCommandData objects + void *AllocateMemory( int size ) + { + int aligned_size = ALIGN_VALUE( size, 8 ); + + if(aligned_size + m_CurrentMemoryBufferSize > kMemoryBufferChunkMaxSize) + { + m_CurrentMemoryBufferSize = 0; + *m_MemoryBuffer.AddToTailGetPtr() = (uint8 *)malloc( kMemoryBufferChunkMaxSize ); + } + + int offs = m_CurrentMemoryBufferSize; + m_CurrentMemoryBufferSize += aligned_size; + + return &m_MemoryBuffer[m_MemoryBuffer.Count() - 1][offs]; + } - virtual void QueueThreadSetValue( ConVarRefAbstract *ref, CSplitScreenSlot nSlot, CVValue_t *value ) = 0; + // Allocates memory in its internal string buffer, can't be freed + // (other than freeing internal buffers completely), so is leaky and thus use cautiously + // AMNOTE: Mostly used for allocating cvar/concommand names + const char *AllocateString( const char *string ) + { + if(!string || !string[0]) + return ""; + + int strlen = V_strlen( string ) + 1; + + if(strlen + m_CurrentStringsBufferSize > kStringBufferChunkMaxSize) + { + m_CurrentStringsBufferSize = 0; + *m_StringsBuffer.AddToTailGetPtr() = (char *)malloc( kStringBufferChunkMaxSize ); + } + + int offs = m_CurrentStringsBufferSize; + m_CurrentStringsBufferSize += strlen; + + char *base = &m_StringsBuffer[m_StringsBuffer.Count() - 1][offs]; + V_memmove( base, string, strlen ); + + return base; + } + + struct CConVarChangeCallbackNode_t + { + FnGenericChangeCallback_t m_pCallback; + + // Register index of cvar which change cb comes from + int m_ConVarIndex; + }; + + struct ConCommandCallbackInfoNode_t + { + ConCommandCallbackInfo_t m_CB; + + // Register index of concommand which completion cb comes from + int m_ConCmdIndex; + // Index in a linkedlist of callbackinfos + uint16 m_CallbackInfoIndex; + }; + + struct QueuedConVarSet_t + { + ConVarRefAbstract *m_ConVar; + CSplitScreenSlot m_Slot; + CVValue_t *m_Value; + }; + + CUtlVector m_StringsBuffer; + CUtlVector m_MemoryBuffer; + int m_CurrentStringsBufferSize; + int m_CurrentMemoryBufferSize; + + CUtlLinkedList m_ConVarList; + CUtlHashtable m_ConVarHashes; + CUtlLinkedList m_ConVarChangeCBList; + int m_ConVarCount; + + CUtlVector m_CvarCreationListeners; + CUtlVector m_GlobalChangeCBList; + + CUtlLinkedList m_ConCommandList; + CUtlHashtable m_ConCommandHashes; + CUtlLinkedList m_CallbackInfoList; + int m_ConCommandCount; + + int m_SplitScreenSlots; + + CThreadMutex m_Mutex; + characterset_t m_CharacterSet; + KeyValues *m_GameInfoKV; + + uint8 m_CvarDefaultValues[EConVarType_MAX][128]; + + CUtlVector m_SetValueQueue; + + CConCommandMemberAccessor m_FindCmd; + CConCommandMemberAccessor m_DumpChannelsCmd; + CConCommandMemberAccessor m_LogLevelCmd; + CConCommandMemberAccessor m_LogVerbosityCmd; + CConCommandMemberAccessor m_LogColorCmd; + CConCommandMemberAccessor m_LogFlagsCmd; + CConCommandMemberAccessor m_DifferencesCmd; + CConCommandMemberAccessor m_CvarListCmd; + CConCommandMemberAccessor m_HelpCmd; + CConCommandMemberAccessor m_FindFlagsCmd; }; +#include "memdbgoff.h" + //----------------------------------------------------------------------------- // These global names are defined by tier1.h, duplicated here so you // don't have to include tier1.h diff --git a/public/schemasystem/schemasystem.h b/public/schemasystem/schemasystem.h index 452d704e1..b9e1e0dba 100644 --- a/public/schemasystem/schemasystem.h +++ b/public/schemasystem/schemasystem.h @@ -180,20 +180,12 @@ class CSchemaSystem : public ISchemaSystem int m_nNumConnections; CThreadFastMutex m_Mutex; -#ifdef CONVAR_WORK_FINISHED CConCommandMemberAccessor m_SchemaListBindings; CConCommandMemberAccessor m_SchemaAllListBindings; CConCommandMemberAccessor m_SchemaDumpBinding; CConCommandMemberAccessor m_SchemaDetailedClassLayout; CConCommandMemberAccessor m_SchemaStats; CConCommandMemberAccessor m_SchemaMetaStats; -#else -#ifdef _WIN32 - uint8 pad[288]; -#else - uint8 pad[384]; -#endif // _WIN32 -#endif // CONVAR_WORK_FINISHED CUtlVector m_LoadedModules; CUtlVector m_DetectedSchemaMismatches; diff --git a/public/tier0/icommandline.h b/public/tier0/icommandline.h index 9ca3bb798..1e5faac8c 100644 --- a/public/tier0/icommandline.h +++ b/public/tier0/icommandline.h @@ -1,4 +1,4 @@ -//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======// +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // diff --git a/public/tier1/convar.h b/public/tier1/convar.h index 45cd85523..476cb1b5a 100644 --- a/public/tier1/convar.h +++ b/public/tier1/convar.h @@ -1,4 +1,4 @@ -//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // @@ -19,114 +19,163 @@ #include "tier0/dbg.h" #include "tier1/utlvector.h" #include "tier1/utlstring.h" -#include "Color.h" #include "mathlib/vector4d.h" +#include "bufferstring.h" +#include "tier1/characterset.h" +#include "Color.h" #include "playerslot.h" -#ifdef _WIN32 -#define FORCEINLINE_CVAR FORCEINLINE -#elif POSIX -#define FORCEINLINE_CVAR inline -#else -#error "implement me" -#endif - - -//----------------------------------------------------------------------------- -// Uncomment me to test for threading issues for material system convars -// NOTE: You want to disable all threading when you do this -// +host_thread_mode 0 +r_threaded_particles 0 +sv_parallel_packentities 0 +sv_disable_querycache 0 -//----------------------------------------------------------------------------- -//#define CONVAR_TEST_MATERIAL_THREAD_CONVARS 1 - +#include +#include +#include //----------------------------------------------------------------------------- // Forward declarations //----------------------------------------------------------------------------- -class ConVar; class CCommand; class ConCommand; -class ConCommandBase; +class CCommandContext; class ConVarRefAbstract; -class ConCommandRefAbstract; -struct characterset_t; - -class ALIGN8 ConVarHandle -{ -public: - bool IsValid() { return value != kInvalidConVarHandle; } - uint32 Get() { return value; } - void Set( uint32 _value ) { value = _value; } -private: - uint32 value = kInvalidConVarHandle; - -private: - static const uint32 kInvalidConVarHandle = 0xFFFFFFFF; -} ALIGN8_POST; -enum CommandTarget_t -{ - CT_NO_TARGET = -1, - CT_FIRST_SPLITSCREEN_CLIENT = 0, - CT_LAST_SPLITSCREEN_CLIENT = 3, -}; - -class CCommandContext +struct CSplitScreenSlot { -public: - CCommandContext(CommandTarget_t nTarget, CPlayerSlot nSlot) : - m_nTarget(nTarget), m_nPlayerSlot(nSlot) + CSplitScreenSlot() : + m_Data(0) + {} + + CSplitScreenSlot( int index ) { + m_Data = index; } - - CommandTarget_t GetTarget() const + + int Get() const { - return m_nTarget; + return m_Data; } - CPlayerSlot GetPlayerSlot() const + operator int() const { - return m_nPlayerSlot; + return m_Data; } - -private: - CommandTarget_t m_nTarget; - CPlayerSlot m_nPlayerSlot; + + int m_Data; }; -class ALIGN8 ConCommandHandle +//----------------------------------------------------------------------------- +// Purpose: Internal structure of ConVar objects +//----------------------------------------------------------------------------- +enum EConVarType : int16_t { -public: - bool IsValid() { return value != kInvalidConCommandHandle; } - uint16 Get() { return value; } - void Set( uint16 _value ) { value = _value; } - void Reset() { value = kInvalidConCommandHandle; } + EConVarType_Invalid = -1, + EConVarType_Bool, + EConVarType_Int16, + EConVarType_UInt16, + EConVarType_Int32, + EConVarType_UInt32, + EConVarType_Int64, + EConVarType_UInt64, + EConVarType_Float32, + EConVarType_Float64, + EConVarType_String, + EConVarType_Color, + EConVarType_Vector2, + EConVarType_Vector3, + EConVarType_Vector4, + EConVarType_Qangle, + EConVarType_MAX +}; - bool HasCallback() const; - void Dispatch( const CCommandContext& context, const CCommand& command ); +template +constexpr EConVarType TranslateConVarType(); + +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Bool; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Int16; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_UInt16; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Int32; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_UInt32; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Int64; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_UInt64; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Float32; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Float64; } +template<> constexpr EConVarType TranslateConVarType( void ){ return EConVarType_String; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Color; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Vector2; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Vector3; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Vector4; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Qangle; } +template<> constexpr EConVarType TranslateConVarType( void ) { return EConVarType_Invalid; } - void Unregister(); +union CVValue_t +{ + static CVValue_t *InvalidValue() + { + static uint8 s_Data[sizeof( CVValue_t )] = {}; + return (CVValue_t *)&s_Data; + } -private: - uint16 value = kInvalidConCommandHandle; + CVValue_t() {} + CVValue_t( const bool value ) : m_bValue( value ) {} + CVValue_t( const int16 value ) : m_i16Value( value ) {} + CVValue_t( const uint16 value ) : m_u16Value( value ) {} + CVValue_t( const int32 value ) : m_i32Value( value ) {} + CVValue_t( const uint32 value ) : m_u32Value( value ) {} + CVValue_t( const int64 value ) : m_i64Value( value ) {} + CVValue_t( const uint64 value ) : m_u64Value( value ) {} + CVValue_t( const float32 value ) : m_fl32Value( value ) {} + CVValue_t( const float64 value ) : m_fl64Value( value ) {} + CVValue_t( const CUtlString &value ) : m_StringValue( value ) {} + CVValue_t( const Color &value ) : m_clrValue( value ) {} + CVValue_t( const Vector2D &value ) : m_vec2Value( value ) {} + CVValue_t( const Vector &value ) : m_vec3Value( value ) {} + CVValue_t( const Vector4D &value ) : m_vec4Value( value ) {} + CVValue_t( const QAngle &value ) : m_angValue( value ) {} + ~CVValue_t() {} + + operator bool() const { return m_bValue; } + operator int16() const { return m_i16Value; } + operator uint16() const { return m_u16Value; } + operator int32() const { return m_i32Value; } + operator uint32() const { return m_u32Value; } + operator int64() const { return m_i64Value; } + operator uint64() const { return m_u64Value; } + operator float32() const { return m_fl32Value; } + operator float64() const { return m_fl64Value; } + operator const char*() const { return m_StringValue.Get(); } + operator const CUtlString&() const { return m_StringValue; } + operator const Color&() const { return m_clrValue; } + operator const Vector2D&() const { return m_vec2Value; } + operator const Vector&() const { return m_vec3Value; } + operator const Vector4D&() const { return m_vec4Value; } + operator const QAngle&() const { return m_angValue; } -private: - static const uint16 kInvalidConCommandHandle = 0xFFFF; -} ALIGN8_POST; + bool m_bValue; + int16 m_i16Value; + uint16 m_u16Value; + int32 m_i32Value; + uint32 m_u32Value; + int64 m_i64Value; + uint64 m_u64Value; + float32 m_fl32Value; + float64 m_fl64Value; + CUtlString m_StringValue; + Color m_clrValue; + Vector2D m_vec2Value; + Vector m_vec3Value; + Vector4D m_vec4Value; + QAngle m_angValue; +}; -struct CSplitScreenSlot +struct CVarCreationBase_t { - CSplitScreenSlot( int index ) - { - m_Data = index; - } - - int Get() const - { - return m_Data; - } - - int m_Data; + CVarCreationBase_t() : + m_pszName( nullptr ), + m_pszHelpString( nullptr ), + m_nFlags( 0 ) + {} + + const char* m_pszName; + const char* m_pszHelpString; + uint64 m_nFlags; }; //----------------------------------------------------------------------------- @@ -137,7 +186,7 @@ struct CSplitScreenSlot // Command to ConVars and ConCommands // ConVar Systems -#define FCVAR_LINKED_CONCOMMAND (1<<0) +#define FCVAR_LINKED_CONCOMMAND (1<<0) // Allows concommand callback chaining. When command is dispatched all chained callbacks would fire. #define FCVAR_DEVELOPMENTONLY (1<<1) // Hidden in released products. Flag is removed automatically if ALLOW_DEVELOPMENT_CVARS is defined. #define FCVAR_GAMEDLL (1<<2) // defined by the game DLL #define FCVAR_CLIENTDLL (1<<3) // defined by the client DLL @@ -150,9 +199,11 @@ struct CSplitScreenSlot #define FCVAR_NOTIFY (1<<8) // notifies players when changed #define FCVAR_USERINFO (1<<9) // changes the client's info string -#define FCVAR_MISSING0 (1<<10) // Something that hides the cvar from the cvar lookups +#define FCVAR_REFERENCE (1<<10) // Means cvar is a reference, usually used to get a cvar reference of a cvar registered in other module, + // and is temporary until the actual cvar was registered #define FCVAR_UNLOGGED (1<<11) // If this is a FCVAR_SERVER, don't log changes to the log file / console if we are creating a log -#define FCVAR_MISSING1 (1<<12) // Something that hides the cvar from the cvar lookups +#define FCVAR_INITIAL_SETVALUE (1<<12) // Is set for a first convar SetValue either with its default_value or with a value from a gameinfo. + // Mostly for callbacks to check for. // It's a ConVar that's shared between the client and the server. // At signon, the values of all such ConVars are sent from the server to the client (skipped for local @@ -164,10 +215,11 @@ struct CSplitScreenSlot #define FCVAR_PER_USER (1<<15) // causes varnameN where N == 2 through max splitscreen slots for mod to be autogenerated #define FCVAR_DEMO (1<<16) // record this cvar when starting a demo file #define FCVAR_DONTRECORD (1<<17) // don't record these command in demofiles -#define FCVAR_MISSING2 (1<<18) +#define FCVAR_PERFORMING_CALLBACKS (1<<18) // Is set when cvar is calling to its callbacks from CallChangeCallback or CallGlobalChangeCallbacks, + // usually means if you set the value during these callbacks, its value would be queued and set when all callbacks were fired #define FCVAR_RELEASE (1<<19) // Cvars tagged with this are the only cvars avaliable to customers #define FCVAR_MENUBAR_ITEM (1<<20) -#define FCVAR_MISSING3 (1<<21) +#define FCVAR_COMMANDLINE_ENFORCED (1<<21) // If cvar was set via launch options it would not be reset on calls to ResetConVarsToDefaultValuesByFlag #define FCVAR_NOT_CONNECTED (1<<22) // cvar cannot be changed by a client that is connected to a server #define FCVAR_VCONSOLE_FUZZY_MATCHING (1<<23) @@ -181,61 +233,7 @@ struct CSplitScreenSlot #define FCVAR_EXECUTE_PER_TICK (1<<29) -enum EConVarType : short -{ - EConVarType_Invalid = -1, - EConVarType_Bool, - EConVarType_Int16, - EConVarType_UInt16, - EConVarType_Int32, - EConVarType_UInt32, - EConVarType_Int64, - EConVarType_UInt64, - EConVarType_Float32, - EConVarType_Float64, - EConVarType_String, - EConVarType_Color, - EConVarType_Vector2, - EConVarType_Vector3, - EConVarType_Vector4, - EConVarType_Qangle -}; - -union CVValue_t -{ - bool m_bValue; - short m_i16Value; - uint16 m_u16Value; - int m_i32Value; - uint m_u32Value; - int64 m_i64Value; - uint64 m_u64Value; - float m_flValue; - double m_dbValue; - const char *m_szValue; - Color m_clrValue; - Vector2D m_vec2Value; - Vector m_vec3Value; - Vector4D m_vec4Value; - QAngle m_angValue; -}; - -//----------------------------------------------------------------------------- -// Called when a ConVar changes value -//----------------------------------------------------------------------------- -typedef void(*FnChangeCallbackGlobal_t)(ConVarRefAbstract *cvar, CSplitScreenSlot nSlot, const char *pNewValue, const char *pOldValue); -typedef void(*FnChangeCallback_t)(ConVarRefAbstract *cvar, CSplitScreenSlot nSlot, CVValue_t *pNewValue, CVValue_t *pOldValue); - -//----------------------------------------------------------------------------- -// ConVar & ConCommand creation listener callbacks -//----------------------------------------------------------------------------- -class ICVarListenerCallbacks -{ -public: - virtual void OnConVarCreated( ConVarRefAbstract *pNewCvar ) = 0; - virtual void OnConCommandCreated( ConCommandRefAbstract *pNewCommand ) = 0; -}; - +#define FCVAR_DEFENSIVE (1<<32) //----------------------------------------------------------------------------- @@ -248,7 +246,7 @@ typedef void ( *FnCommandCallbackVoid_t )(); //----------------------------------------------------------------------------- // Returns 0 to COMMAND_COMPLETION_MAXITEMS worth of completion strings //----------------------------------------------------------------------------- -typedef int(*FnCommandCompletionCallback)( const char *partial, CUtlVector< CUtlString > &commands ); +typedef void (*FnCommandCompletionCallback)( const CCommand &command, CUtlVector< CUtlString > &completions ); //----------------------------------------------------------------------------- @@ -263,62 +261,47 @@ class ICommandCallback class ICommandCompletionCallback { public: - virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands ) = 0; -}; - -class ConCommandRefAbstract -{ -public: - ConCommandHandle handle; + virtual int CommandCompletionCallback( const CCommand &command, CUtlVector< CUtlString > &completions ) = 0; }; //----------------------------------------------------------------------------- -// Purpose: The base console invoked command/cvar interface +// Command tokenizer //----------------------------------------------------------------------------- -class ConCommandBase +enum CommandTarget_t { - friend class CCvar; - friend class ConCommand; + CT_NO_TARGET = -1, + CT_FIRST_SPLITSCREEN_CLIENT = 0, + CT_LAST_SPLITSCREEN_CLIENT = 3, +}; -protected: - ConCommandBase( void ); +class CCommandContext +{ public: + CCommandContext( CommandTarget_t nTarget, CPlayerSlot nSlot ) : + m_nTarget( nTarget ), m_nPlayerSlot( nSlot ) + {} - ~ConCommandBase( void ); - // Check flag - bool IsFlagSet( int64 flag ) const; - // Set flag - void AddFlags( int64 flags ); - // Clear flag - void RemoveFlags( int64 flags ); - - int64 GetFlags() const; - - // Return name of cvar - const char *GetName( void ) const; + CommandTarget_t GetTarget() const + { + return m_nTarget; + } - // Return help text for cvar - const char *GetHelpText( void ) const; + CPlayerSlot GetPlayerSlot() const + { + return m_nPlayerSlot; + } -private: - // Static data - const char *m_pszName; - const char *m_pszHelpString; - - // ConVar flags - int64 m_nFlags; +private: + CommandTarget_t m_nTarget; + CPlayerSlot m_nPlayerSlot; }; - -//----------------------------------------------------------------------------- -// Command tokenizer -//----------------------------------------------------------------------------- class CCommand { public: CCommand(); CCommand( int nArgC, const char **ppArgV ); - bool Tokenize( const char *pCommand, characterset_t *pBreakSet = NULL ); + bool Tokenize( CUtlString pCommand, characterset_t *pBreakSet = nullptr ); void Reset(); int ArgC() const; @@ -339,6 +322,8 @@ class CCommand static characterset_t* DefaultBreakSet(); private: + void EnsureBuffers(); + enum { COMMAND_MAX_ARGC = 64, @@ -391,667 +376,1026 @@ inline const char *CCommand::operator[]( int nIndex ) const return Arg( nIndex ); } - -//----------------------------------------------------------------------------- -// Purpose: The console invoked command -//----------------------------------------------------------------------------- -class ConCommand : public ConCommandBase +struct ConCommandCallbackInfo_t { -friend class CCvar; -friend class ConCommandHandle; + ConCommandCallbackInfo_t() : + m_fnCommandCallback( nullptr ), + m_bIsInterface( false ), + m_bIsVoidCallback( false ), + m_bIsContextLess( false ) + {} + + ConCommandCallbackInfo_t( FnCommandCallback_t cb ) : + m_fnCommandCallback( cb ), + m_bIsInterface( false ), + m_bIsVoidCallback( false ), + m_bIsContextLess( false ) + {} + + ConCommandCallbackInfo_t( FnCommandCallbackVoid_t cb ) : + m_fnVoidCommandCallback( cb ), + m_bIsInterface( false ), + m_bIsVoidCallback( cb ? true : false ), + m_bIsContextLess( false ) + {} + + ConCommandCallbackInfo_t( FnCommandCallbackNoContext_t cb ) : + m_fnContextlessCommandCallback( cb ), + m_bIsInterface( false ), + m_bIsVoidCallback( false ), + m_bIsContextLess( cb ? true : false ) + {} + + ConCommandCallbackInfo_t( ICommandCallback *cb ) : + m_pCommandCallback( cb ), + m_bIsInterface( cb ? true : false ), + m_bIsVoidCallback( false ), + m_bIsContextLess( false ) + {} + + bool IsValid() const { return m_fnCommandCallback != nullptr; } + + void Dispatch( const CCommandContext &context, const CCommand &command ) const + { + if(!IsValid()) + return; + + if(m_bIsInterface) + m_pCommandCallback->CommandCallback( context, command ); + else if(m_bIsVoidCallback) + m_fnVoidCommandCallback(); + else if(m_bIsContextLess) + m_fnContextlessCommandCallback( command ); + else + m_fnCommandCallback( context, command ); + } -public: - typedef ConCommandBase BaseClass; + union + { + FnCommandCallback_t m_fnCommandCallback; + FnCommandCallbackVoid_t m_fnVoidCommandCallback; + FnCommandCallbackNoContext_t m_fnContextlessCommandCallback; + ICommandCallback *m_pCommandCallback; + }; - ConCommand( ConCommandRefAbstract *pReferenceOut, const char *pName, FnCommandCallback_t callback, - const char *pHelpString = 0, int64 flags = 0, FnCommandCompletionCallback completionFunc = 0 ); - ConCommand( ConCommandRefAbstract *pReferenceOut, const char *pName, FnCommandCallbackVoid_t callback, - const char *pHelpString = 0, int64 flags = 0, FnCommandCompletionCallback completionFunc = 0 ); - ConCommand( ConCommandRefAbstract* pReferenceOut, const char* pName, FnCommandCallbackNoContext_t callback, - const char* pHelpString = 0, int64 flags = 0, FnCommandCompletionCallback completionFunc = 0 ); - ConCommand( ConCommandRefAbstract *pReferenceOut, const char *pName, ICommandCallback *pCallback, - const char *pHelpString = 0, int64 flags = 0, ICommandCompletionCallback *pCommandCompletionCallback = 0 ); + bool m_bIsInterface : 1; + bool m_bIsVoidCallback : 1; + bool m_bIsContextLess : 1; +}; - ~ConCommand( void ); +struct ConCommandCompletionCallbackInfo_t +{ + ConCommandCompletionCallbackInfo_t() : + m_fnCompletionCallback( nullptr ), + m_bIsFunction( false ), + m_bIsInterface( false ) + {} + + ConCommandCompletionCallbackInfo_t( FnCommandCompletionCallback cb ) : + m_fnCompletionCallback( cb ), + m_bIsFunction( cb ? true : false ), + m_bIsInterface( false ) + {} + + ConCommandCompletionCallbackInfo_t( ICommandCompletionCallback *cb ) : + m_pCommandCompletionCallback( cb ), + m_bIsFunction( false ), + m_bIsInterface( cb ? true : false ) + {} + + bool IsValid() const { return m_fnCompletionCallback != nullptr; } + + int Dispatch( const CCommand &command, CUtlVector< CUtlString > &completions ) const + { + if(!IsValid()) + return 0; + + if(m_bIsInterface) + return m_pCommandCompletionCallback->CommandCompletionCallback( command, completions ); + + m_fnCompletionCallback( command, completions ); + return completions.Count(); + } - // Used internally by OneTimeInit to initialize/shutdown - void Init(); - void Shutdown(); + union + { + FnCommandCompletionCallback m_fnCompletionCallback; + ICommandCompletionCallback *m_pCommandCompletionCallback; + }; - void Create( const char *pName, const char *pHelpString = 0, - int64 flags = 0 ); + bool m_bIsFunction; + bool m_bIsInterface; +}; - int AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands ); +struct ConCommandCreation_t : CVarCreationBase_t +{ + ConCommandCallbackInfo_t m_CBInfo; + ConCommandCompletionCallbackInfo_t m_CompletionCBInfo; +}; - bool CanAutoComplete( void ); +//----------------------------------------------------------------------------- +// ConCommands internal data storage class +//----------------------------------------------------------------------------- +class ConCommandData +{ +public: + const char *GetName() const { return m_pszName; } + const char *GetHelpText() const { return m_pszHelpString; } + bool HasHelpText() const { return m_pszHelpString && m_pszHelpString[0]; } - inline ConCommandRefAbstract *GetRef( void ) const - { - return m_pReference; - } + bool IsFlagSet( uint64 flag ) const { return (m_nFlags & flag) != 0; } + void AddFlags( uint64 flags ) { m_nFlags |= flags; } + void RemoveFlags( uint64 flags ) { m_nFlags &= ~flags; } + uint64 GetFlags( void ) const { return m_nFlags; } - inline void SetHandle( ConCommandHandle hndl ) + int GetAutoCompleteSuggestions( const CCommand &command, CUtlVector< CUtlString > &completions ) const { - m_pReference->handle = hndl; + return m_CompletionCB.Dispatch( command, completions ); } -private: - // Call this function when executing the command - class CallbackInfo_t - { - public: - union { - FnCommandCallback_t m_fnCommandCallback; - FnCommandCallbackVoid_t m_fnVoidCommandCallback; - FnCommandCallbackNoContext_t m_fnContextlessCommandCallback; - ICommandCallback* m_pCommandCallback; - }; - - bool m_bUsingCommandCallbackInterface : 1; - bool m_bHasVoidCommandCallback : 1; - bool m_bHasContextlessCommandCallback : 1; - }; - - CallbackInfo_t m_Callback; + bool HasCompletionCallback() const { return m_CompletionCB.IsValid(); } + bool HasCallback() const { return m_CallbackInfoIndex > 0; } - // NOTE: To maintain backward compat, we have to be very careful: - // All public virtual methods must appear in the same order always - // since engine code will be calling into this code, which *does not match* - // in the mod code; it's using slightly different, but compatible versions - // of this class. Also: Be very careful about adding new fields to this class. - // Those fields will not exist in the version of this class that is instanced - // in mod code. + const char* m_pszName; + const char* m_pszHelpString; + uint64 m_nFlags; - union - { - FnCommandCompletionCallback m_fnCompletionCallback; - ICommandCompletionCallback* m_pCommandCompletionCallback; - }; + ConCommandCompletionCallbackInfo_t m_CompletionCB; - bool m_bHasCompletionCallback : 1; - bool m_bUsingCommandCompletionInterface : 1; - - ConCommandRefAbstract *m_pReference; + // Register index of concommand which completion cb comes from + int m_CompletionCBCmdIndex; + // Index in a linkedlist of callbackinfos + uint16 m_CallbackInfoIndex; }; - //----------------------------------------------------------------------------- -// Purpose: A console variable +// Used to access registered concommands //----------------------------------------------------------------------------- -class ConVar +class ConCommandRef { -friend class CCvar; -friend class ConVarRef; -friend class SplitScreenConVarRef; +private: + static const uint16 kInvalidAccessIndex = 0xFFFF; public: -#ifdef CONVAR_WORK_FINISHED - ConVar( const char *pName, const char *pDefaultValue, int64 flags = 0); - - ConVar( const char *pName, const char *pDefaultValue, int64 flags, - const char *pHelpString ); - ConVar( const char *pName, const char *pDefaultValue, int64 flags, - const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax ); - ConVar( const char *pName, const char *pDefaultValue, int64 flags, - const char *pHelpString, FnChangeCallback_t callback ); - ConVar( const char *pName, const char *pDefaultValue, int64 flags, - const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax, - FnChangeCallback_t callback ); - - ~ConVar( void ); - - bool IsFlagSet( int64 flag ) const; - const char* GetHelpText( void ) const; - const char *GetName( void ) const; - // Return name of command (usually == GetName(), except in case of FCVAR_SS_ADDED vars - const char *GetBaseName( void ) const; - int GetSplitScreenPlayerSlot() const; - - void AddFlags( int64 flags ); - int64 GetFlags() const; - - // Install a change callback (there shouldn't already be one....) - void InstallChangeCallback( FnChangeCallback_t callback, bool bInvoke = true ); - void RemoveChangeCallback( FnChangeCallback_t callbackToRemove ); - - int GetChangeCallbackCount() const { return m_pParent->m_fnChangeCallbacks.Count(); } - FnChangeCallback_t GetChangeCallback( int slot ) const { return m_pParent->m_fnChangeCallbacks[ slot ]; } - - // Retrieve value - FORCEINLINE_CVAR float GetFloat( void ) const; - FORCEINLINE_CVAR int GetInt( void ) const; - FORCEINLINE_CVAR Color GetColor( void ) const; - FORCEINLINE_CVAR bool GetBool() const { return !!GetInt(); } - FORCEINLINE_CVAR char const *GetString( void ) const; - - // Compiler driven selection for template use - template T Get( void ) const; - template T Get( T * ) const; - - // Any function that allocates/frees memory needs to be virtual or else you'll have crashes - // from alloc/free across dll/exe boundaries. - - // These just call into the IConCommandBaseAccessor to check flags and set the var (which ends up calling InternalSetValue). - void SetValue( const char *value ); - void SetValue( float value ); - void SetValue( int value ); - void SetValue( Color value ); - - // Reset to default value - void Revert( void ); - // True if it has a min/max setting - bool HasMin() const; - bool HasMax() const; + ConCommandRef() : m_CommandAccessIndex( kInvalidAccessIndex ), m_CommandRegisteredIndex( 0 ) {} + ConCommandRef( uint16 command_idx ) : m_CommandAccessIndex( command_idx ), m_CommandRegisteredIndex( 0 ) {} + ConCommandRef( uint16 access_idx, int reg_idx ) : m_CommandAccessIndex( access_idx ), m_CommandRegisteredIndex( reg_idx ) {} - bool GetMin( float& minVal ) const; - bool GetMax( float& maxVal ) const; + ConCommandRef( const char *name, bool allow_defensive = false ); - float GetMinValue() const; - float GetMaxValue() const; + ConCommandData *GetRawData(); + const ConCommandData *GetRawData() const { return const_cast(this)->GetRawData(); } - const char *GetDefault( void ) const; + const char *GetName() const { return GetRawData()->GetName(); } + const char *GetHelpText() const { return GetRawData()->GetHelpText(); } + bool HasHelpText() const { return GetRawData()->HasHelpText(); } + bool IsFlagSet( uint64 flag ) const { return GetRawData()->GetFlags(); } + void AddFlags( uint64 flags ) { GetRawData()->AddFlags( flags ); } + void RemoveFlags( uint64 flags ) { GetRawData()->RemoveFlags( flags ); } + uint64 GetFlags() const { return GetRawData()->GetFlags(); } - FORCEINLINE_CVAR CVValue_t &GetRawValue() - { - return m_Value; - } - FORCEINLINE_CVAR const CVValue_t &GetRawValue() const - { - return m_Value; - } + bool HasCompletionCallback() const { return GetRawData()->HasCompletionCallback(); } + bool HasCallback() const { return GetRawData()->HasCallback(); } -private: - bool InternalSetColorFromString( const char *value ); - // Called by CCvar when the value of a var is changing. - void InternalSetValue(const char *value); - // For CVARs marked FCVAR_NEVER_AS_STRING - void InternalSetFloatValue( float fNewValue ); - void InternalSetIntValue( int nValue ); - void InternalSetColorValue( Color value ); + void Dispatch( const CCommandContext &context, const CCommand &command ); - bool ClampValue( float& value ); - void ChangeStringValue( const char *tempVal, float flOldValue ); + int GetCompletionCommandIndex() const { return GetRawData()->m_CompletionCBCmdIndex; } + uint16 GetCallbackIndex() const { return GetRawData()->m_CallbackInfoIndex; } - void Create( const char *pName, const char *pDefaultValue, int64 flags = 0, - const char *pHelpString = 0, bool bMin = false, float fMin = 0.0, - bool bMax = false, float fMax = false, FnChangeCallback_t callback = 0 ); + int GetAutoCompleteSuggestions( const CCommand &command, CUtlVector< CUtlString > &completions ) const + { + return GetRawData()->GetAutoCompleteSuggestions( command, completions ); + } - // Used internally by OneTimeInit to initialize. - void Init(); + void InvalidateRef() { m_CommandAccessIndex = kInvalidAccessIndex; m_CommandRegisteredIndex = 0; } + bool IsValidRef() const { return m_CommandAccessIndex != kInvalidAccessIndex; } + uint16 GetAccessIndex() const { return m_CommandAccessIndex; } + int GetRegisteredIndex() const { return m_CommandRegisteredIndex; } +private: + // Index into internal linked list of concommands + uint16 m_CommandAccessIndex; + // Commands registered positional index + int m_CommandRegisteredIndex; +}; +//----------------------------------------------------------------------------- +// Used to register concommands in the system as well as access its own data +//----------------------------------------------------------------------------- +class ConCommand : public ConCommandRef +{ +public: + typedef ConCommandRef BaseClass; -protected: -#if 0 - // Next ConVar in chain - // Prior to register, it points to the next convar in the DLL. - // Once registered, though, m_pNext is reset to point to the next - // convar in the global list - ConCommandBase* m_pNext; + ConCommand( const char *pName, ConCommandCallbackInfo_t callback, + const char *pHelpString, uint64 flags = 0, + ConCommandCompletionCallbackInfo_t completionFunc = ConCommandCompletionCallbackInfo_t() ) + : BaseClass() + { + Create( pName, callback, pHelpString, flags, completionFunc ); + } - // Has the cvar been added to the global list? - bool m_bRegistered; + ~ConCommand() + { + Destroy(); + } - // Static data - const char* m_pszName; - const char* m_pszHelpString; +private: + void Create( const char *pName, const ConCommandCallbackInfo_t &cb, const char *pHelpString, uint64 flags, const ConCommandCompletionCallbackInfo_t &completion_cb ); + void Destroy( ); +}; - // ConVar flags - int64 m_nFlags; +using FnGenericChangeCallback_t = void(*)(ConVarRefAbstract* ref, CSplitScreenSlot nSlot, const CVValue_t* pNewValue, const CVValue_t* pOldValue); -protected: - // ConVars add themselves to this list for the executable. - // Then ConVar_Register runs through all the console variables - // and registers them into a global list stored in vstdlib.dll - static ConCommandBase* s_pConCommandBases; - - // ConVars in this executable use this 'global' to access values. - static IConCommandBaseAccessor* s_pAccessor; - // This either points to "this" or it points to the original declaration of a ConVar. - // This allows ConVars to exist in separate modules, and they all use the first one to be declared. - // m_pParent->m_pParent must equal m_pParent (ie: m_pParent must be the root, or original, ConVar). - ConVar *m_pParent; - - // Static data - const char *m_pszDefaultValue; - - CVValue_t m_Value; +struct ConVarValueInfo_t +{ + ConVarValueInfo_t( EConVarType type = EConVarType_Invalid ) : + m_Version( 0 ), + m_bHasDefault( false ), + m_bHasMin( false ), + m_bHasMax( false ), + m_defaultValue {}, + m_minValue {}, + m_maxValue {}, + m_fnCallBack(), + m_eVarType( type ) + {} + + template + void SetDefaultValue( const T &value ) + { + m_bHasDefault = true; + *reinterpret_cast(m_defaultValue) = value; + } - // Min/Max values - bool m_bHasMin; - float m_fMinVal; - bool m_bHasMax; - float m_fMaxVal; - - // Call this function when ConVar changes - CUtlVector< FnChangeCallback_t > m_fnChangeCallbacks; -#endif -#endif // CONVAR_WORK_FINISHED - const char* m_pszName; - CVValue_t* m_cvvDefaultValue; - CVValue_t* m_cvvMinValue; - CVValue_t* m_cvvMaxValue; - const char* m_pszHelpString; - EConVarType m_eVarType; + template + void SetMinValue( const T &min ) + { + m_bHasMin = true; + *reinterpret_cast(m_minValue) = min; + } - // This gets copied from the ConVarDesc_t on creation - short unk1; + template + void SetMaxValue( const T &max ) + { + m_bHasMax = true; + *reinterpret_cast(m_maxValue) = max; + } - unsigned int timesChanged; - int64 flags; - unsigned int callback_index; + int32 m_Version; - // Used when setting default, max, min values from the ConVarDesc_t - // although that's not the only place of usage - // flags seems to be: - // (1 << 0) Skip setting value to split screen slots and also something keyvalues related - // (1 << 1) Skip setting default value - // (1 << 2) Skip setting min/max values - int allocation_flag_of_some_sort; + bool m_bHasDefault; + bool m_bHasMin; + bool m_bHasMax; - CVValue_t** values; +private: + // Don't use CVValue_t directly to prevent align issues and to avoid initialising memory + uint8 m_defaultValue[sizeof( CVValue_t )]; + uint8 m_minValue[sizeof( CVValue_t )]; + uint8 m_maxValue[sizeof( CVValue_t )]; + +public: + FnGenericChangeCallback_t m_fnCallBack; + EConVarType m_eVarType; }; +struct ConVarCreation_t : CVarCreationBase_t +{ + ConVarValueInfo_t m_valueInfo; +}; -#ifdef CONVAR_WORK_FINISHED -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a float -// Output : float -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR float ConVar::GetFloat( void ) const +template static void CvarTypeTrait_ConstructFn( CVValue_t *obj ) { Construct( (T *)obj ); } +template static void CvarTypeTrait_CopyFn( CVValue_t *obj, const CVValue_t &other ) { *(T *)obj = *(T *)&other; } +template static void CvarTypeTrait_DestructFn( CVValue_t *obj ) { Destruct( (T *)obj ); } +template static bool CvarTypeTrait_StringToValueFn( const char *string, CVValue_t *obj ) { return V_StringToValue( string, *(T *)obj ); } +template static void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ); +template static bool CvarTypeTrait_EqualFn( const CVValue_t *obj, const CVValue_t *other ) { return *(T *)obj == *(T *)other; } +template static void CvarTypeTrait_ClampFn( CVValue_t *obj, const CVValue_t *min, const CVValue_t *max ) { - return m_pParent->m_Value.m_fValue; + if(min) + *(T *)obj = Max( *(T *)obj, *(T *)min ); + + if(max) + *(T *)obj = Min( *(T *)obj, *(T *)max ); } -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as an int -// Output : int -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR int ConVar::GetInt( void ) const +template<> bool CvarTypeTrait_StringToValueFn( const char *string, CVValue_t *obj ) { obj->m_StringValue = string; return true; } + +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Insert( 0, obj->m_bValue ? "true" : "false" ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%d", obj->m_i16Value ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%u", obj->m_u16Value ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%d", obj->m_i32Value ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%u", obj->m_u32Value ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%lld", obj->m_i64Value ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%llu", obj->m_u64Value ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%f", obj->m_fl32Value ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%lf", obj->m_fl64Value ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Insert( 0, obj->m_StringValue.Get() ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%f %f", obj->m_vec2Value[0], obj->m_vec2Value[1] ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%f %f %f", obj->m_vec3Value[0], obj->m_vec3Value[1], obj->m_vec3Value[2] ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%f %f %f %f", obj->m_vec4Value[0], obj->m_vec4Value[1], obj->m_vec4Value[2], obj->m_vec4Value[3] ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { buf.Format( "%f %f %f", obj->m_angValue[0], obj->m_angValue[1], obj->m_angValue[2] ); } +template<> void CvarTypeTrait_ValueToStringFn( const CVValue_t *obj, CBufferString &buf ) { - return m_pParent->m_Value.m_nValue; + if( obj->m_clrValue.a() == 255 ) + buf.Format( "%d %d %d", obj->m_clrValue[0], obj->m_clrValue[1], obj->m_clrValue[2] ); + else + buf.Format( "%d %d %d %d", obj->m_clrValue[0], obj->m_clrValue[1], obj->m_clrValue[2], obj->m_clrValue[3] ); } -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a color -// Output : Color -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR Color ConVar::GetColor( void ) const +template<> void CvarTypeTrait_ClampFn( CVValue_t *obj, const CVValue_t *min, const CVValue_t *max ) { } +template<> void CvarTypeTrait_ClampFn( CVValue_t *obj, const CVValue_t *min, const CVValue_t *max ) +{ + if(min) for(int i = 0; i < 4; i++) obj->m_clrValue[i] = Max( obj->m_clrValue[i], min->m_clrValue[i] ); + if(max) for(int i = 0; i < 4; i++) obj->m_clrValue[i] = Min( obj->m_clrValue[i], max->m_clrValue[i] ); +} +template<> void CvarTypeTrait_ClampFn( CVValue_t *obj, const CVValue_t *min, const CVValue_t *max ) { - unsigned char *pColorElement = ((unsigned char *)&m_pParent->m_Value.m_nValue); - return Color( pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3] ); + if(min) for(int i = 0; i < 2; i++) obj->m_vec2Value[i] = Max( obj->m_vec2Value[i], min->m_vec2Value[i] ); + if(max) for(int i = 0; i < 2; i++) obj->m_vec2Value[i] = Min( obj->m_vec2Value[i], max->m_vec2Value[i] ); +} +template<> void CvarTypeTrait_ClampFn( CVValue_t *obj, const CVValue_t *min, const CVValue_t *max ) +{ + if(min) for(int i = 0; i < 3; i++) obj->m_vec3Value[i] = Max( obj->m_vec3Value[i], min->m_vec3Value[i] ); + if(max) for(int i = 0; i < 3; i++) obj->m_vec3Value[i] = Min( obj->m_vec3Value[i], max->m_vec3Value[i] ); +} +template<> void CvarTypeTrait_ClampFn( CVValue_t *obj, const CVValue_t *min, const CVValue_t *max ) +{ + if(min) for(int i = 0; i < 4; i++) obj->m_vec4Value[i] = Max( obj->m_vec4Value[i], min->m_vec4Value[i] ); + if(max) for(int i = 0; i < 4; i++) obj->m_vec4Value[i] = Min( obj->m_vec4Value[i], max->m_vec4Value[i] ); +} +template<> void CvarTypeTrait_ClampFn( CVValue_t *obj, const CVValue_t *min, const CVValue_t *max ) +{ + if(min) for(int i = 0; i < 3; i++) obj->m_angValue[i] = Max( obj->m_angValue[i], min->m_angValue[i] ); + if(max) for(int i = 0; i < 3; i++) obj->m_angValue[i] = Min( obj->m_angValue[i], max->m_angValue[i] ); } +class ConVarData; +static ConVarData *GetInvalidConVarData( EConVarType type ); -//----------------------------------------------------------------------------- +struct CVarTypeTraits +{ + template + CVarTypeTraits *InitAs( const char *name, const char *default_value ) + { + m_TypeName = name; + m_ByteSize = sizeof( T ); + m_IsPrimitive = std::is_literal_type_v; + + Construct = &CvarTypeTrait_ConstructFn; + Copy = &CvarTypeTrait_CopyFn; + Destruct = &CvarTypeTrait_DestructFn; + StringToValue = &CvarTypeTrait_StringToValueFn; + ValueToString = &CvarTypeTrait_ValueToStringFn; + Equal = &CvarTypeTrait_EqualFn; + Clamp = &CvarTypeTrait_ClampFn; + + m_DefaultValue = default_value; + m_InvalidCvarData = GetInvalidConVarData( TranslateConVarType() ); + + return this; + } -template <> FORCEINLINE_CVAR float ConVar::Get( void ) const { return GetFloat(); } -template <> FORCEINLINE_CVAR int ConVar::Get( void ) const { return GetInt(); } -template <> FORCEINLINE_CVAR bool ConVar::Get( void ) const { return GetBool(); } -template <> FORCEINLINE_CVAR const char * ConVar::Get( void ) const { return GetString(); } -template <> FORCEINLINE_CVAR float ConVar::Get( float *p ) const { return ( *p = GetFloat() ); } -template <> FORCEINLINE_CVAR int ConVar::Get( int *p ) const { return ( *p = GetInt() ); } -template <> FORCEINLINE_CVAR bool ConVar::Get( bool *p ) const { return ( *p = GetBool() ); } -template <> FORCEINLINE_CVAR const char * ConVar::Get( char const **p ) const { return ( *p = GetString() ); } + const char *m_TypeName; + int m_ByteSize; + bool m_IsPrimitive; -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. -// Output : const char * -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR const char *ConVar::GetString( void ) const + void (*Construct)(CVValue_t *obj); + void (*Copy)(CVValue_t *obj, const CVValue_t &other); + void (*Destruct)(CVValue_t *obj); + bool (*StringToValue)(const char *string, CVValue_t *obj); + void (*ValueToString)(const CVValue_t *obj, CBufferString &buf); + bool (*Equal)(const CVValue_t *obj, const CVValue_t *other); + void (*Clamp)(CVValue_t *obj, const CVValue_t *min, const CVValue_t *max); + + const char *m_DefaultValue; + ConVarData *m_InvalidCvarData; +}; + +static CVarTypeTraits *GetCvarTypeTraits( EConVarType type ) { - if ( m_nFlags & FCVAR_NEVER_AS_STRING ) - return "FCVAR_NEVER_AS_STRING"; - - char const *str = m_pParent->m_Value.m_pszString; - return str ? str : ""; + Assert( type >= 0 && type < EConVarType_MAX ); + + static CVarTypeTraits s_TypeTraits[EConVarType_MAX] = { + *CVarTypeTraits().InitAs( "bool", "false" ), + *CVarTypeTraits().InitAs( "int16", "0" ), + *CVarTypeTraits().InitAs( "uint16", "0" ), + *CVarTypeTraits().InitAs( "int32", "0" ), + *CVarTypeTraits().InitAs( "uint32", "0" ), + *CVarTypeTraits().InitAs( "int64", "0" ), + *CVarTypeTraits().InitAs( "uint64", "0" ), + *CVarTypeTraits().InitAs( "float32", "0" ), + *CVarTypeTraits().InitAs( "float64", "0" ), + *CVarTypeTraits().InitAs( "string", "" ), + *CVarTypeTraits().InitAs( "color", "0 0 0 255" ), + *CVarTypeTraits().InitAs( "vector2", "0 0" ), + *CVarTypeTraits().InitAs( "vector3", "0 0 0" ), + *CVarTypeTraits().InitAs( "vector4", "0 0 0 0" ), + *CVarTypeTraits().InitAs( "qangle", "0 0 0" ) + }; + + return &s_TypeTraits[type]; } -class CSplitScreenAddedConVar : public ConVar +class ConVarData { - typedef ConVar BaseClass; public: - CSplitScreenAddedConVar( int nSplitScreenSlot, const char *pName, const ConVar *pBaseVar ) : - BaseClass - ( - pName, - pBaseVar->GetDefault(), - // Keep basevar flags, except remove _SS and add _SS_ADDED instead - ( pBaseVar->GetFlags() & ~FCVAR_SS ) | FCVAR_SS_ADDED, - pBaseVar->GetHelpText(), - pBaseVar->HasMin(), - pBaseVar->GetMinValue(), - pBaseVar->HasMax(), - pBaseVar->GetMaxValue() - ), - m_pBaseVar( pBaseVar ), - m_nSplitScreenSlot( nSplitScreenSlot ) + enum { - for ( int i = 0; i < pBaseVar->GetChangeCallbackCount(); ++i ) - { - InstallChangeCallback( pBaseVar->GetChangeCallback( i ), false ); - } - Assert( nSplitScreenSlot >= 1 ); - Assert( nSplitScreenSlot < MAX_SPLITSCREEN_CLIENTS ); - Assert( m_pBaseVar ); - Assert( IsFlagSet( FCVAR_SS_ADDED ) ); - Assert( !IsFlagSet( FCVAR_SS ) ); + // GameInfo was used to initialize this cvar + CVARGI_INITIALIZED = (1 << 0), + // GameInfo has set default value, and it cannot be overriden + CVARGI_HAS_DEFAULT_VALUE = (1 << 1), + // GameInfo has set min value, and it cannot be overriden + CVARGI_HAS_MIN_VALUE = (1 << 2), + // GameInfo has set max value, and it cannot be overriden + CVARGI_HAS_MAX_VALUE = (1 << 3), + // GameInfo has set cvar version + CVARGI_HAS_VERSION = (1 << 4) + }; + + ConVarData( EConVarType type = EConVarType_Invalid ) : + m_Values {} + { + Invalidate( type, true ); } - const ConVar *GetBaseVar() const; - virtual const char *GetBaseName() const; - void SetSplitScreenPlayerSlot( int nSlot ); - virtual int GetSplitScreenPlayerSlot() const; + // Helper method to invalidate convar data to its default, pre-register state + void Invalidate( EConVarType type = EConVarType_Invalid, bool as_undefined = false ) + { + if(as_undefined) + m_pszName = ""; + m_defaultValue = CVValue_t::InvalidValue(); + m_minValue = nullptr; + m_maxValue = nullptr; + m_pszHelpString = as_undefined ? "This convar is being accessed prior to ConVar_Register being called" : nullptr; + m_eVarType = type; + m_Version = 0; + m_iTimesChanged = 0; + m_nFlags = FCVAR_REFERENCE; + m_iCallbackIndex = 0; + m_GameInfoFlags = 0; + } -protected: + const char *GetName( void ) const { return m_pszName; } + const char *GetHelpText( void ) const { return m_pszHelpString; } + bool HasHelpText() const { return m_pszHelpString && m_pszHelpString[0]; } - const ConVar *m_pBaseVar; - int m_nSplitScreenSlot; -}; + EConVarType GetType() const { return m_eVarType; } -FORCEINLINE_CVAR const ConVar *CSplitScreenAddedConVar::GetBaseVar() const -{ - Assert( m_pBaseVar ); - return m_pBaseVar; -} + short GetVersion() const { return m_Version; } -FORCEINLINE_CVAR const char *CSplitScreenAddedConVar::GetBaseName() const -{ - Assert( m_pBaseVar ); - return m_pBaseVar->GetName(); -} + int GetTimesChanged() const { return m_iTimesChanged; } + void SetTimesChanged( int val ) { m_iTimesChanged = val; } + void IncrementTimesChanged() { m_iTimesChanged++; } -FORCEINLINE_CVAR void CSplitScreenAddedConVar::SetSplitScreenPlayerSlot( int nSlot ) -{ - m_nSplitScreenSlot = nSlot; -} + bool IsFlagSet( uint64 flag ) const { return (m_nFlags & flag) != 0; } + void AddFlags( uint64 flags ) { m_nFlags |= flags; } + void RemoveFlags( uint64 flags ) { m_nFlags &= ~flags; } + uint64 GetFlags() const { return m_nFlags; } -FORCEINLINE_CVAR int CSplitScreenAddedConVar::GetSplitScreenPlayerSlot() const -{ - return m_nSplitScreenSlot; -} + int GetGameInfoFlags() const { return m_GameInfoFlags; } + int GetCallbackIndex() const { return m_iCallbackIndex; } -#endif // CONVAR_WORK_FINISHED + int GetMaxSplitScreenSlots() const; -//----------------------------------------------------------------------------- -// Used to read/write convars that already exist (replaces the FindVar method) -//----------------------------------------------------------------------------- -class ConVarRefAbstract -{ -public: -#ifdef CONVAR_WORK_FINISHED - ConVarRefAbstract( const char *pName ); - ConVarRefAbstract( const char *pName, bool bIgnoreMissing ); - ConVarRefAbstract( IConVar *pConVar ); + CVarTypeTraits *TypeTraits() const + { + Assert( m_eVarType != EConVarType_Invalid ); + return GetCvarTypeTraits( m_eVarType ); + } + + int GetDataByteSize() const { return TypeTraits()->m_ByteSize; } + bool IsPrimitiveType() const { return TypeTraits()->m_IsPrimitive; } + const char *GetDataTypeName() const { return TypeTraits()->m_TypeName; } - void Init( const char *pName, bool bIgnoreMissing ); - bool IsValid() const; - bool IsFlagSet( int64 nFlags ) const; - IConVar *GetLinkedConVar(); + bool HasDefaultValue() const { return m_defaultValue && m_defaultValue != CVValue_t::InvalidValue(); } + bool HasMinValue() const { return m_minValue != nullptr; } + bool HasMaxValue() const { return m_maxValue != nullptr; } - // Get/Set value - float GetFloat( void ) const; - int GetInt( void ) const; - Color GetColor( void ) const; - bool GetBool() const { return !!GetInt(); } - const char *GetString( void ) const; + CVValue_t *DefaultValue() const { return m_defaultValue; } + CVValue_t *MinValue() const { return m_minValue; } + CVValue_t *MaxValue() const { return m_maxValue; } - void SetValue( const char *pValue ); - void SetValue( float flValue ); - void SetValue( int nValue ); - void SetValue( Color value ); - void SetValue( bool bValue ); + // Since cvars can't be without default value, this just resets it to a global default value if it had one set + void RemoveDefaultValue() { if(HasDefaultValue()) TypeTraits()->StringToValue( TypeTraits()->m_DefaultValue, m_defaultValue ); } - const char *GetName() const; + // AMNOTE: Min/Max values are allocated by default by a CCvar allocator which doesn't cleanup memory, + // thus be careful when removing min/max values allocated by other sources and do cleanup yourself + void RemoveMinValue() { m_minValue = nullptr; } + void RemoveMaxValue() { m_maxValue = nullptr; } - const char *GetDefault() const; + // Updates default/min/max values to other value if present + bool UpdateDefaultValueString( const char *value ) { return HasDefaultValue() && TypeTraits()->StringToValue( value, m_defaultValue ); } + bool UpdateMinValueString( const char *value ) { return HasMinValue() && TypeTraits()->StringToValue( value, m_minValue ); } + bool UpdateMaxValueString( const char *value ) { return HasMaxValue() && TypeTraits()->StringToValue( value, m_maxValue ); } - const char *GetBaseName() const; + // Sets default value by copying it, doesn't need to be allocated + void SetDefaultValue( CVValue_t *value ) { TypeTraits()->Copy( m_defaultValue, value ); } - int GetSplitScreenPlayerSlot() const; + // AMNOTE: Expects you to manually allocate its value and for it to be alive while it's used by the cvar + // Also you should be responsible for clearing memory on cleanup, by default game uses CCvar memory allocator for this + void SetMinValue( CVValue_t *value ) { m_minValue = value; } + void SetMaxValue( CVValue_t *value ) { m_maxValue = value; } + + // Could be nullptr if slot is invalid for this cvar + CVValue_t *Value( CSplitScreenSlot slot ) const; + // Returns default_value if slot is invalid + CVValue_t *ValueOrDefault( CSplitScreenSlot slot ) const; + + bool IsSetToDefault( CSplitScreenSlot slot ) const { return TypeTraits()->Equal( ValueOrDefault( slot ), m_defaultValue ); } + bool IsAllSetToDefault() const; + + void ValueToString( CSplitScreenSlot slot, CBufferString &buf ) const { TypeTraits()->ValueToString( ValueOrDefault( slot ), buf ); } + void DefaultValueToString( CBufferString &buf ) const { TypeTraits()->ValueToString( m_defaultValue, buf ); } + void MinValueToString( CBufferString &buf ) const; + void MaxValueToString( CBufferString &buf ) const; + + void Construct( CSplitScreenSlot slot ) const { if(IsSlotInRange( slot )) TypeTraits()->Construct( Value( slot ) ); } + void Destruct( CSplitScreenSlot slot ) const { if(IsSlotInRange( slot )) TypeTraits()->Destruct( Value( slot ) ); } + bool IsEqual( CSplitScreenSlot slot, CVValue_t *other ) const { return TypeTraits()->Equal( ValueOrDefault( slot ), other ); } + void Clamp( CSplitScreenSlot slot ) const { if(IsSlotInRange( slot )) TypeTraits()->Clamp( Value( slot ), m_minValue, m_maxValue ); } private: -#endif // CONVAR_WORK_FINISHED - // High-speed method to read convar data - ConVarHandle m_Handle; - ConVar *m_pConVarState; -}; + bool IsSlotInRange( CSplitScreenSlot slot ) const { return slot.Get() == -1 || (slot.Get() >= 0 && slot.Get() < GetMaxSplitScreenSlots()); } + const char* m_pszName; -#ifdef CONVAR_WORK_FINISHED -//----------------------------------------------------------------------------- -// Did we find an existing convar of that name? -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR bool ConVarRefAbstract::IsFlagSet( int64 nFlags ) const -{ - return ( m_pConVar->IsFlagSet( nFlags ) != 0 ); -} + // Default value is expected to always be present, + // even if convar wasn't created with default value + // it would use global per type default value in that case + CVValue_t* m_defaultValue; -FORCEINLINE_CVAR IConVar *ConVarRefAbstract::GetLinkedConVar() -{ - return m_pConVar; -} + // Min/Max Could be nullptr if not set + CVValue_t* m_minValue; + CVValue_t* m_maxValue; -FORCEINLINE_CVAR const char *ConVarRefAbstract::GetName() const -{ - return m_pConVar->GetName(); -} + const char* m_pszHelpString; + EConVarType m_eVarType; -FORCEINLINE_CVAR const char *ConVarRefAbstract::GetBaseName() const -{ - return m_pConVar->GetBaseName(); -} + // Might be set by a gameinfo config via "version" key + short m_Version; -FORCEINLINE_CVAR int ConVarRefAbstract::GetSplitScreenPlayerSlot() const -{ - return m_pConVar->GetSplitScreenPlayerSlot(); -} + unsigned int m_iTimesChanged; + uint64 m_nFlags; -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a float -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR float ConVarRefAbstract::GetFloat( void ) const -{ - return m_pConVarState->m_Value.m_fValue; -} + // Index into a linked list of cvar callbacks + unsigned int m_iCallbackIndex; -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as an int -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR int ConVarRefAbstract::GetInt( void ) const -{ - return m_pConVarState->m_Value.m_nValue; -} + int m_GameInfoFlags; -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a color -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR Color ConVarRefAbstract::GetColor( void ) const + // At convar registration this is trimmed to better match convar type being used + // or if it was initialized as EConVarType_Invalid it would be of this size + uint8 m_Values[sizeof( CVValue_t ) * MAX_SPLITSCREEN_CLIENTS]; +}; + +static ConVarData *GetInvalidConVarData( EConVarType type ) { - return m_pConVarState->GetColor(); + Assert( type >= EConVarType_Invalid && type < EConVarType_MAX ); + + static ConVarData s_InvalidConVar[EConVarType_MAX + 1] = { + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ), + ConVarData( TranslateConVarType() ) // EConVarType_MAX + }; + + if(type == EConVarType_Invalid) + { + return &s_InvalidConVar[EConVarType_MAX]; + } + return &s_InvalidConVar[type]; } -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR const char *ConVarRefAbstract::GetString( void ) const +//----------------------------------------------------------------- +// Used to read/write/create convars (replaces the FindVar method) +//----------------------------------------------------------------- +class ConVarRef { - Assert( !IsFlagSet( FCVAR_NEVER_AS_STRING ) ); - return m_pConVarState->m_Value.m_pszString; -} +private: + static const uint16 kInvalidAccessIndex = 0xFFFF; +public: + ConVarRef() : m_ConVarAccessIndex( kInvalidAccessIndex ), m_ConVarRegisteredIndex( 0 ) {} + ConVarRef( uint16 convar_idx ) : m_ConVarAccessIndex( convar_idx ), m_ConVarRegisteredIndex( 0 ) {} + ConVarRef( uint16 convar_idx, int registered_idx ) : m_ConVarAccessIndex( convar_idx ), m_ConVarRegisteredIndex( registered_idx ) {} -FORCEINLINE_CVAR void ConVarRefAbstract::SetValue( const char *pValue ) -{ - m_pConVar->SetValue( pValue ); -} + ConVarRef( const char *name, bool allow_defensive = false ); -FORCEINLINE_CVAR void ConVarRefAbstract::SetValue( float flValue ) -{ - m_pConVar->SetValue( flValue ); -} + void InvalidateRef() { m_ConVarAccessIndex = kInvalidAccessIndex; m_ConVarRegisteredIndex = 0; } + bool IsValidRef() const { return m_ConVarAccessIndex != kInvalidAccessIndex; } + uint16 GetAccessIndex() const { return m_ConVarAccessIndex; } + int GetRegisteredIndex() const { return m_ConVarRegisteredIndex; } -FORCEINLINE_CVAR void ConVarRefAbstract::SetValue( int nValue ) -{ - m_pConVar->SetValue( nValue ); -} +protected: + // Index into internal linked list of concommands + uint16 m_ConVarAccessIndex; + // ConVar registered positional index + int m_ConVarRegisteredIndex; +}; -FORCEINLINE_CVAR void ConVarRefAbstract::SetValue( Color value ) +class ConVarRefAbstract : public ConVarRef { - m_pConVar->SetValue( value ); -} +public: + typedef ConVarRef BaseClass; -FORCEINLINE_CVAR void ConVarRefAbstract::SetValue( bool bValue ) -{ - m_pConVar->SetValue( bValue ? 1 : 0 ); -} + ConVarRefAbstract( const char *name, bool allow_defensive = false ) + : BaseClass( name, allow_defensive ), m_ConVarData( nullptr ) + { + Init( *this ); + } -FORCEINLINE_CVAR const char *ConVarRefAbstract::GetDefault() const -{ - return m_pConVarState->m_pszDefaultValue; -} + ConVarRefAbstract( ConVarRef ref, EConVarType type = EConVarType_Invalid ) + : BaseClass( ref ), m_ConVarData( nullptr ) + { + Init( *this, type ); + } -#if 0 -//----------------------------------------------------------------------------- -// Helper for referencing splitscreen convars (i.e., "name" and "name2") -//----------------------------------------------------------------------------- -class SplitScreenConVarRef + ConVarRefAbstract( const ConVarRefAbstract &ref ) + : BaseClass(), m_ConVarData( nullptr ) + { + CopyRef( ref ); + } + + const char *GetName() const { return m_ConVarData->GetName(); } + const char *GetHelpText() const { return m_ConVarData->GetHelpText(); } + bool HasHelpText() const { return m_ConVarData->HasHelpText(); } + + EConVarType GetType() const { return m_ConVarData->GetType(); } + + bool HasDefault() const { return m_ConVarData->HasDefaultValue(); } + bool HasMin() const { return m_ConVarData->HasMinValue(); } + bool HasMax() const { return m_ConVarData->HasMaxValue(); } + + bool IsFlagSet( uint64 flag ) const { return m_ConVarData->IsFlagSet( flag ); } + void AddFlags( uint64 flags ) { m_ConVarData->AddFlags( flags ); } + void RemoveFlags( uint64 flags ) { return m_ConVarData->RemoveFlags( flags ); } + uint64 GetFlags( void ) const { return m_ConVarData->GetFlags(); } + + // Returns true if convar should be treated as hidden + bool ShouldBeHidden() const { return !m_ConVarData || IsFlagSet( FCVAR_REFERENCE | FCVAR_HIDDEN | FCVAR_DEVELOPMENTONLY ); } + + bool IsSetToDefault( CSplitScreenSlot slot = -1 ) const { return m_ConVarData->IsSetToDefault( slot ); } + bool IsAllSetToDefault() const { return m_ConVarData->IsAllSetToDefault(); } + + void GetValueAsString( CBufferString &buf, CSplitScreenSlot slot = -1 ) const { m_ConVarData->ValueToString( slot, buf ); } + void GetDefaultAsString( CBufferString &buf ) const { m_ConVarData->DefaultValueToString( buf ); } + void GetMinAsString( CBufferString &buf ) const { m_ConVarData->MinValueToString( buf ); } + void GetMaxAsString( CBufferString &buf ) const { m_ConVarData->MaxValueToString( buf ); } + + bool IsValuePrimitive() const { return m_ConVarData->GetType() != EConVarType_Invalid && m_ConVarData->IsPrimitiveType(); } + + // Attempts to get value as a type T, does type conversion if possible, + // if no such action is available for CvarType/T combo, global default value for type T would be returned + template + const T GetAs( CSplitScreenSlot slot = -1 ) const; + + // Attempts to get value as a bool, does type conversion if possible, + // if no such action is available for CvarType/bool combo, global default value for bool would be returned + bool GetBool( CSplitScreenSlot slot = -1 ) const { return GetAs( slot ); } + // Attempts to get value as a float, does type conversion if possible, + // if no such action is available for CvarType/float combo, global default value for float would be returned + float GetFloat( CSplitScreenSlot slot = -1 ) const { return GetAs( slot ); } + // Attempts to get value as an int, does type conversion if possible, + // if no such action is available for CvarType/int combo, global default value for int would be returned + int GetInt( CSplitScreenSlot slot = -1 ) const { return GetAs( slot ); } + // Parses the value to string, mostly the same to GetValueAsString + CUtlString GetString( CSplitScreenSlot slot = -1 ) const; + + // Attempts to set value as a type T, does type conversion if possible, + // if no such action is available for CvarType/T combo, no action would be done + // + // AMNOTE: Using this function (as well as SetBool, SetFloat and SetInt) in a + // change callbacks (either global or cvar one) will result in a crash since it + // does constructor casts to CConVarRef which would be invalid if setvalue is queued + // (which will happen in callbacks), so the solution is to do a static_cast to + // CConVarRef yourself on the cvar you receive in a callback and call .Set() on it + template + void SetAs( T value, CSplitScreenSlot slot = -1 ); + + // Attempts to set value as a bool, does type conversion if possible, + // if no such action is available for CvarType/bool combo, no action would be done + void SetBool( bool value, CSplitScreenSlot slot = -1 ) { SetAs( value, slot ); } + // Attempts to set value as a float, does type conversion if possible, + // if no such action is available for CvarType/float combo, no action would be done + void SetFloat( float value, CSplitScreenSlot slot = -1 ) { SetAs( value, slot ); } + // Attempts to set value as an int, does type conversion if possible, + // if no such action is available for CvarType/int combo, no action would be done + void SetInt( int value, CSplitScreenSlot slot = -1 ) { SetAs( value, slot ); } + // Parses the string to CvarType type, returns true on success, false otherwise + bool SetString( CUtlString string, CSplitScreenSlot slot = -1 ); + + // Reset to default value + void Revert( CSplitScreenSlot slot = -1 ); + // Clamps value to min/max bounds if set + void Clamp( CSplitScreenSlot slot = -1 ) { m_ConVarData->Clamp( slot ); } + + CVarTypeTraits *TypeTraits() const { return m_ConVarData->TypeTraits(); } + ConVarData* GetConVarData() const { return m_ConVarData; }; + + // Checks if stored ConVarData points to invalid convar data + bool IsConVarDataValid() const { return m_ConVarData && GetType() != EConVarType_Invalid && m_ConVarData != TypeTraits()->m_InvalidCvarData; } + + // Checks if ConVarData is available for usage (means cvar was registered), + // mostly useful for CConVarRef cvars which register themselves partially + bool IsConVarDataAvailable() const { return !m_ConVarData->IsFlagSet( FCVAR_REFERENCE ) && IsConVarDataValid(); } + +protected: + ConVarRefAbstract() : BaseClass(), m_ConVarData( nullptr ) {} + + void CopyRef( const ConVarRefAbstract &ref ) + { + m_ConVarAccessIndex = ref.m_ConVarAccessIndex; + m_ConVarRegisteredIndex = ref.m_ConVarRegisteredIndex; + m_ConVarData = ref.m_ConVarData; + } + + void CallChangeCallbacks( CSplitScreenSlot slot, CVValue_t *new_value, CVValue_t *prev_value, const char *new_str, const char *prev_str ); + + void SetOrQueueValueInternal( CSplitScreenSlot slot, CVValue_t *value ); + void QueueSetValueInternal( CSplitScreenSlot slot, CVValue_t *value ); + void SetValueInternal( CSplitScreenSlot slot, CVValue_t *value ); + + // Does type conversion from CvarType to type T, only valid for primitive types + template + T ConvertFromPrimitiveTo( CSplitScreenSlot slot ) const; + // Does type conversion from type T to CvarType, only valid for primitive types + template + void ConvertToPrimitiveFrom( CSplitScreenSlot slot, T value ) const; + + // Initialises this cvar, if ref is invalid, ConVarData would be initialised to invalid convar data of a set type + void Init( ConVarRef ref, EConVarType type = EConVarType_Invalid ); + + void InvalidateConVarData( EConVarType type = EConVarType_Invalid ); + + ConVarData* m_ConVarData; +}; + +uint64 SanitiseConVarFlags( uint64 flags ); +void SetupConVar( ConVarRefAbstract *cvar, ConVarData **cvar_data, ConVarCreation_t &info ); +void UnRegisterConVar( ConVarRef *cvar ); + +template +class CConVarRef : public ConVarRefAbstract { public: - SplitScreenConVarRef( const char *pName ); - SplitScreenConVarRef( const char *pName, bool bIgnoreMissing ); - SplitScreenConVarRef( IConVar *pConVar ); + typedef ConVarRefAbstract BaseClass; - void Init( const char *pName, bool bIgnoreMissing ); - bool IsValid() const; - bool IsFlagSet( int64 nFlags ) const; + // Creates a convar ref that will pre-register convar in a system if it's not yet registered + // otherwise it just acts as a normal reference to a convar + // Mostly useful for cross module convar access, where you can't guarantee the order of creation + // and can't get direct access to convar CConVar + CConVarRef( const char *name ) : BaseClass() + { + ConVarValueInfo_t value_info( TranslateConVarType() ); + Register( name, FCVAR_REFERENCE, nullptr, value_info ); + } - // Get/Set value - float GetFloat( int nSlot ) const; - int GetInt( int nSlot ) const; - Color GetColor( int nSlot ) const; - bool GetBool( int nSlot ) const { return !!GetInt( nSlot ); } - const char *GetString( int nSlot ) const; + // Constructs typed cvar ref if the type matches, otherwise this would be initialised to invalid convar data! + CConVarRef( const ConVarRefAbstract &ref ) : BaseClass() + { + // If the ref type doesn't match ours, bad cast was attempted, + // fall back to invalid cvar data + if(ref.GetType() == TranslateConVarType()) + CopyRef( ref ); + else + Init( ConVarRef(), TranslateConVarType() ); + } - void SetValue( int nSlot, const char *pValue ); - void SetValue( int nSlot, float flValue ); - void SetValue( int nSlot, int nValue ); - void SetValue( int nSlot, Color value ); - void SetValue( int nSlot, bool bValue ); + // Constructs typed cvar ref if the type matches, otherwise this would be initialised to invalid convar data! + CConVarRef( const ConVarRef &ref ) : CConVarRef( ConVarRefAbstract( ref ) ) { } - const char *GetName( int nSlot ) const; + const T &Get( CSplitScreenSlot slot = -1 ) const { return *reinterpret_cast(m_ConVarData->ValueOrDefault( slot )); } + void Set( const T &value, CSplitScreenSlot slot = -1 ); - const char *GetDefault() const; + const T &GetDefault() const { *reinterpret_cast(m_ConVarData->DefaultValue()); } + const T &GetMin() const { *reinterpret_cast(m_ConVarData->MinValue()); } + const T &GetMax() const { *reinterpret_cast(m_ConVarData->MaxValue()); } - const char *GetBaseName() const; +protected: + CConVarRef() : BaseClass() {} -private: - struct cv_t + void Register( const char *name, uint64 flags, const char *help_string, const ConVarValueInfo_t &value_info ) { - IConVar *m_pConVar; - ConVar *m_pConVarState; - }; + Assert( name ); + + Init( ConVarRef(), TranslateConVarType() ); - cv_t m_Info[ MAX_SPLITSCREEN_CLIENTS ]; + ConVarCreation_t info; + info.m_pszName = name; + info.m_pszHelpString = help_string; + info.m_nFlags = SanitiseConVarFlags( flags ); + info.m_valueInfo = value_info; + + SetupConVar( this, &m_ConVarData, info ); + } }; -//----------------------------------------------------------------------------- -// Did we find an existing convar of that name? -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR bool SplitScreenConVarRef::IsFlagSet( int64 nFlags ) const +template +class CConVar : public CConVarRef { - return ( m_Info[ 0 ].m_pConVar->IsFlagSet( nFlags ) != 0 ); -} +public: + typedef CConVarRef BaseClass; -FORCEINLINE_CVAR const char *SplitScreenConVarRef::GetName( int nSlot ) const -{ - return m_Info[ nSlot ].m_pConVar->GetName(); -} + using FnChangeCallback_t = void(*)(CConVar *ref, CSplitScreenSlot nSlot, const T *pNewValue, const T *pOldValue); -FORCEINLINE_CVAR const char *SplitScreenConVarRef::GetBaseName() const -{ - return m_Info[ 0 ].m_pConVar->GetBaseName(); -} + CConVar( const char *name, uint64 flags, const char *help_string, const T &default_value, FnChangeCallback_t cb = nullptr ) + : BaseClass() + { + Assert( name ); -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a float -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR float SplitScreenConVarRef::GetFloat( int nSlot ) const + BaseClass::Init( ConVarRef(), TranslateConVarType() ); + + ConVarValueInfo_t value_info( TranslateConVarType() ); + value_info.SetDefaultValue( default_value ); + value_info.m_fnCallBack = reinterpret_cast(cb); + + BaseClass::Register( name, flags, help_string, value_info ); + } + + CConVar( const char *name, uint64 flags, const char *help_string, const T &default_value, bool min, const T &minValue, bool max, const T &maxValue, FnChangeCallback_t cb = nullptr ) + : BaseClass() + { + Assert( name ); + + BaseClass::Init( ConVarRef(), TranslateConVarType() ); + + ConVarValueInfo_t value_info( TranslateConVarType() ); + value_info.SetDefaultValue( default_value ); + + if(min) + value_info.SetMinValue( minValue ); + + if(max) + value_info.SetMaxValue( maxValue ); + + value_info.m_fnCallBack = reinterpret_cast(cb); + + BaseClass::Register( name, flags, help_string, value_info ); + } + + ~CConVar() + { + UnRegisterConVar( this ); + BaseClass::InvalidateConVarData(); + } +}; + +template +inline const T ConVarRefAbstract::GetAs( CSplitScreenSlot slot ) const { - return m_Info[ nSlot ].m_pConVarState->m_Value.m_fValue; + CVValue_t *value = m_ConVarData->ValueOrDefault( slot ); + + if(GetType() == TranslateConVarType()) + return *value; + else if(GetType() == EConVarType_String) + { + CVValue_t obj; + GetCvarTypeTraits( TranslateConVarType() )->Construct( &obj ); + + if(GetCvarTypeTraits( TranslateConVarType() )->StringToValue( value->m_StringValue.Get(), &obj )) + { + T ret = obj; + GetCvarTypeTraits( TranslateConVarType() )->Destruct( &obj ); + return ret; + } + + GetCvarTypeTraits( TranslateConVarType() )->Destruct( &obj ); + } + else if(IsValuePrimitive()) + return ConvertFromPrimitiveTo( slot ); + + return *GetInvalidConVarData( TranslateConVarType() )->DefaultValue(); } -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as an int -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR int SplitScreenConVarRef::GetInt( int nSlot ) const +template<> +inline const CUtlString ConVarRefAbstract::GetAs( CSplitScreenSlot slot ) const { - return m_Info[ nSlot ].m_pConVarState->m_Value.m_nValue; + CBufferString buf; + GetValueAsString( buf, slot ); + return buf.Get(); } -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as an int -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR Color SplitScreenConVarRef::GetColor( int nSlot ) const +inline CUtlString ConVarRefAbstract::GetString( CSplitScreenSlot slot ) const { - return m_Info[ nSlot ].m_pConVarState->GetColor(); + return GetAs( slot ); } -//----------------------------------------------------------------------------- -// Purpose: Return ConVar value as a string, return "" for bogus string pointer, etc. -//----------------------------------------------------------------------------- -FORCEINLINE_CVAR const char *SplitScreenConVarRef::GetString( int nSlot ) const +template +inline T ConVarRefAbstract::ConvertFromPrimitiveTo( CSplitScreenSlot slot ) const { - Assert( !IsFlagSet( FCVAR_NEVER_AS_STRING ) ); - return m_Info[ nSlot ].m_pConVarState->m_Value.m_pszString; -} + if constexpr(std::is_literal_type_v) + { + CVValue_t *value = m_ConVarData->ValueOrDefault( slot ); + switch(GetType()) + { + case EConVarType_Bool: return value->m_bValue; + case EConVarType_Int16: return value->m_i16Value; + case EConVarType_UInt16: return value->m_u16Value; + case EConVarType_Int32: return value->m_i32Value; + case EConVarType_UInt32: return value->m_u32Value; + case EConVarType_Int64: return value->m_i64Value; + case EConVarType_UInt64: return value->m_u64Value; + case EConVarType_Float32: return value->m_fl32Value; + case EConVarType_Float64: return value->m_fl64Value; + } + } -FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, const char *pValue ) -{ - m_Info[ nSlot ].m_pConVar->SetValue( pValue ); + return *GetInvalidConVarData( TranslateConVarType() )->DefaultValue(); } -FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, float flValue ) +template +inline void CConVarRef::Set( const T &value, CSplitScreenSlot slot ) { - m_Info[ nSlot ].m_pConVar->SetValue( flValue ); -} + CVValue_t *cvvalue = m_ConVarData->Value( slot ); -FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, int nValue ) -{ - m_Info[ nSlot ].m_pConVar->SetValue( nValue ); + if(cvvalue) + { + CVValue_t newval; + TypeTraits()->Construct( &newval ); + TypeTraits()->Copy( &newval, value ); + + SetOrQueueValueInternal( slot, &newval ); + + TypeTraits()->Destruct( &newval ); + } } -FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, Color value ) +template +inline void ConVarRefAbstract::SetAs( T value, CSplitScreenSlot slot ) { - m_Info[ nSlot ].m_pConVar->SetValue( value ); + if(GetType() == TranslateConVarType()) + { + CConVarRef( *this ).Set( value, slot ); + } + else if(GetType() == EConVarType_String) + { + CBufferString buf; + CVValue_t cvvalue( value ); + GetCvarTypeTraits( TranslateConVarType() )->ValueToString( &cvvalue, buf ); + + CConVarRef( *this ).Set( buf.Get(), slot ); + } + else if(IsValuePrimitive()) + { + ConvertToPrimitiveFrom( slot, value ); + } } -FORCEINLINE_CVAR void SplitScreenConVarRef::SetValue( int nSlot, bool bValue ) +template<> inline void ConVarRefAbstract::SetAs( CUtlString value, CSplitScreenSlot slot ) { - m_Info[ nSlot ].m_pConVar->SetValue( bValue ? 1 : 0 ); + SetString( value, slot ); } -FORCEINLINE_CVAR const char *SplitScreenConVarRef::GetDefault() const +template +inline void ConVarRefAbstract::ConvertToPrimitiveFrom( CSplitScreenSlot slot, T value ) const { - return m_Info[ 0 ].m_pConVarState->m_pszDefaultValue; + if constexpr(std::is_literal_type_v) + { + switch(GetType()) + { + case EConVarType_Bool: return CConVarRef( *this ).Set( value, slot ); + case EConVarType_Int16: return CConVarRef( *this ).Set( value, slot ); + case EConVarType_UInt16: return CConVarRef( *this ).Set( value, slot ); + case EConVarType_Int32: return CConVarRef( *this ).Set( value, slot ); + case EConVarType_UInt32: return CConVarRef( *this ).Set( value, slot ); + case EConVarType_Int64: return CConVarRef( *this ).Set( value, slot ); + case EConVarType_UInt64: return CConVarRef( *this ).Set( value, slot ); + case EConVarType_Float32: return CConVarRef( *this ).Set( value, slot ); + case EConVarType_Float64: return CConVarRef( *this ).Set( value, slot ); + } + } } -#endif -#endif // CONVAR_WORK_FINISHED //----------------------------------------------------------------------------- // Called by the framework to register ConVars and ConCommands with the ICVar //----------------------------------------------------------------------------- -void ConVar_Register( int64 nCVarFlag = 0 ); +typedef void (*FnConVarRegisterCallback)(ConVarRefAbstract *ref); +typedef void (*FnConCommandRegisterCallback)(ConCommandRef *ref); + +void ConVar_Register( uint64 nCVarFlag = 0, FnConVarRegisterCallback cvar_reg_cb = nullptr, FnConCommandRegisterCallback cmd_reg_cb = nullptr ); void ConVar_Unregister( ); //----------------------------------------------------------------------------- // Utility methods //----------------------------------------------------------------------------- -void ConVar_PrintDescription( const ConCommandBase *pVar ); +void ConVar_PrintDescription( const ConVarRefAbstract *ref ); //----------------------------------------------------------------------------- @@ -1062,16 +1406,16 @@ void ConVar_PrintDescription( const ConCommandBase *pVar ); #endif template< class T > -class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, public ICommandCompletionCallback +class CConCommandMemberAccessor : public ICommandCallback, public ICommandCompletionCallback, public ConCommand { typedef ConCommand BaseClass; typedef void ( T::*FnMemberCommandCallback_t )( const CCommandContext &context, const CCommand &command ); - typedef int ( T::*FnMemberCommandCompletionCallback_t )( const char *pPartial, CUtlVector< CUtlString > &commands ); + typedef int ( T::*FnMemberCommandCompletionCallback_t )( const CCommand &command, CUtlVector< CUtlString > &completions ); public: - CConCommandMemberAccessor( T* pOwner, const char *pName, FnMemberCommandCallback_t callback, const char *pHelpString = 0, - int64 flags = 0, FnMemberCommandCompletionCallback_t completionFunc = 0 ) : - BaseClass( &m_ConCommandRef, pName, this, pHelpString, flags, ( completionFunc != 0 ) ? this : NULL ) + CConCommandMemberAccessor( T* pOwner, const char *pName, FnMemberCommandCallback_t callback, const char *pHelpString, + uint64 flags = 0, FnMemberCommandCompletionCallback_t completionFunc = 0 ) : + BaseClass( pName, this, pHelpString, flags, ( completionFunc != 0 ) ? this : NULL ) { m_pOwner = pOwner; m_Func = callback; @@ -1080,7 +1424,7 @@ class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, pu ~CConCommandMemberAccessor() { - Shutdown(); + this->Destroy(); } void SetOwner( T* pOwner ) @@ -1088,23 +1432,22 @@ class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, pu m_pOwner = pOwner; } - virtual void CommandCallback( const CCommandContext &context, const CCommand &command ) + virtual void CommandCallback( const CCommandContext &context, const CCommand &command ) override { Assert( m_pOwner && m_Func ); (m_pOwner->*m_Func)( context, command ); } - virtual int CommandCompletionCallback( const char *pPartial, CUtlVector< CUtlString > &commands ) + virtual int CommandCompletionCallback( const CCommand &command, CUtlVector< CUtlString > &completions ) override { Assert( m_pOwner && m_CompletionFunc ); - return (m_pOwner->*m_CompletionFunc)( pPartial, commands ); + return (m_pOwner->*m_CompletionFunc)( command, completions ); } private: T* m_pOwner; FnMemberCommandCallback_t m_Func; FnMemberCommandCompletionCallback_t m_CompletionFunc; - ConCommandRefAbstract m_ConCommandRef; }; #ifdef _MSC_VER @@ -1115,61 +1458,52 @@ class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, pu // Purpose: Utility macros to quicky generate a simple console command //----------------------------------------------------------------------------- #define CON_COMMAND( name, description ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommand &args ); \ - static ConCommand name##_command( &name##_ref, #name, name##_callback, description ); \ + static ConCommand name##_command( #name, name##_callback, description ); \ static void name##_callback( const CCommand &args ) #ifdef CLIENT_DLL #define CON_COMMAND_SHARED( name, description ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommandContext &context, const CCommand &args ); \ - static ConCommand name##_command_client( &name##_ref, #name "_client", name##_callback, description ); \ + static ConCommand name##_command_client( #name "_client", name##_callback, description ); \ static void name##_callback( const CCommandContext &context, const CCommand &args ) #else #define CON_COMMAND_SHARED( name, description ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommandContext &context, const CCommand &args ); \ - static ConCommand name##_command( &name##_ref, #name, name##_callback, description ); \ + static ConCommand name##_command( #name, name##_callback, description ); \ static void name##_callback( const CCommandContext &context, const CCommand &args ) #endif #define CON_COMMAND_F( name, description, flags ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommandContext &context, const CCommand &args ); \ - static ConCommand name##_command( &name##_ref, #name, name##_callback, description, flags ); \ + static ConCommand name##_command( #name, name##_callback, description, flags ); \ static void name##_callback( const CCommandContext &context, const CCommand &args ) #ifdef CLIENT_DLL #define CON_COMMAND_F_SHARED( name, description, flags ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommandContext &context, const CCommand &args ); \ - static ConCommand name##_command_client( &name##_ref, #name "_client", name##_callback, description, flags ); \ + static ConCommand name##_command_client( #name "_client", name##_callback, description, flags ); \ static void name##_callback( const CCommandContext &context, const CCommand &args ) #else #define CON_COMMAND_F_SHARED( name, description, flags ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommandContext &context, const CCommand &args ); \ - static ConCommand name##_command( &name##_ref, #name, name##_callback, description, flags ); \ + static ConCommand name##_command( #name, name##_callback, description, flags ); \ static void name##_callback( const CCommandContext &context, const CCommand &args ) #endif #define CON_COMMAND_F_COMPLETION( name, description, flags, completion ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommandContext &context, const CCommand &args ); \ - static ConCommand name##_command( &name##_ref, #name, name##_callback, description, flags, completion ); \ + static ConCommand name##_command( #name, name##_callback, description, flags, completion ); \ static void name##_callback( const CCommandContext &context, const CCommand &args ) #ifdef CLIENT_DLL #define CON_COMMAND_F_COMPLETION_SHARED( name, description, flags, completion ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommandContext &context, const CCommand &args ); \ static ConCommand name##_command_client( name##_command, #name "_client", name##_callback, description, flags, completion ); \ static void name##_callback( const CCommandContext &context, const CCommand &args ) #else #define CON_COMMAND_F_COMPLETION_SHARED( name, description, flags, completion ) \ - static ConCommandRefAbstract name##_ref; \ static void name##_callback( const CCommandContext &context, const CCommand &args ); \ static ConCommand name##_command( name##_command, #name, name##_callback, description, flags, completion ); \ static void name##_callback( const CCommandContext &context, const CCommand &args ) @@ -1177,15 +1511,13 @@ class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, pu #define CON_COMMAND_EXTERN( name, _funcname, description ) \ - static ConCommandRefAbstract name##_ref; \ void _funcname( const CCommandContext &context, const CCommand &args ); \ - static ConCommand name##_command( &name##_ref, #name, _funcname, description ); \ + static ConCommand name##_command( #name, _funcname, description ); \ void _funcname( const CCommandContext &context, const CCommand &args ) #define CON_COMMAND_EXTERN_F( name, _funcname, description, flags ) \ - static ConCommandRefAbstract name##_ref; \ void _funcname( const CCommandContext &context, const CCommand &args ); \ - static ConCommand name##_command( &name##_ref, #name, _funcname, description, flags ); \ + static ConCommand name##_command( #name, _funcname, description, flags ); \ void _funcname( const CCommandContext &context, const CCommand &args ) #define CON_COMMAND_MEMBER_F( _thisclass, name, _funcname, description, flags ) \ @@ -1204,5 +1536,4 @@ class CConCommandMemberAccessor : public ConCommand, public ICommandCallback, pu \ CCommandMemberInitializer_##_funcname m_##_funcname##_register; \ - #endif // CONVAR_H diff --git a/public/tier1/convar_serverbounded.h b/public/tier1/convar_serverbounded.h deleted file mode 100644 index 94b1ecb7e..000000000 --- a/public/tier1/convar_serverbounded.h +++ /dev/null @@ -1,54 +0,0 @@ -//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============// -// -// Purpose: Helper class for cvars that have restrictions on their value. -// -//=============================================================================// - -#ifndef CONVAR_SERVERBOUNDED_H -#define CONVAR_SERVERBOUNDED_H -#ifdef _WIN32 -#pragma once -#endif - -#ifdef CONVAR_WORK_FINISHED -// This class is used to virtualize a ConVar's value, so the client can restrict its -// value while connected to a server. When using this across modules, it's important -// to dynamic_cast it to a ConVar_ServerBounded or you won't get the restricted value. -// -// NOTE: FCVAR_USERINFO vars are not virtualized before they are sent to the server -// (we have no way to detect if the virtualized value would change), so -// if you want to use a bounded cvar's value on the server, you must rebound it -// the same way the client does. -class ConVar_ServerBounded : public ConVar -{ -public: - ConVar_ServerBounded( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString ) - : ConVar( pName, pDefaultValue, flags, pHelpString ) - { - } - - ConVar_ServerBounded( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, FnChangeCallback_t callback ) - : ConVar( pName, pDefaultValue, flags, pHelpString, callback ) - { - } - - ConVar_ServerBounded( char const *pName, char const *pDefaultValue, int flags, char const *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) - : ConVar( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax ) {} - - // You must implement GetFloat. - virtual float GetFloat() const = 0; - - // You can optionally implement these. - virtual int GetInt() const { return (int)GetFloat(); } - virtual bool GetBool() const { return ( GetInt() != 0 ); } - - // Use this to get the underlying cvar's value. - float GetBaseFloatValue() const - { - return ConVar::GetFloat(); - } -}; -#endif // CONVAR_WORK_FINISHED - - -#endif // CONVAR_SERVERBOUNDED_H diff --git a/public/tier1/strtools.h b/public/tier1/strtools.h index 95ad8ddb2..b0cf7c425 100644 --- a/public/tier1/strtools.h +++ b/public/tier1/strtools.h @@ -310,6 +310,46 @@ PLATFORM_INTERFACE float64 V_StringToFloat64Raw(const char *buf, float64 default // Parses string as a float32 value, if the parsing fails, default_value is returned, doesn't perform error checking/reporting PLATFORM_INTERFACE float32 V_StringToFloat32Raw(const char *buf, float32 default_value, bool *successful = NULL, char **remainder = NULL); +// Templatised and shortened version of the generic V_StringTo* functions +// these are silent, so you won't get error console warnings if parsing fails by default +template +inline bool V_StringToValue( const char *string, T &value, uint flags = PARSING_FLAG_SKIP_ASSERT | PARSING_FLAG_SKIP_WARNING ); + +template <> inline bool V_StringToValue( const char *string, bool &value, uint flags ) +{ bool success = false; value = V_StringToBool( string, false, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, int8 &value, uint flags ) +{ bool success = false; value = V_StringToInt8( string, 0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, uint8 &value, uint flags ) +{ bool success = false; value = V_StringToUint8( string, 0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, int16 &value, uint flags ) +{ bool success = false; value = V_StringToInt16( string, 0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, uint16 &value, uint flags ) +{ bool success = false; value = V_StringToUint16( string, 0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, int32 &value, uint flags ) +{ bool success = false; value = V_StringToInt32( string, 0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, uint32 &value, uint flags ) +{ bool success = false; value = V_StringToUint32( string, 0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, int64 &value, uint flags ) +{ bool success = false; value = V_StringToInt64( string, 0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, uint64 &value, uint flags ) +{ bool success = false; value = V_StringToUint64( string, 0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, float32 &value, uint flags ) +{ bool success = false; value = V_StringToFloat32( string, 0.0f, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, float64 &value, uint flags ) +{ bool success = false; value = V_StringToFloat64( string, 0.0, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, Vector &value, uint flags ) +{ bool success = false; V_StringToVector( string, value, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, Vector2D &value, uint flags ) +{ bool success = false; V_StringToVector2D( string, value, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, Vector4D &value, uint flags ) +{ bool success = false; V_StringToVector4D( string, value, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, Color &value, uint flags ) +{ bool success = false; V_StringToColor( string, value, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, QAngle &value, uint flags ) +{ bool success = false; V_StringToQAngle( string, value, &success, nullptr, flags ); return success; } +template <> inline bool V_StringToValue( const char *string, Quaternion &value, uint flags ) +{ bool success = false; V_StringToQuaternion( string, value, &success, nullptr, flags ); return success; } + // returns string immediately following prefix, (ie str+strlen(prefix)) or NULL if prefix not found PLATFORM_INTERFACE const char *_V_StringAfterPrefix( const char *str, const char *prefix ); PLATFORM_INTERFACE const char *_V_StringAfterPrefixCaseSensitive( const char *str, const char *prefix ); diff --git a/tier1/convar.cpp b/tier1/convar.cpp index da72ad532..98ccb0ae7 100644 --- a/tier1/convar.cpp +++ b/tier1/convar.cpp @@ -1,5 +1,5 @@ -//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // @@ -17,7 +17,6 @@ #include "tier1/utlvector.h" #include "tier1/utlbuffer.h" #include "tier1/tier1.h" -#include "tier1/convar_serverbounded.h" #include "icvar.h" #include "tier0/dbg.h" #include "Color.h" @@ -26,39 +25,37 @@ #endif #include "tier0/memdbgon.h" - -// Comment this out when we release. -//#define ALLOW_DEVELOPMENT_CVARS - +// AMNOTE: Define this if you need cvars/concommands to respect flags sanitization rules +// #define SANITIZE_CVAR_FLAGS 1 //----------------------------------------------------------------------------- -// Statically constructed list of ConCommandBases, +// Statically constructed list of ConVars/ConCommands, // used for registering them with the ICVar interface //----------------------------------------------------------------------------- -static int64 s_nCVarFlag = 0; +static FnConVarRegisterCallback s_ConVarRegCB = nullptr; +static FnConCommandRegisterCallback s_ConCommandRegCB = nullptr; +static uint64 s_nCVarFlag = 0; static bool s_bRegistered = false; -class ConCommandRegList; class ConCommandRegList { public: - static void RegisterCommand(ConCommand* pCmd) + struct Entry_t { - if (s_bConCommandsRegistered) - { - ConCommandHandle hndl = g_pCVar->RegisterConCommand(pCmd, s_nCVarFlag); - if (!hndl.IsValid()) - { - Plat_FatalErrorFunc("RegisterConCommand: Unknown error registering con command \"%s\"!\n", pCmd->GetName()); - DebuggerBreakIfDebugging(); - } + ConCommandCreation_t m_Info; + ConCommandRef *m_Command = nullptr; + }; - pCmd->SetHandle(hndl); - } - else + static void RegisterConCommand( const Entry_t &cmd ) + { + *cmd.m_Command = g_pCVar->RegisterConCommand( cmd.m_Info, s_nCVarFlag ); + if(!cmd.m_Command->IsValidRef()) { - GetCommandRegList()->AddToTail(pCmd); + Plat_FatalErrorFunc( "RegisterConCommand: Unknown error registering con command \"%s\"!\n", cmd.m_Info.m_pszName ); + DebuggerBreakIfDebugging(); } + else if(s_ConCommandRegCB) + s_ConCommandRegCB( cmd.m_Command ); } static void RegisterAll() @@ -67,234 +64,235 @@ class ConCommandRegList { s_bConCommandsRegistered = true; - for(int i = 0; i < GetCommandRegList()->Count(); i++) + ConCommandRegList *prev = nullptr; + for(auto list = s_pRoot; list; list = prev) { - ConCommand *pCmd = GetCommandRegList()->Element(i); - ConCommandHandle hndl = g_pCVar->RegisterConCommand(pCmd, s_nCVarFlag); - pCmd->SetHandle(hndl); - - if (!hndl.IsValid()) + for(size_t i = 0; i < list->m_nSize; i++) { - Plat_FatalErrorFunc("RegisterConCommand: Unknown error registering con command \"%s\"!\n", pCmd->GetName()); - DebuggerBreakIfDebugging(); + RegisterConCommand( list->m_Entries[i] ); } - } + + prev = list->m_pPrev; + delete list; + }; } } -private: - // GAMMACASE: Required to prevent static initialization order problem https://isocpp.org/wiki/faq/ctors#static-init-order - static CUtlVector *GetCommandRegList() + static void AddToList( const Entry_t &cmd ) { - static CUtlVector s_ConCommandRegList; - return &s_ConCommandRegList; + if(s_bConCommandsRegistered) + { + RegisterConCommand( cmd ); + return; + } + + auto list = s_pRoot; + + if(!list || list->m_nSize >= (sizeof( m_Entries ) / sizeof( m_Entries[0] ))) + { + list = new ConCommandRegList; + list->m_nSize = 0; + list->m_pPrev = s_pRoot; + + s_pRoot = list; + } + + list->m_Entries[list->m_nSize++] = cmd; } +private: + uint32 m_nSize; + Entry_t m_Entries[100]; + ConCommandRegList *m_pPrev; + +public: static bool s_bConCommandsRegistered; + static ConCommandRegList *s_pRoot; }; bool ConCommandRegList::s_bConCommandsRegistered = false; +ConCommandRegList *ConCommandRegList::s_pRoot = nullptr; -#ifdef CONVAR_WORK_FINISHED -template -void check_size() { - static_assert(ExpectedSize == RealSize, "Size mismatch"); -}; +void SetupConCommand( ConCommand *cmd, const ConCommandCreation_t& info ) +{ + ConCommandRegList::Entry_t entry; + entry.m_Info = info; + entry.m_Command = cmd; -class ConVarRegList; -static ConVarRegList* s_pConVarRegList = nullptr; + ConCommandRegList::AddToList( entry ); +} + +void UnRegisterConCommand( ConCommand *cmd ) +{ + if(cmd->IsValidRef()) + { + if(g_pCVar) + g_pCVar->UnregisterConCommandCallbacks( *cmd ); + + cmd->InvalidateRef(); + } +} class ConVarRegList { public: - ConVarRegList() + struct Entry_t { - check_size(); - check_size(); - } + ConVarCreation_t m_Info; - static bool AreConVarsRegistered() + ConVarRefAbstract *m_pConVar = nullptr; + ConVarData **m_pConVarData = nullptr; + }; + + static void RegisterConVar( const Entry_t &cvar ) { - return s_bConVarsRegistered; + g_pCVar->RegisterConVar( cvar.m_Info, s_nCVarFlag, cvar.m_pConVar, cvar.m_pConVarData ); + if(!cvar.m_pConVar->IsValidRef()) + { + Plat_FatalErrorFunc( "RegisterConVar: Unknown error registering convar \"%s\"!\n", cvar.m_Info.m_pszName ); + DebuggerBreakIfDebugging(); + } + // Don't let references pass as a newly registered cvar + else if(s_ConVarRegCB && (cvar.m_Info.m_nFlags & FCVAR_REFERENCE) == 0) + s_ConVarRegCB( cvar.m_pConVar ); } static void RegisterAll() { - if (!s_bConVarsRegistered && g_pCVar) + if(!s_bConVarsRegistered && g_pCVar) { s_bConVarsRegistered = true; - ConVarRegList* pList = s_pConVarRegList; - while (pList != nullptr) + ConVarRegList *prev = nullptr; + for(auto list = s_pRoot; list; list = prev) { - FOR_EACH_VEC(s_pConVarRegList->m_Vec, i) + for(size_t i = 0; i < list->m_nSize; i++) { - ConVar* pConVar = &pList->m_Vec[i]; - ConVarHandle hndl; - //g_pCVar->RegisterConVar(pConVar, s_nCVarFlag, hndl); - //pConVar->SetHandle(hndl); - - if (!hndl.IsValid()) - { - Plat_FatalErrorFunc("RegisterConCommand: Unknown error registering convar \"%s\"!\n", pConVar->GetName()); - DebuggerBreakIfDebugging(); - } + RegisterConVar( list->m_Entries[i] ); } - ConVarRegList *pNext = pList->m_pNext; - delete pList; - pList = pNext; - } - - s_pConVarRegList = nullptr; + prev = list->m_pPrev; + delete list; + }; } } -private: - CUtlVectorFixed m_Vec; - ConVarRegList* m_pNext = nullptr; - static bool s_bConVarsRegistered; -}; + static void AddToList( const Entry_t &cvar ) + { + if(s_bConVarsRegistered) + { + RegisterConVar( cvar ); + return; + } -bool ConVarRegList::s_bConVarsRegistered = false; -#endif // CONVAR_WORK_FINISHED + auto list = s_pRoot; -//----------------------------------------------------------------------------- -// Called by the framework to register ConCommandBases with the ICVar -//----------------------------------------------------------------------------- -void ConVar_Register( int64 nCVarFlag) -{ - if ( !g_pCVar || s_bRegistered ) - return; + if(!list || list->m_nSize >= (sizeof( m_Entries ) / sizeof( m_Entries[0] ))) + { + list = new ConVarRegList; + list->m_nSize = 0; + list->m_pPrev = s_pRoot; - s_bRegistered = true; - s_nCVarFlag = nCVarFlag; + s_pRoot = list; + } - ConCommandRegList::RegisterAll(); -#ifdef CONVAR_WORK_FINISHED - ConVarRegList::RegisterAll(); -#endif // CONVAR_WORK_FINISHED -} + list->m_Entries[list->m_nSize++] = cvar; + } -void ConVar_Unregister( ) -{ - if ( !g_pCVar || !s_bRegistered ) - return; +private: + uint32 m_nSize; + Entry_t m_Entries[100]; + ConVarRegList *m_pPrev; - s_bRegistered = false; -} +public: + static bool s_bConVarsRegistered; + static ConVarRegList *s_pRoot; +}; +bool ConVarRegList::s_bConVarsRegistered = false; +ConVarRegList *ConVarRegList::s_pRoot = nullptr; -//----------------------------------------------------------------------------- -// Purpose: Default constructor -//----------------------------------------------------------------------------- -ConCommandBase::ConCommandBase( void ) +void SetupConVar( ConVarRefAbstract *cvar, ConVarData **cvar_data, ConVarCreation_t &info ) { - m_pszName = NULL; - m_pszHelpString = NULL; + ConVarRegList::Entry_t entry; + entry.m_Info = info; + entry.m_pConVar = cvar; + entry.m_pConVarData = cvar_data; - m_nFlags = 0; + ConVarRegList::AddToList( entry ); } -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -ConCommandBase::~ConCommandBase( void ) +void UnRegisterConVar( ConVarRef *cvar ) { -} + if(cvar->IsValidRef()) + { + if(g_pCVar) + g_pCVar->UnregisterConVarCallbacks( *cvar ); -//----------------------------------------------------------------------------- -// Purpose: Return name of the command/var -// Output : const char -//----------------------------------------------------------------------------- -const char *ConCommandBase::GetName( void ) const -{ - return m_pszName; + cvar->InvalidateRef(); + } } - -//----------------------------------------------------------------------------- -// Purpose: -// Input : flag - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool ConCommandBase::IsFlagSet( int64 flag ) const +uint64 SanitiseConVarFlags( uint64 flags ) { - return ( flag & m_nFlags ) ? true : false; +#ifdef SANITIZE_CVAR_FLAGS + if(!CommandLine()->HasParm( "-tools" ) + && (flags & (FCVAR_DEVELOPMENTONLY + | FCVAR_ARCHIVE + | FCVAR_USERINFO + | FCVAR_CHEAT + | FCVAR_RELEASE + | FCVAR_SERVER_CAN_EXECUTE + | FCVAR_CLIENT_CAN_EXECUTE + | FCVAR_CLIENTCMD_CAN_EXECUTE)) == 0) + { + flags |= FCVAR_DEFENSIVE | FCVAR_DEVELOPMENTONLY; + } +#endif + return flags; } //----------------------------------------------------------------------------- -// Purpose: -// Input : flags - +// Called by the framework to register ConCommandBases with the ICVar //----------------------------------------------------------------------------- -void ConCommandBase::AddFlags( int64 flags ) +void ConVar_Register( uint64 nCVarFlag, FnConVarRegisterCallback cvar_reg_cb, FnConCommandRegisterCallback cmd_reg_cb ) { - m_nFlags |= flags; + if ( !g_pCVar || s_bRegistered ) + { + return; + } -#ifdef ALLOW_DEVELOPMENT_CVARS - m_nFlags &= ~FCVAR_DEVELOPMENTONLY; -#endif -} + s_bRegistered = true; + s_nCVarFlag = nCVarFlag; + s_ConVarRegCB = cvar_reg_cb; + s_ConCommandRegCB = cmd_reg_cb; -void ConCommandBase::RemoveFlags( int64 flags ) -{ - m_nFlags &= ~flags; + ConCommandRegList::RegisterAll(); + ConVarRegList::RegisterAll(); } -int64 ConCommandBase::GetFlags( void ) const +void ConVar_Unregister( ) { - return m_nFlags; -} + if ( !g_pCVar || !s_bRegistered ) + return; -//----------------------------------------------------------------------------- -// Purpose: -// Output : const char -//----------------------------------------------------------------------------- -const char *ConCommandBase::GetHelpText( void ) const -{ - return m_pszHelpString; + s_bRegistered = false; } -//----------------------------------------------------------------------------- -// -// 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, "{}()':" ); - } - + EnsureBuffers(); Reset(); } -CCommand::CCommand( int nArgC, const char **ppArgV ) +CCommand::CCommand( int nArgC, const char **ppArgV ) : CCommand() { Assert( nArgC > 0 ); - if ( !s_bBuiltBreakSet ) - { - s_bBuiltBreakSet = true; - CharacterSetBuild( &s_BreakSet, "{}()':" ); - } - - Reset(); - char *pBuf = m_ArgvBuffer.Base(); char *pSBuf = m_ArgSBuffer.Base(); for ( int i = 0; i < nArgC; ++i ) @@ -327,50 +325,59 @@ CCommand::CCommand( int nArgC, const char **ppArgV ) } } +void CCommand::EnsureBuffers() +{ + m_ArgSBuffer.SetSize( MaxCommandLength() ); + m_ArgvBuffer.SetSize( MaxCommandLength() ); +} + void CCommand::Reset() { m_nArgv0Size = 0; - m_ArgSBuffer.RemoveAll(); - m_ArgvBuffer.RemoveAll(); + m_ArgSBuffer.Base()[0] = '\0'; m_Args.RemoveAll(); } characterset_t* CCommand::DefaultBreakSet() { - return &s_BreakSet; + return g_pCVar->GetCharacterSet(); } -bool CCommand::Tokenize( const char *pCommand, characterset_t *pBreakSet ) +bool CCommand::Tokenize( CUtlString pCommand, characterset_t *pBreakSet ) { + if(m_ArgSBuffer.Count() == 0) + EnsureBuffers(); + Reset(); - if ( !pCommand ) + + if ( pCommand.IsEmpty() ) return false; // Use default break set if ( !pBreakSet ) { - pBreakSet = &s_BreakSet; + pBreakSet = DefaultBreakSet(); } // 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 = V_strlen( pCommand ); - if ( nLen >= COMMAND_MAX_LENGTH - 1 ) + int nLen = pCommand.Length(); + if ( nLen >= m_ArgSBuffer.Count() - 1 ) { Warning( "CCommand::Tokenize: Encountered command which overflows the tokenizer buffer.. Skipping!\n" ); return false; } - memcpy( m_ArgSBuffer.Base(), pCommand, nLen + 1 ); + memmove( m_ArgSBuffer.Base(), pCommand, nLen + 1 ); // Parse the current command into the current command buffer CUtlBuffer bufParse( m_ArgSBuffer.Base(), nLen, CUtlBuffer::TEXT_BUFFER | CUtlBuffer::READ_ONLY); int nArgvBufferSize = 0; - while ( bufParse.IsValid() && ( m_Args.Count() < COMMAND_MAX_ARGC ) ) + while ( bufParse.IsValid() ) { char *pArgvBuf = &m_ArgvBuffer[nArgvBufferSize]; - int nMaxLen = COMMAND_MAX_LENGTH - nArgvBufferSize; + int nMaxLen = m_ArgvBuffer.Count() - nArgvBufferSize; int nStartGet = bufParse.TellGet(); int nSize = bufParse.ParseToken( pBreakSet, pArgvBuf, nMaxLen ); if ( nSize < 0 ) @@ -406,13 +413,10 @@ bool CCommand::Tokenize( const char *pCommand, characterset_t *pBreakSet ) } m_Args.AddToTail( pArgvBuf ); - if( m_Args.Count() >= COMMAND_MAX_ARGC ) - { - Warning( "CCommand::Tokenize: Encountered command which overflows the argument buffer.. Clamped!\n" ); - } - nArgvBufferSize += nSize + 1; - Assert( nArgvBufferSize <= COMMAND_MAX_LENGTH ); + + if(nArgvBufferSize >= m_ArgvBuffer.Count()) + break; } return true; @@ -442,853 +446,268 @@ int CCommand::FindArgInt( const char *pName, int nDefaultVal ) const return nDefaultVal; } - -//----------------------------------------------------------------------------- -// Default console command autocompletion function -//----------------------------------------------------------------------------- -int DefaultCompletionFunc( const char *partial, CUtlVector< CUtlString > &commands ) +ConCommandRef::ConCommandRef( const char *name, bool allow_developer ) { - return 0; + *this = g_pCVar->FindConCommand( name, allow_developer ); } - -ConCommand::ConCommand( ConCommandRefAbstract *pReferenceOut, const char *pName, FnCommandCallback_t callback, const char *pHelpString /*= 0*/, int64 flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ ) +ConCommandData *ConCommandRef::GetRawData() { - m_Callback.m_fnCommandCallback = callback; - m_Callback.m_bUsingCommandCallbackInterface = false; - m_Callback.m_bHasVoidCommandCallback = false; - m_Callback.m_bHasContextlessCommandCallback = false; - - m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc; - m_bHasCompletionCallback = completionFunc != 0 ? true : false; - m_bUsingCommandCompletionInterface = false; - - m_pReference = pReferenceOut; - m_pReference->handle.Reset(); - - // Setup the rest - Create( pName, pHelpString, flags ); + return g_pCVar->GetConCommandData( *this ); } -ConCommand::ConCommand( ConCommandRefAbstract *pReferenceOut, const char *pName, FnCommandCallbackVoid_t callback, const char *pHelpString /*= 0*/, int64 flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ ) +void ConCommandRef::Dispatch( const CCommandContext &context, const CCommand &command ) { - m_Callback.m_fnVoidCommandCallback = callback; - m_Callback.m_bUsingCommandCallbackInterface = false; - m_Callback.m_bHasVoidCommandCallback = true; - m_Callback.m_bHasContextlessCommandCallback = false; - - m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc; - m_bHasCompletionCallback = completionFunc != 0 ? true : false; - m_bUsingCommandCompletionInterface = false; - - m_pReference = pReferenceOut; - m_pReference->handle.Reset(); - - // Setup the rest - Create( pName, pHelpString, flags ); + g_pCVar->DispatchConCommand( *this, context, command ); } -ConCommand::ConCommand( ConCommandRefAbstract *pReferenceOut, const char *pName, FnCommandCallbackNoContext_t callback, const char *pHelpString /*= 0*/, int64 flags /*= 0*/, FnCommandCompletionCallback completionFunc /*= 0*/ ) +void ConCommand::Create( const char* pName, const ConCommandCallbackInfo_t &cb, const char* pHelpString, uint64 flags, const ConCommandCompletionCallbackInfo_t &completion_cb ) { - m_Callback.m_fnContextlessCommandCallback = callback; - m_Callback.m_bUsingCommandCallbackInterface = false; - m_Callback.m_bHasVoidCommandCallback = false; - m_Callback.m_bHasContextlessCommandCallback = true; - - m_fnCompletionCallback = completionFunc ? completionFunc : DefaultCompletionFunc; - m_bHasCompletionCallback = completionFunc != 0 ? true : false; - m_bUsingCommandCompletionInterface = false; - - m_pReference = pReferenceOut; - m_pReference->handle.Reset(); - - // Setup the rest - Create(pName, pHelpString, flags); -} - -ConCommand::ConCommand( ConCommandRefAbstract *pReferenceOut, const char *pName, ICommandCallback *pCallback, const char *pHelpString /*= 0*/, int64 flags /*= 0*/, ICommandCompletionCallback *pCompletionCallback /*= 0*/ ) -{ - m_Callback.m_pCommandCallback = pCallback; - m_Callback.m_bUsingCommandCallbackInterface = true; - m_Callback.m_bHasVoidCommandCallback = false; - m_Callback.m_bHasContextlessCommandCallback = false; - - m_pCommandCompletionCallback = pCompletionCallback; - m_bHasCompletionCallback = true; - m_bUsingCommandCompletionInterface = true; - - m_pReference = pReferenceOut; - m_pReference->handle.Reset(); - - // Setup the rest - Create( pName, pHelpString, flags ); -} - -//----------------------------------------------------------------------------- -// Destructor -//----------------------------------------------------------------------------- -ConCommand::~ConCommand( void ) -{ - ConCommandRefAbstract *pRef = GetRef(); - if ( pRef ) - { - pRef->handle.Unregister(); - } -} - - -//----------------------------------------------------------------------------- -// Purpose: Used internally by OneTimeInit to initialize. -//----------------------------------------------------------------------------- -void ConCommand::Init() -{ - ConCommandRegList::RegisterCommand( this ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : *pName - -// callback - -// *pHelpString - -// flags - -//----------------------------------------------------------------------------- -void ConCommand::Create( const char* pName, const char* pHelpString /*= 0*/, int64 flags /*= 0*/ ) -{ - static const char* empty_string = ""; - // Name should be static data Assert(pName); - m_pszName = pName; - m_pszHelpString = pHelpString ? pHelpString : empty_string; - - m_nFlags = flags; - -#ifdef ALLOW_DEVELOPMENT_CVARS - m_nFlags &= ~FCVAR_DEVELOPMENTONLY; -#endif - Init(); -} - -void ConCommand::Shutdown() -{ - GetRef()->handle.Unregister(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Invoke the function if there is one -//----------------------------------------------------------------------------- -void ConCommandHandle::Dispatch( const CCommandContext &context, const CCommand &command ) -{ - ConCommand *pCommand = g_pCVar->GetCommand( *this ); - if ( pCommand->m_Callback.m_fnCommandCallback ) - { - if ( pCommand->m_Callback.m_bUsingCommandCallbackInterface ) - { - pCommand->m_Callback.m_pCommandCallback->CommandCallback( context, command ); - } - else if ( pCommand->m_Callback.m_bHasVoidCommandCallback ) - { - pCommand->m_Callback.m_fnVoidCommandCallback(); - } - else if ( pCommand->m_Callback.m_bHasContextlessCommandCallback ) - { - pCommand->m_Callback.m_fnContextlessCommandCallback( command ); - } - else - { - pCommand->m_Callback.m_fnCommandCallback( context, command ); - } - } + ConCommandCreation_t info; + info.m_pszName = pName; + info.m_pszHelpString = pHelpString; + info.m_nFlags = SanitiseConVarFlags( flags ); + info.m_CBInfo = cb; + info.m_CompletionCBInfo = completion_cb; - // Command without callback!!! - AssertMsg(0, ("Encountered ConCommand without a callback!\n")); + SetupConCommand( this, info ); } -bool ConCommandHandle::HasCallback() const +void ConCommand::Destroy() { - ConCommand *pCommand = g_pCVar->GetCommand( *this ); - return pCommand->m_Callback.m_fnCommandCallback != nullptr; -} - -void ConCommandHandle::Unregister() -{ - if (IsValid()) - { - if ( g_pCVar ) - g_pCVar->UnregisterConCommand( *this ); - - Reset(); - } + UnRegisterConCommand( this ); } -//----------------------------------------------------------------------------- -// Purpose: Calls the autocompletion method to get autocompletion suggestions -//----------------------------------------------------------------------------- -int ConCommand::AutoCompleteSuggest( const char *partial, CUtlVector< CUtlString > &commands ) -{ - if (m_bUsingCommandCompletionInterface) - { - if ( !m_pCommandCompletionCallback ) - return 0; - return m_pCommandCompletionCallback->CommandCompletionCallback( partial, commands ); - } - - Assert( m_fnCompletionCallback ); - if ( !m_fnCompletionCallback ) - return 0; - - return m_fnCompletionCallback( partial, commands ); -} - - -//----------------------------------------------------------------------------- -// Returns true if the console command can autocomplete -//----------------------------------------------------------------------------- -bool ConCommand::CanAutoComplete( void ) -{ - return m_bHasCompletionCallback; -} - - -#ifdef CONVAR_WORK_FINISHED //----------------------------------------------------------------------------- // // Console Variables // //----------------------------------------------------------------------------- -//----------------------------------------------------------------------------- -// Various constructors -//----------------------------------------------------------------------------- -ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags /* = 0 */ ) +int ConVarData::GetMaxSplitScreenSlots() const { - Create( pName, pDefaultValue, flags ); -} + if((m_nFlags & FCVAR_PER_USER) != 0) + return g_pCVar->GetMaxSplitScreenSlots(); -ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags, const char *pHelpString ) -{ - Create( pName, pDefaultValue, flags, pHelpString ); + return 1; } -ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags, const char *pHelpString, bool bMin, float fMin, bool bMax, float fMax ) +CVValue_t *ConVarData::Value( CSplitScreenSlot slot ) const { - Create( pName, pDefaultValue, flags, pHelpString, bMin, fMin, bMax, fMax ); -} + if(slot.Get() == -1) + slot = CSplitScreenSlot( 0 ); -ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 flags, const char *pHelpString, FnChangeCallback_t callback ) -{ - Create( pName, pDefaultValue, flags, pHelpString, false, 0.0, false, 0.0, callback ); -} + if(!IsSlotInRange( slot )) + return nullptr; -ConVar::ConVar( const char *pName, const char *pDefaultValue, int64 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 ); + return (CVValue_t *)&m_Values[GetDataByteSize() * slot.Get()]; } - -//----------------------------------------------------------------------------- -// Destructor -//----------------------------------------------------------------------------- -ConVar::~ConVar( void ) +CVValue_t *ConVarData::ValueOrDefault( CSplitScreenSlot slot ) const { - if ( m_Value.m_pszString ) - { - delete[] m_Value.m_pszString; - m_Value.m_pszString = NULL; - } + CVValue_t *value = Value( slot ); + return value ? value : m_defaultValue; } - -//----------------------------------------------------------------------------- -// Install a change callback (there shouldn't already be one....) -//----------------------------------------------------------------------------- -void ConVar::InstallChangeCallback( FnChangeCallback_t callback, bool bInvoke ) +bool ConVarData::IsAllSetToDefault() const { - if (callback) - { - if (m_fnChangeCallbacks.Find(callback) != -1) - { - m_fnChangeCallbacks.AddToTail(callback); - if (bInvoke) - callback(this, m_Value.m_pszString, m_Value.m_fValue); - } - else - { - Warning("InstallChangeCallback ignoring duplicate change callback!!!\n"); - } - } - else + for(int i = 0; i < GetMaxSplitScreenSlots(); i++) { - Warning("InstallChangeCallback called with NULL callback, ignoring!!!\n"); + if(!IsSetToDefault( i )) + return false; } -} - -bool ConVar::IsFlagSet( int64 flag ) const -{ - return ( flag & m_pParent->m_nFlags ) ? true : false; -} - -const char *ConVar::GetHelpText( void ) const -{ - return m_pParent->m_pszHelpString; -} - -void ConVar::AddFlags( int64 flags ) -{ - m_pParent->m_nFlags |= flags; -#ifdef ALLOW_DEVELOPMENT_CVARS - m_pParent->m_nFlags &= ~FCVAR_DEVELOPMENTONLY; -#endif + return true; } -int64 ConVar::GetFlags( void ) const +void ConVarData::MinValueToString( CBufferString &buf ) const { - return m_pParent->m_nFlags; + if(HasMinValue()) + TypeTraits()->ValueToString( m_minValue, buf ); + else + buf.Insert( 0, "" ); } -const char *ConVar::GetName( void ) const +void ConVarData::MaxValueToString( CBufferString &buf ) const { - return m_pParent->m_pszName; -} - - -//----------------------------------------------------------------------------- -// Purpose: -// Input : -//----------------------------------------------------------------------------- -void ConVar::Init() -{ - BaseClass::Init(); + if(HasMaxValue()) + TypeTraits()->ValueToString( m_maxValue, buf ); + else + buf.Insert( 0, "" ); } -const char *ConVar::GetBaseName( void ) const +ConVarRef::ConVarRef( const char *name, bool allow_developer ) { - return m_pParent->m_pszName; + *this = g_pCVar->FindConVar( name, allow_developer ); } -int ConVar::GetSplitScreenPlayerSlot( void ) const +void ConVarRefAbstract::Init( ConVarRef ref, EConVarType type ) { - return 0; -} + m_ConVarData = nullptr; -//----------------------------------------------------------------------------- -// Purpose: -// Input : *value - -//----------------------------------------------------------------------------- -void ConVar::InternalSetValue( const char *value ) -{ - float fNewValue; - char tempVal[ 32 ]; - char *val; - - Assert(m_pParent == this); // Only valid for root convars. - - float flOldValue = m_Value.m_fValue; - - val = (char *)value; - fNewValue = ( float )atof( value ); - - if ( ClampValue( fNewValue ) ) - { - Q_snprintf( tempVal,sizeof(tempVal), "%f", fNewValue ); - val = tempVal; - } + if(g_pCVar) + m_ConVarData = g_pCVar->GetConVarData( ref ); - // Redetermine value - m_Value.m_fValue = fNewValue; - m_Value.m_nValue = ( int )( fNewValue ); - - if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ) - { - ChangeStringValue( val, flOldValue ); - } + if(!m_ConVarData) + InvalidateConVarData( type ); } -//----------------------------------------------------------------------------- -// Purpose: -// Input : *tempVal - -//----------------------------------------------------------------------------- -void ConVar::ChangeStringValue( const char *tempVal, float flOldValue ) +void ConVarRefAbstract::InvalidateConVarData( EConVarType type ) { - Assert( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ); - - char* pszOldValue = (char*)stackalloc( m_Value.m_StringLength ); - memcpy( pszOldValue, m_Value.m_pszString, m_Value.m_StringLength ); - - int len = Q_strlen(tempVal) + 1; - - if ( len > m_Value.m_StringLength) - { - if (m_Value.m_pszString) - { - delete[] m_Value.m_pszString; - } - - m_Value.m_pszString = new char[len]; - m_Value.m_StringLength = 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, flOldValue ); - } - - if (g_pCVar) - g_pCVar->CallGlobalChangeCallbacks( this, pszOldValue, flOldValue ); + if(type == EConVarType_Invalid) + m_ConVarData = GetInvalidConVarData( EConVarType_Invalid ); + else + m_ConVarData = GetCvarTypeTraits( type )->m_InvalidCvarData; } -//----------------------------------------------------------------------------- -// Purpose: Check whether to clamp and then perform clamp -// Input : value - -// Output : Returns true if value changed -//----------------------------------------------------------------------------- -bool ConVar::ClampValue( float& value ) +void ConVarRefAbstract::CallChangeCallbacks( CSplitScreenSlot slot, CVValue_t *new_value, CVValue_t *prev_value, const char *new_str, const char *prev_str ) { - if ( m_bHasMin && ( value < m_fMinVal ) ) - { - value = m_fMinVal; - return true; - } - - if ( m_bHasMax && ( value > m_fMaxVal ) ) - { - value = m_fMaxVal; - return true; - } + if(slot.Get() == -1) + slot = CSplitScreenSlot( 0 ); - return false; + g_pCVar->CallChangeCallback( *this, slot, new_value, prev_value ); + g_pCVar->CallGlobalChangeCallbacks( this, slot, new_str, prev_str ); } -//----------------------------------------------------------------------------- -// Purpose: -// Input : *value - -//----------------------------------------------------------------------------- -void ConVar::InternalSetFloatValue( float fNewValue ) +void ConVarRefAbstract::SetOrQueueValueInternal( CSplitScreenSlot slot, CVValue_t *value ) { - if ( fNewValue == m_Value.m_fValue ) - return; - - Assert( m_pParent == this ); // Only valid for root convars. - - // Check bounds - ClampValue( fNewValue ); - - // Redetermine value - float flOldValue = m_Value.m_fValue; - m_Value.m_fValue = fNewValue; - m_Value.m_nValue = ( int )fNewValue; - - if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ) - { - char tempVal[ 32 ]; - Q_snprintf( tempVal, sizeof( tempVal), "%f", m_Value.m_fValue ); - ChangeStringValue( tempVal, flOldValue ); - } + if(m_ConVarData->IsFlagSet( FCVAR_PERFORMING_CALLBACKS )) + QueueSetValueInternal( slot, value ); else - { - Assert( m_fnChangeCallbacks.Count() == 0 ); - } + SetValueInternal( slot, value ); } -//----------------------------------------------------------------------------- -// Purpose: -// Input : *value - -//----------------------------------------------------------------------------- -void ConVar::InternalSetIntValue( int nValue ) +void ConVarRefAbstract::QueueSetValueInternal( CSplitScreenSlot slot, CVValue_t *value ) { - if ( nValue == m_Value.m_nValue ) - return; + if(slot.Get() == -1) + slot = CSplitScreenSlot( 0 ); - Assert( m_pParent == this ); // Only valid for root convars. + TypeTraits()->Clamp( value, m_ConVarData->MinValue(), m_ConVarData->MaxValue() ); - float fValue = (float)nValue; - if ( ClampValue( fValue ) ) - { - nValue = ( int )( fValue ); - } - - // Redetermine value - float flOldValue = m_Value.m_fValue; - m_Value.m_fValue = fValue; - m_Value.m_nValue = nValue; - - if ( !( m_nFlags & FCVAR_NEVER_AS_STRING ) ) - { - char tempVal[ 32 ]; - Q_snprintf( tempVal, sizeof( tempVal ), "%d", m_Value.m_nValue ); - ChangeStringValue( tempVal, flOldValue ); - } - else - { - Assert( m_fnChangeCallbacks.Count() == 0 ); - } + if(!m_ConVarData->IsEqual( slot, value )) + g_pCVar->QueueThreadSetValue( this, slot, value ); } -//----------------------------------------------------------------------------- -// Purpose: -// Input : *value - -//----------------------------------------------------------------------------- -void ConVar::InternalSetColorValue( Color cValue ) +void ConVarRefAbstract::SetValueInternal( CSplitScreenSlot slot, CVValue_t *value ) { - int color = cValue.GetRawColor(); - InternalSetIntValue( color ); -} + CVValue_t *curr_value = m_ConVarData->ValueOrDefault( slot ); -//----------------------------------------------------------------------------- -// Purpose: Private creation -//----------------------------------------------------------------------------- -void ConVar::Create( const char *pName, const char *pDefaultValue, int64 flags /*= 0*/, - const char *pHelpString /*= NULL*/, bool bMin /*= false*/, float fMin /*= 0.0*/, - bool bMax /*= false*/, float fMax /*= false*/, FnChangeCallback_t callback /*= NULL*/ ) -{ - static const char *empty_string = ""; + CVValue_t prev; + TypeTraits()->Construct( &prev ); - m_pParent = this; + TypeTraits()->Copy( &prev, *curr_value ); + TypeTraits()->Destruct( curr_value ); - // Name should be static data - m_pszDefaultValue = pDefaultValue ? pDefaultValue : empty_string; - Assert( m_pszDefaultValue ); + TypeTraits()->Copy( curr_value, *value ); + m_ConVarData->Clamp( slot ); - m_Value.m_StringLength = strlen( m_pszDefaultValue ) + 1; - m_Value.m_pszString = new char[m_Value.m_StringLength]; - memcpy( m_Value.m_pszString, m_pszDefaultValue, m_Value.m_StringLength ); - - m_bHasMin = bMin; - m_fMinVal = fMin; - m_bHasMax = bMax; - m_fMaxVal = fMax; - - if (callback) - m_fnChangeCallbacks.AddToTail(callback); - - m_Value.m_fValue = ( float )atof( m_Value.m_pszString ); - - // Bounds Check, should never happen, if it does, no big deal - if ( m_bHasMin && ( m_Value.m_fValue < m_fMinVal ) ) + if(!m_ConVarData->IsEqual( slot, &prev )) { - Assert( 0 ); - } - - if ( m_bHasMax && ( m_Value.m_fValue > m_fMaxVal ) ) - { - Assert( 0 ); - } - - m_Value.m_nValue = ( int )m_Value.m_fValue; - - BaseClass::Create( 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: -// Input : value - -//----------------------------------------------------------------------------- -void ConVar::SetValue( Color value ) -{ - ConVar *var = ( ConVar * )m_pParent; - var->InternalSetColorValue( value ); -} + CBufferString prev_str, new_str; -//----------------------------------------------------------------------------- -// Purpose: Reset to default value -//----------------------------------------------------------------------------- -void ConVar::Revert( void ) -{ - // Force default value again - ConVar *var = ( ConVar * )m_pParent; - var->SetValue( var->m_pszDefaultValue ); -} + TypeTraits()->ValueToString( &prev, prev_str ); + TypeTraits()->ValueToString( curr_value, new_str ); -//----------------------------------------------------------------------------- -// 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; -} + m_ConVarData->IncrementTimesChanged(); -//----------------------------------------------------------------------------- -// Purpose: -// Input : maxVal - -//----------------------------------------------------------------------------- -bool ConVar::GetMax( float& maxVal ) const -{ - maxVal = m_pParent->m_fMaxVal; - return m_pParent->m_bHasMax; -} + CallChangeCallbacks( slot, curr_value, &prev, new_str.Get(), prev_str.Get() ); + } -//----------------------------------------------------------------------------- -// Purpose: -// Output : const char -//----------------------------------------------------------------------------- -const char *ConVar::GetDefault( void ) const -{ - return m_pParent->m_pszDefaultValue; + TypeTraits()->Destruct( &prev ); } - -//----------------------------------------------------------------------------- -// This version is simply used to make reading convars simpler. -// Writing convars isn't allowed in this mode -//----------------------------------------------------------------------------- -class CEmptyConVar : public ConVar +bool ConVarRefAbstract::SetString( CUtlString string, CSplitScreenSlot slot ) { -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 void SetValue( Color cValue ) {} - virtual const char *GetName( void ) const { return ""; } - virtual bool IsFlagSet( int nFlags ) const { return false; } -}; + CVValue_t *value = m_ConVarData->Value( slot ); -static CEmptyConVar s_EmptyConVar; + if(!value) + return true; + + if(GetType() != EConVarType_String) + string.Trim( "\t\n\v\f\r " ); -ConVarRef::ConVarRef( const char *pName ) -{ - Init( pName, false ); -} + CVValue_t new_value; + TypeTraits()->Construct( &new_value ); -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 ) + bool success = false; + if(TypeTraits()->StringToValue( string.Get(), &new_value )) { - 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; - } + SetOrQueueValueInternal( slot, &new_value ); + success = true; } -} -ConVarRef::ConVarRef( IConVar *pConVar ) -{ - m_pConVar = pConVar ? pConVar : &s_EmptyConVar; - m_pConVarState = static_cast< ConVar * >( m_pConVar ); + TypeTraits()->Destruct( &new_value ); + return success; } -bool ConVarRef::IsValid() const +void ConVarRefAbstract::Revert( CSplitScreenSlot slot ) { - return m_pConVar != &s_EmptyConVar; + CBufferString buf; + GetDefaultAsString( buf ); + SetString( buf.Get(), slot ); } - //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void ConVar_PrintFlags( const ConCommandBase *var ) +void ConVar_PrintDescription( const ConVarRefAbstract *ref ) { - bool any = false; - if ( var->IsFlagSet( FCVAR_GAMEDLL ) ) - { - ConMsg( " game" ); - any = true; - } + Assert( ref ); - if ( var->IsFlagSet( FCVAR_CLIENTDLL ) ) + static struct { - ConMsg( " client" ); - any = true; - } - - if ( var->IsFlagSet( FCVAR_ARCHIVE ) ) - { - ConMsg( " archive" ); - any = true; - } + uint64 m_Flag; + const char *m_Name; + } s_FlagsMap[] = { + { FCVAR_GAMEDLL, "game" }, + { FCVAR_CLIENTDLL, "client" }, + { FCVAR_ARCHIVE, "archive" }, + { FCVAR_NOTIFY, "notify" }, + { FCVAR_SPONLY, "singleplayer" }, + { FCVAR_NOT_CONNECTED, "notconnected" }, + { FCVAR_CHEAT, "cheat" }, + { FCVAR_REPLICATED, "replicated" }, + { FCVAR_SERVER_CAN_EXECUTE, "server_can_execute" }, + { FCVAR_CLIENTCMD_CAN_EXECUTE, "clientcmd_can_execute" }, + { FCVAR_USERINFO, "userinfo" }, + { FCVAR_PER_USER, "per_user" } + }; - if ( var->IsFlagSet( FCVAR_NOTIFY ) ) - { - ConMsg( " notify" ); - any = true; - } + CBufferStringN<4096> desc; + CBufferString buf; - if ( var->IsFlagSet( FCVAR_SPONLY ) ) - { - ConMsg( " singleplayer" ); - any = true; - } + ref->GetValueAsString( buf ); + desc.AppendFormat( "\"%s\" = \"%s\"", ref->GetName(), buf.Get() ); - if ( var->IsFlagSet( FCVAR_NOT_CONNECTED ) ) + if(!ref->IsSetToDefault()) { - ConMsg( " notconnected" ); - any = true; + ref->GetDefaultAsString( buf ); + desc.AppendFormat( " ( def. \"%s\" )", buf.Get() ); } - if ( var->IsFlagSet( FCVAR_CHEAT ) ) + if(ref->HasMin()) { - ConMsg( " cheat" ); - any = true; + ref->GetMinAsString( buf ); + desc.AppendFormat( " min. %s", buf.Get() ); } - if ( var->IsFlagSet( FCVAR_REPLICATED ) ) + if(ref->HasMax()) { - ConMsg( " replicated" ); - any = true; + ref->GetMaxAsString( buf ); + desc.AppendFormat( " max. %s", buf.Get() ); } - if ( var->IsFlagSet( FCVAR_SERVER_CAN_EXECUTE ) ) + for(size_t i = 0; i < (sizeof( s_FlagsMap ) / sizeof( s_FlagsMap[0] )); i++) { - ConMsg( " server_can_execute" ); - any = true; + if(ref->IsFlagSet( s_FlagsMap[i].m_Flag )) + desc.AppendFormat( " %s", s_FlagsMap[i].m_Name ); } - 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( 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() ); - } - } + if(ref->HasHelpText()) + ConMsg( "%-120s - %s\n", desc.Get(), ref->GetHelpText() ); 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 ); - } + ConMsg( "%-120s\n", desc.Get() ); } -#endif // CONVAR_WORK_FINISHED