aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj5
-rw-r--r--NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters21
-rw-r--r--NorthstarDedicatedTest/audio.cpp4
-rw-r--r--NorthstarDedicatedTest/bits.cpp28
-rw-r--r--NorthstarDedicatedTest/bits.h10
-rw-r--r--NorthstarDedicatedTest/buildainfile.cpp6
-rw-r--r--NorthstarDedicatedTest/chatcommand.cpp3
-rw-r--r--NorthstarDedicatedTest/clientauthhooks.cpp6
-rw-r--r--NorthstarDedicatedTest/color.h90
-rw-r--r--NorthstarDedicatedTest/concommand.cpp100
-rw-r--r--NorthstarDedicatedTest/concommand.h79
-rw-r--r--NorthstarDedicatedTest/convar.cpp474
-rw-r--r--NorthstarDedicatedTest/convar.h114
-rw-r--r--NorthstarDedicatedTest/cvar.cpp65
-rw-r--r--NorthstarDedicatedTest/cvar.h37
-rw-r--r--NorthstarDedicatedTest/dedicated.cpp6
-rw-r--r--NorthstarDedicatedTest/gameutils.cpp23
-rw-r--r--NorthstarDedicatedTest/latencyflex.cpp4
-rw-r--r--NorthstarDedicatedTest/logging.cpp9
-rw-r--r--NorthstarDedicatedTest/masterserver.cpp60
-rw-r--r--NorthstarDedicatedTest/modmanager.cpp2
-rw-r--r--NorthstarDedicatedTest/pch.h11
-rw-r--r--NorthstarDedicatedTest/playlist.cpp4
-rw-r--r--NorthstarDedicatedTest/serverauthentication.cpp54
-rw-r--r--NorthstarDedicatedTest/sourceconsole.cpp1
25 files changed, 1028 insertions, 188 deletions
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
index 5235dfc9..6da5f2b7 100644
--- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
+++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj
@@ -110,16 +110,19 @@
<ClInclude Include="audio.h" />
<ClInclude Include="bansystem.h" />
<ClInclude Include="bitbuf.h" />
+ <ClInclude Include="bits.h" />
<ClInclude Include="buildainfile.h" />
<ClInclude Include="chatcommand.h" />
<ClInclude Include="clientchathooks.h" />
<ClInclude Include="localchatwriter.h" />
<ClInclude Include="serverchathooks.h" />
<ClInclude Include="clientauthhooks.h" />
+ <ClInclude Include="color.h" />
<ClInclude Include="concommand.h" />
<ClInclude Include="configurables.h" />
<ClInclude Include="context.h" />
<ClInclude Include="convar.h" />
+ <ClInclude Include="cvar.h" />
<ClInclude Include="dedicated.h" />
<ClInclude Include="dedicatedmaterialsystem.h" />
<ClInclude Include="filesystem.h" />
@@ -556,6 +559,7 @@
<ItemGroup>
<ClCompile Include="audio.cpp" />
<ClCompile Include="bansystem.cpp" />
+ <ClCompile Include="bits.cpp" />
<ClCompile Include="buildainfile.cpp" />
<ClCompile Include="chatcommand.cpp" />
<ClCompile Include="clientauthhooks.cpp" />
@@ -564,6 +568,7 @@
<ClCompile Include="configurables.cpp" />
<ClCompile Include="context.cpp" />
<ClCompile Include="convar.cpp" />
+ <ClCompile Include="cvar.cpp" />
<ClCompile Include="dedicated.cpp" />
<ClCompile Include="dedicatedmaterialsystem.cpp" />
<ClCompile Include="dllmain.cpp" />
diff --git a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
index 11205541..f0c8c380 100644
--- a/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
+++ b/NorthstarDedicatedTest/NorthstarDedicatedTest.vcxproj.filters
@@ -121,6 +121,12 @@
<Filter Include="Header Files\include\libcurl">
<UniqueIdentifier>{ea1e17a6-40b7-4e1b-8edb-e9ae704ce604}</UniqueIdentifier>
</Filter>
+ <Filter Include="Source Files\Shared\Math">
+ <UniqueIdentifier>{59b0f68f-daa7-4641-b6fa-8464b56da2bb}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Shared\Math">
+ <UniqueIdentifier>{44a83740-9d70-480d-9a7a-43b81f8eab9e}</UniqueIdentifier>
+ </Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
@@ -1449,6 +1455,15 @@
<ClInclude Include="configurables.h">
<Filter>Header Files\Client</Filter>
</ClInclude>
+ <ClInclude Include="cvar.h">
+ <Filter>Header Files\Shared\Convar</Filter>
+ </ClInclude>
+ <ClInclude Include="color.h">
+ <Filter>Header Files\Shared\Math</Filter>
+ </ClInclude>
+ <ClInclude Include="bits.h">
+ <Filter>Header Files\Shared\Math</Filter>
+ </ClInclude>
<ClInclude Include="serverchathooks.h">
<Filter>Header Files\Server</Filter>
</ClInclude>
@@ -1592,6 +1607,12 @@
<ClCompile Include="configurables.cpp">
<Filter>Source Files\Client</Filter>
</ClCompile>
+ <ClCompile Include="cvar.cpp">
+ <Filter>Source Files\Shared\Convar</Filter>
+ </ClCompile>
+ <ClCompile Include="bits.cpp">
+ <Filter>Source Files\Shared\Math</Filter>
+ </ClCompile>
<ClCompile Include="serverchathooks.cpp">
<Filter>Source Files\Server</Filter>
</ClCompile>
diff --git a/NorthstarDedicatedTest/audio.cpp b/NorthstarDedicatedTest/audio.cpp
index d6f70255..d3201b89 100644
--- a/NorthstarDedicatedTest/audio.cpp
+++ b/NorthstarDedicatedTest/audio.cpp
@@ -345,7 +345,7 @@ bool __declspec(noinline) __fastcall LoadSampleMetadata_Internal(
{
char* eventName = (char*)parentEvent + 0x110;
- if (Cvar_ns_print_played_sounds->m_nValue > 0)
+ if (Cvar_ns_print_played_sounds->GetInt() > 0)
spdlog::info("[AUDIO] Playing event {}", eventName);
auto iter = g_CustomAudioManager.m_loadedAudioOverrides.find(eventName);
@@ -462,7 +462,7 @@ void __fastcall MilesLog_Hook(int level, const char* string) { spdlog::info("[MS
void InitialiseMilesAudioHooks(HMODULE baseAddress)
{
- Cvar_ns_print_played_sounds = RegisterConVar("ns_print_played_sounds", "0", FCVAR_NONE, "");
+ Cvar_ns_print_played_sounds = new ConVar("ns_print_played_sounds", "0", FCVAR_NONE, "");
if (IsDedicated())
return;
diff --git a/NorthstarDedicatedTest/bits.cpp b/NorthstarDedicatedTest/bits.cpp
new file mode 100644
index 00000000..45375b18
--- /dev/null
+++ b/NorthstarDedicatedTest/bits.cpp
@@ -0,0 +1,28 @@
+//=============================================================================//
+//
+// Purpose: look for NANs, infinities, and underflows.
+//
+//=============================================================================//
+
+#include "pch.h"
+#include "bits.h"
+
+//-----------------------------------------------------------------------------
+// This follows the ANSI/IEEE 754-1985 standard
+//-----------------------------------------------------------------------------
+unsigned long& FloatBits(float& f) { return *reinterpret_cast<unsigned long*>(&f); }
+
+unsigned long const& FloatBits(float const& f) { return *reinterpret_cast<unsigned long const*>(&f); }
+
+float BitsToFloat(unsigned long i) { return *reinterpret_cast<float*>(&i); }
+
+bool IsFinite(float f) { return ((FloatBits(f) & 0x7F800000) != 0x7F800000); }
+
+unsigned long FloatAbsBits(float f) { return FloatBits(f) & 0x7FFFFFFF; }
+
+float FloatMakePositive(float f) { return fabsf(f); }
+
+float FloatNegate(float f)
+{
+ return -f; // BitsToFloat( FloatBits(f) ^ 0x80000000 );
+}
diff --git a/NorthstarDedicatedTest/bits.h b/NorthstarDedicatedTest/bits.h
new file mode 100644
index 00000000..0532a9bd
--- /dev/null
+++ b/NorthstarDedicatedTest/bits.h
@@ -0,0 +1,10 @@
+#pragma once
+
+unsigned long& FloatBits(float& f);
+unsigned long const& FloatBits(float const& f);
+float BitsToFloat(unsigned long i);
+bool IsFinite(float f);
+unsigned long FloatAbsBits(float f);
+
+#define FLOAT32_NAN_BITS (std::uint32_t)0x7FC00000 // NaN!
+#define FLOAT32_NAN BitsToFloat(FLOAT32_NAN_BITS)
diff --git a/NorthstarDedicatedTest/buildainfile.cpp b/NorthstarDedicatedTest/buildainfile.cpp
index 1d37c0a9..3adb8324 100644
--- a/NorthstarDedicatedTest/buildainfile.cpp
+++ b/NorthstarDedicatedTest/buildainfile.cpp
@@ -231,7 +231,7 @@ void DumpAINInfo(CAI_Network* aiNetwork)
spdlog::info("calculated total linkcount: {}", calculatedLinkcount);
calculatedLinkcount /= 2;
- if (Cvar_ns_ai_dumpAINfileFromLoad->m_nValue)
+ if (Cvar_ns_ai_dumpAINfileFromLoad->GetBool())
{
if (aiNetwork->linkcount == calculatedLinkcount)
spdlog::info("caculated linkcount is normal!");
@@ -365,7 +365,7 @@ void LoadAINFileHook(void* aimanager, void* buf, const char* filename)
{
LoadAINFile(aimanager, buf, filename);
- if (Cvar_ns_ai_dumpAINfileFromLoad->m_nValue)
+ if (Cvar_ns_ai_dumpAINfileFromLoad->GetBool())
{
spdlog::info("running DumpAINInfo for loaded file {}", filename);
DumpAINInfo(*(CAI_Network**)((char*)aimanager + 2536));
@@ -374,7 +374,7 @@ void LoadAINFileHook(void* aimanager, void* buf, const char* filename)
void InitialiseBuildAINFileHooks(HMODULE baseAddress)
{
- Cvar_ns_ai_dumpAINfileFromLoad = RegisterConVar(
+ Cvar_ns_ai_dumpAINfileFromLoad = new ConVar(
"ns_ai_dumpAINfileFromLoad", "0", FCVAR_NONE, "For debugging: whether we should dump ain data for ains loaded from disk");
HookEnabler hook;
diff --git a/NorthstarDedicatedTest/chatcommand.cpp b/NorthstarDedicatedTest/chatcommand.cpp
index cd6a998c..ac41014a 100644
--- a/NorthstarDedicatedTest/chatcommand.cpp
+++ b/NorthstarDedicatedTest/chatcommand.cpp
@@ -1,6 +1,7 @@
#include "pch.h"
-#include "chatcommand.h"
+#include "convar.h"
#include "concommand.h"
+#include "chatcommand.h"
#include "dedicated.h"
#include "localchatwriter.h"
diff --git a/NorthstarDedicatedTest/clientauthhooks.cpp b/NorthstarDedicatedTest/clientauthhooks.cpp
index 41f0fd32..3b98e84a 100644
--- a/NorthstarDedicatedTest/clientauthhooks.cpp
+++ b/NorthstarDedicatedTest/clientauthhooks.cpp
@@ -20,10 +20,10 @@ void AuthWithStryderHook(void* a1)
{
// game will call this forever, until it gets a valid auth key
// so, we need to manually invalidate our key until we're authed with northstar, then we'll allow game to auth with stryder
- if (!g_MasterServerManager->m_bOriginAuthWithMasterServerDone && Cvar_ns_has_agreed_to_send_token->m_nValue != DISAGREED_TO_SEND_TOKEN)
+ if (!g_MasterServerManager->m_bOriginAuthWithMasterServerDone && Cvar_ns_has_agreed_to_send_token->GetInt() != DISAGREED_TO_SEND_TOKEN)
{
// if player has agreed to send token and we aren't already authing, try to auth
- if (Cvar_ns_has_agreed_to_send_token->m_nValue == AGREED_TO_SEND_TOKEN &&
+ if (Cvar_ns_has_agreed_to_send_token->GetInt() == AGREED_TO_SEND_TOKEN &&
!g_MasterServerManager->m_bOriginAuthWithMasterServerInProgress)
g_MasterServerManager->AuthenticateOriginWithMasterServer(g_LocalPlayerUserID, g_LocalPlayerOriginToken);
@@ -40,7 +40,7 @@ void InitialiseClientAuthHooks(HMODULE baseAddress)
return;
// this cvar will save to cfg once initially agreed with
- Cvar_ns_has_agreed_to_send_token = RegisterConVar(
+ Cvar_ns_has_agreed_to_send_token = new ConVar(
"ns_has_agreed_to_send_token", "0", FCVAR_ARCHIVE_PLAYERPROFILE,
"whether the user has agreed to send their origin token to the northstar masterserver");
diff --git a/NorthstarDedicatedTest/color.h b/NorthstarDedicatedTest/color.h
new file mode 100644
index 00000000..b2d02d1e
--- /dev/null
+++ b/NorthstarDedicatedTest/color.h
@@ -0,0 +1,90 @@
+#pragma once
+
+struct color24
+{
+ uint8_t r, g, b;
+};
+
+typedef struct color32_s
+{
+ bool operator!=(const struct color32_s& other) const { return r != other.r || g != other.g || b != other.b || a != other.a; }
+ inline unsigned* asInt(void) { return reinterpret_cast<unsigned*>(this); }
+ inline const unsigned* asInt(void) const { return reinterpret_cast<const unsigned*>(this); }
+ inline void Copy(const color32_s& rhs) { *asInt() = *rhs.asInt(); }
+
+ uint8_t r, g, b, a;
+} color32;
+
+//-----------------------------------------------------------------------------
+// Purpose: Basic handler for an rgb set of colors
+// This class is fully inline
+//-----------------------------------------------------------------------------
+class Color
+{
+ public:
+ Color(int r, int g, int b, int a)
+ {
+ _color[0] = (unsigned char)r;
+ _color[1] = (unsigned char)g;
+ _color[2] = (unsigned char)b;
+ _color[3] = (unsigned char)a;
+ }
+ void SetColor(int _r, int _g, int _b, int _a = 0)
+ {
+ _color[0] = (unsigned char)_r;
+ _color[1] = (unsigned char)_g;
+ _color[2] = (unsigned char)_b;
+ _color[3] = (unsigned char)_a;
+ }
+ void GetColor(int& _r, int& _g, int& _b, int& _a) const
+ {
+ _r = _color[0];
+ _g = _color[1];
+ _b = _color[2];
+ _a = _color[3];
+ }
+ int GetValue(int index) const { return _color[index]; }
+ void SetRawColor(int color32) { *((int*)this) = color32; }
+ int GetRawColor(void) const { return *((int*)this); }
+
+ inline int r() const { return _color[0]; }
+ inline int g() const { return _color[1]; }
+ inline int b() const { return _color[2]; }
+ inline int a() const { return _color[3]; }
+
+ unsigned char& operator[](int index) { return _color[index]; }
+
+ const unsigned char& operator[](int index) const { return _color[index]; }
+
+ bool operator==(const Color& rhs) const { return (*((int*)this) == *((int*)&rhs)); }
+
+ bool operator!=(const Color& rhs) const { return !(operator==(rhs)); }
+
+ Color& operator=(const Color& rhs)
+ {
+ SetRawColor(rhs.GetRawColor());
+ return *this;
+ }
+
+ Color& operator=(const color32& rhs)
+ {
+ _color[0] = rhs.r;
+ _color[1] = rhs.g;
+ _color[2] = rhs.b;
+ _color[3] = rhs.a;
+ return *this;
+ }
+
+ color32 ToColor32(void) const
+ {
+ color32 newColor{};
+ newColor.r = _color[0];
+ newColor.g = _color[1];
+ newColor.b = _color[2];
+ newColor.a = _color[3];
+ return newColor;
+ }
+
+ private:
+ unsigned char _color[4];
+};
diff --git a/NorthstarDedicatedTest/concommand.cpp b/NorthstarDedicatedTest/concommand.cpp
index b876a05c..b455100f 100644
--- a/NorthstarDedicatedTest/concommand.cpp
+++ b/NorthstarDedicatedTest/concommand.cpp
@@ -17,17 +17,97 @@ void RegisterConCommand(const char* name, void (*callback)(const CCommand&), con
conCommandConstructor(newCommand, name, callback, helpString, flags, nullptr);
}
-ConCommand* FindConCommand(const char* name)
-{
- ICvar* icvar =
- *g_pCvar; // hellish call because i couldn't get icvar vtable stuff in convar.h to get the right offset for whatever reason
- typedef ConCommand* (*FindCommandBaseType)(ICvar * self, const char* varName);
- FindCommandBaseType FindCommandBase = *(FindCommandBaseType*)((*(char**)icvar) + 112);
- return FindCommandBase(icvar, name);
-}
-
void InitialiseConCommands(HMODULE baseAddress)
{
conCommandConstructor = (ConCommandConstructorType)((char*)baseAddress + 0x415F60);
AddMiscConCommands();
-} \ No newline at end of file
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if this is a command
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConCommand::IsCommand(void) const { return true; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if this is a command
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConCommandBase::IsCommand(void) const { return true; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Has this cvar been registered
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool ConCommandBase::IsRegistered(void) const { return m_bRegistered; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Test each ConCommand query before execution.
+// Input : *pCommandBase - nFlags
+// Output : False if execution is permitted, true if not.
+//-----------------------------------------------------------------------------
+bool ConCommandBase::IsFlagSet(int nFlags) const
+{
+ return false; // !TODO: Returning false on every query? (original implementation in Northstar before ConCommandBase refactor)
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if ConCommand has requested flags.
+// Input : nFlags -
+// Output : True if ConCommand has nFlags.
+//-----------------------------------------------------------------------------
+bool ConCommandBase::HasFlags(int nFlags) { return m_nFlags & nFlags; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Add's flags to ConCommand.
+// Input : nFlags -
+//-----------------------------------------------------------------------------
+void ConCommandBase::AddFlags(int nFlags) { m_nFlags |= nFlags; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes flags from ConCommand.
+// Input : nFlags -
+//-----------------------------------------------------------------------------
+void ConCommandBase::RemoveFlags(int nFlags) { m_nFlags &= ~nFlags; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns current flags.
+// Output : int
+//-----------------------------------------------------------------------------
+int ConCommandBase::GetFlags(void) const { return m_nFlags; }
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : const ConCommandBase
+//-----------------------------------------------------------------------------
+ConCommandBase* ConCommandBase::GetNext(void) const { return m_pNext; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the ConCommandBase help text.
+// Output : const char*
+//-----------------------------------------------------------------------------
+const char* ConCommandBase::GetHelpText(void) const { return m_pszHelpString; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Copies string using local new/delete operators
+// Input : *szFrom -
+// Output : char
+//-----------------------------------------------------------------------------
+char* ConCommandBase::CopyString(const char* szFrom) const
+{
+ size_t nLen;
+ char* szTo;
+
+ nLen = strlen(szFrom);
+ if (nLen <= 0)
+ {
+ szTo = new char[1];
+ szTo[0] = 0;
+ }
+ else
+ {
+ szTo = new char[nLen + 1];
+ memmove(szTo, szFrom, nLen + 1);
+ }
+ return szTo;
+}
diff --git a/NorthstarDedicatedTest/concommand.h b/NorthstarDedicatedTest/concommand.h
index c7458794..b1342163 100644
--- a/NorthstarDedicatedTest/concommand.h
+++ b/NorthstarDedicatedTest/concommand.h
@@ -1,26 +1,15 @@
#pragma once
-#include "convar.h"
-// taken from ttf2sdk
-class ConCommand
+// From Source SDK
+class ConCommandBase;
+class IConCommandBaseAccessor
{
- unsigned char unknown[0x68];
-
public:
- virtual void EngineDestructor(void) {}
- virtual bool IsCommand(void) const { return false; }
- virtual bool IsFlagSet(int flag) { return false; }
- virtual void AddFlags(int flags) {}
- virtual void RemoveFlags(int flags) {}
- virtual int GetFlags() const { return 0; }
- virtual const char* GetName(void) const { return nullptr; }
- virtual const char* GetHelpText(void) const { return nullptr; }
- virtual bool IsRegistered(void) const { return false; }
- // NOTE: there are more virtual methods here
- // NOTE: Not using the engine's destructor here because it doesn't do anything useful for us
+ // Flags is a combination of FCVAR flags in cvar.h.
+ // hOut is filled in with a handle to the variable.
+ virtual bool RegisterConCommandBase(ConCommandBase* pVar) = 0;
};
-// From Source SDK
class CCommand
{
public:
@@ -50,15 +39,10 @@ class CCommand
};
inline int CCommand::MaxCommandLength() { return COMMAND_MAX_LENGTH - 1; }
-
inline int64_t CCommand::ArgC() const { return m_nArgc; }
-
inline const char** CCommand::ArgV() const { return m_nArgc ? (const char**)m_ppArgv : NULL; }
-
inline const char* CCommand::ArgS() const { return m_nArgv0Size ? &m_pArgSBuffer[m_nArgv0Size] : ""; }
-
inline const char* CCommand::GetCommandString() const { return m_nArgc ? m_pArgSBuffer : ""; }
-
inline const char* CCommand::Arg(int nIndex) const
{
// FIXME: Many command handlers appear to not be particularly careful
@@ -68,9 +52,56 @@ inline const char* CCommand::Arg(int nIndex) const
return "";
return m_ppArgv[nIndex];
}
-
inline const char* CCommand::operator[](int nIndex) const { return Arg(nIndex); }
+// From r5reloaded
+class ConCommandBase
+{
+ public:
+ bool HasFlags(int nFlags);
+ void AddFlags(int nFlags);
+ void RemoveFlags(int nFlags);
+
+ bool IsCommand(void) const;
+ bool IsRegistered(void) const;
+ bool IsFlagSet(int nFlags) const;
+ static bool IsFlagSet(ConCommandBase* pCommandBase, int nFlags); // For hooking to engine's implementation.
+
+ int GetFlags(void) const;
+ ConCommandBase* GetNext(void) const;
+ const char* GetHelpText(void) const;
+
+ char* CopyString(const char* szFrom) const;
+
+ void* m_pConCommandBaseVTable; // 0x0000
+ ConCommandBase* m_pNext; // 0x0008
+ bool m_bRegistered; // 0x0010
+ char pad_0011[7]; // 0x0011 <- 3 bytes padding + unk int32.
+ const char* m_pszName; // 0x0018
+ const char* m_pszHelpString; // 0x0020
+ int m_nFlags; // 0x0028
+ ConCommandBase* s_pConCommandBases; // 0x002C
+ IConCommandBaseAccessor* s_pAccessor; // 0x0034
+}; // Size: 0x0040
+
+// taken from ttf2sdk
+class ConCommand : public ConCommandBase
+{
+ friend class CCVar;
+
+ public:
+ ConCommand(void){}; // !TODO: Rebuild engine constructor in SDK instead.
+ ConCommand(const char* szName, const char* szHelpString, int nFlags, void* pCallback, void* pCommandCompletionCallback);
+ void Init(void);
+ bool IsCommand(void) const;
+
+ void* m_pCommandCallback{}; // 0x0040 <- starts from 0x40 since we inherit ConCommandBase.
+ void* m_pCompletionCallback{}; // 0x0048 <- defaults to sub_180417410 ('xor eax, eax').
+ int m_nCallbackFlags{}; // 0x0050
+ char pad_0054[4]; // 0x0054
+ int unk0; // 0x0058
+ int unk1; // 0x005C
+}; // Size: 0x0060
+
void RegisterConCommand(const char* name, void (*callback)(const CCommand&), const char* helpString, int flags);
-ConCommand* FindConCommand(const char* name);
void InitialiseConCommands(HMODULE baseAddress); \ No newline at end of file
diff --git a/NorthstarDedicatedTest/convar.cpp b/NorthstarDedicatedTest/convar.cpp
index 4157b42f..0f4eeba7 100644
--- a/NorthstarDedicatedTest/convar.cpp
+++ b/NorthstarDedicatedTest/convar.cpp
@@ -1,56 +1,478 @@
#include "pch.h"
+#include "bits.h"
+#include "cvar.h"
#include "convar.h"
#include "hookutils.h"
#include "gameutils.h"
#include "sourceinterface.h"
-#include <set>
// should this be in modmanager?
std::unordered_map<std::string, ConVar*>
g_CustomConvars; // this is used in modloading code to determine whether we've registered a mod convar already
-SourceInterface<ICvar>* g_pCvar;
-typedef void (*ConVarConstructorType)(ConVar* newVar, const char* name, const char* defaultValue, int flags, const char* helpString);
-ConVarConstructorType conVarConstructor;
+typedef void (*ConVarRegisterType)(
+ ConVar* pConVar, const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, bool bMin, float fMin,
+ bool bMax, float fMax, void* pCallback);
+ConVarRegisterType conVarRegister;
+
+typedef void (*ConVarMallocType)(void* pConVarMaloc, int a2, int a3);
+ConVarMallocType conVarMalloc;
+
+void* g_pConVar_Vtable = nullptr;
+void* g_pIConVar_Vtable = nullptr;
+
typedef bool (*CvarIsFlagSetType)(ConVar* self, int flags);
CvarIsFlagSetType CvarIsFlagSet;
-ConVar* RegisterConVar(const char* name, const char* defaultValue, int flags, const char* helpString)
+//-----------------------------------------------------------------------------
+// Purpose: ConVar interface initialization
+//-----------------------------------------------------------------------------
+void InitialiseConVars(HMODULE baseAddress)
+{
+ conVarMalloc = (ConVarMallocType)((char*)baseAddress + 0x415C20);
+ conVarRegister = (ConVarRegisterType)((char*)baseAddress + 0x417230);
+
+ g_pConVar_Vtable = (char*)baseAddress + 0x67FD28;
+ g_pIConVar_Vtable = (char*)baseAddress + 0x67FDC8;
+
+ g_pCVarInterface = new SourceInterface<CCvar>("vstdlib.dll", "VEngineCvar007");
+ g_pCVar = *g_pCVarInterface;
+
+ HookEnabler hook;
+ ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417FA0, &ConVar::IsFlagSet, reinterpret_cast<LPVOID*>(&CvarIsFlagSet));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+//-----------------------------------------------------------------------------
+ConVar::ConVar(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString)
+{
+ spdlog::info("Registering Convar {}", pszName);
+
+ this->m_ConCommandBase.m_pConCommandBaseVTable = g_pConVar_Vtable;
+ this->m_ConCommandBase.s_pConCommandBases = (ConCommandBase*)g_pIConVar_Vtable;
+
+ conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar.
+ conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, 0, 0, 0, 0, 0);
+
+ g_CustomConvars.emplace(pszName, this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+//-----------------------------------------------------------------------------
+ConVar::ConVar(
+ const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, bool bMin, float fMin, bool bMax, float fMax,
+ void* pCallback)
+{
+ spdlog::info("Registering Convar {}", pszName);
+
+ this->m_ConCommandBase.m_pConCommandBaseVTable = g_pConVar_Vtable;
+ this->m_ConCommandBase.s_pConCommandBases = (ConCommandBase*)g_pIConVar_Vtable;
+
+ conVarMalloc(&this->m_pMalloc, 0, 0); // Allocate new memory for ConVar.
+ conVarRegister(this, pszName, pszDefaultValue, nFlags, pszHelpString, bMin, fMin, bMax, fMax, pCallback);
+
+ g_CustomConvars.emplace(pszName, this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: destructor
+//-----------------------------------------------------------------------------
+ConVar::~ConVar(void)
+{
+ if (m_Value.m_pszString)
+ {
+ delete[] m_Value.m_pszString;
+ m_Value.m_pszString = NULL;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the base ConVar name.
+// Output : const char*
+//-----------------------------------------------------------------------------
+const char* ConVar::GetBaseName(void) const { return m_ConCommandBase.m_pszName; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the ConVar help text.
+// Output : const char*
+//-----------------------------------------------------------------------------
+const char* ConVar::GetHelpText(void) const { return m_ConCommandBase.m_pszHelpString; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Add's flags to ConVar.
+// Input : nFlags -
+//-----------------------------------------------------------------------------
+void ConVar::AddFlags(int nFlags) { m_ConCommandBase.m_nFlags |= nFlags; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes flags from ConVar.
+// Input : nFlags -
+//-----------------------------------------------------------------------------
+void ConVar::RemoveFlags(int nFlags) { m_ConCommandBase.m_nFlags &= ~nFlags; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as a boolean.
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::GetBool(void) const { return !!GetInt(); }
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as a float.
+// Output : float
+//-----------------------------------------------------------------------------
+float ConVar::GetFloat(void) const { return m_Value.m_fValue; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as an integer.
+// Output : int
+//-----------------------------------------------------------------------------
+int ConVar::GetInt(void) const { return m_Value.m_nValue; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as a color.
+// Output : Color
+//-----------------------------------------------------------------------------
+Color ConVar::GetColor(void) const
+{
+ unsigned char* pColorElement = ((unsigned char*)&m_Value.m_nValue);
+ return Color(pColorElement[0], pColorElement[1], pColorElement[2], pColorElement[3]);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return ConVar value as a string.
+// Output : const char *
+//-----------------------------------------------------------------------------
+const char* ConVar::GetString(void) const
+{
+ if (m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING)
+ {
+ return "FCVAR_NEVER_AS_STRING";
+ }
+
+ char const* str = m_Value.m_pszString;
+ return str ? str : "";
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flMinVal -
+// Output : true if there is a min set.
+//-----------------------------------------------------------------------------
+bool ConVar::GetMin(float& flMinVal) const
+{
+ flMinVal = m_fMinVal;
+ return m_bHasMin;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : flMaxVal -
+// Output : true if there is a max set.
+//-----------------------------------------------------------------------------
+bool ConVar::GetMax(float& flMaxVal) const
+{
+ flMaxVal = m_fMaxVal;
+ return m_bHasMax;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the min value.
+// Output : float
+//-----------------------------------------------------------------------------
+float ConVar::GetMinValue(void) const { return m_fMinVal; }
+
+//-----------------------------------------------------------------------------
+// Purpose: returns the max value.
+// Output : float
+//-----------------------------------------------------------------------------
+float ConVar::GetMaxValue(void) const
+{
+ return m_fMaxVal;
+ ;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: checks if ConVar has min value.
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::HasMin(void) const { return m_bHasMin; }
+
+//-----------------------------------------------------------------------------
+// Purpose: checks if ConVar has max value.
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::HasMax(void) const { return m_bHasMax; }
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar int value.
+// Input : nValue -
+//-----------------------------------------------------------------------------
+void ConVar::SetValue(int nValue)
{
- spdlog::info("Registering Convar {}", name);
+ if (nValue == m_Value.m_nValue)
+ {
+ return;
+ }
+
+ // Only valid for root ConVars.
+ assert(m_pParent == this);
- // no need to free this ever really, it should exist as long as game does
- ConVar* newVar = new ConVar;
- conVarConstructor(newVar, name, defaultValue, flags, helpString);
+ float flValue = (float)nValue;
- g_CustomConvars.emplace(name, newVar);
+ // Check bounds.
+ if (ClampValue(flValue))
+ {
+ nValue = (int)(flValue);
+ }
- return newVar;
+ // Redetermine value.
+ float flOldValue = m_Value.m_fValue;
+ m_Value.m_fValue = flValue;
+ m_Value.m_nValue = nValue;
+
+ if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
+ {
+ char szTempValue[32];
+ snprintf(szTempValue, sizeof(szTempValue), "%d", m_Value.m_nValue);
+ ChangeStringValue(szTempValue, flOldValue);
+ }
}
-ConVar* FindConVar(const char* name)
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar float value.
+// Input : flValue -
+//-----------------------------------------------------------------------------
+void ConVar::SetValue(float flValue)
{
- ICvar* icvar =
- *g_pCvar; // hellish call because i couldn't get icvar vtable stuff in convar.h to get the right offset for whatever reason
- typedef ConVar* (*FindConVarType)(ICvar * self, const char* varName);
- FindConVarType FindConVarInternal = *(FindConVarType*)((*(char**)icvar) + 128);
- return FindConVarInternal(icvar, name);
+ if (flValue == m_Value.m_fValue)
+ {
+ return;
+ }
+
+ // Only valid for root ConVars.
+ assert(m_pParent == this);
+
+ // Check bounds.
+ ClampValue(flValue);
+
+ // Redetermine value.
+ float flOldValue = m_Value.m_fValue;
+ m_Value.m_fValue = flValue;
+ m_Value.m_nValue = (int)m_Value.m_fValue;
+
+ if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
+ {
+ char szTempValue[32];
+ snprintf(szTempValue, sizeof(szTempValue), "%f", m_Value.m_fValue);
+ ChangeStringValue(szTempValue, flOldValue);
+ }
}
-bool CvarIsFlagSetHook(ConVar* self, int flags)
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar string value.
+// Input : *szValue -
+//-----------------------------------------------------------------------------
+void ConVar::SetValue(const char* pszValue)
+{
+ if (strcmp(this->m_Value.m_pszString, pszValue) == 0)
+ {
+ return;
+ }
+ this->m_Value.m_pszString = pszValue;
+
+ char szTempValue[32]{};
+ const char* pszNewValue{};
+
+ // Only valid for root convars.
+ assert(m_pParent == this);
+
+ float flOldValue = m_Value.m_fValue;
+ pszNewValue = (char*)pszValue;
+ if (!pszNewValue)
+ {
+ pszNewValue = "";
+ }
+
+ if (!SetColorFromString(pszValue))
+ {
+ // Not a color, do the standard thing
+ float flNewValue = (float)atof(pszValue);
+ if (!IsFinite(flNewValue))
+ {
+ spdlog::warn("Warning: ConVar '{}' = '{}' is infinite, clamping value.\n", GetBaseName(), pszValue);
+ flNewValue = FLT_MAX;
+ }
+
+ if (ClampValue(flNewValue))
+ {
+ snprintf(szTempValue, sizeof(szTempValue), "%f", flNewValue);
+ pszNewValue = szTempValue;
+ }
+
+ // Redetermine value
+ m_Value.m_fValue = flNewValue;
+ m_Value.m_nValue = (int)(m_Value.m_fValue);
+ }
+
+ if (!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING))
+ {
+ ChangeStringValue(pszNewValue, flOldValue);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar color value.
+// Input : clValue -
+//-----------------------------------------------------------------------------
+void ConVar::SetValue(Color clValue)
+{
+ std::string svResult = "";
+
+ for (int i = 0; i < 4; i++)
+ {
+ if (!(clValue.GetValue(i) == 0 && svResult.size() == 0))
+ {
+ svResult += std::to_string(clValue.GetValue(i));
+ svResult.append(" ");
+ }
+ }
+
+ this->m_Value.m_pszString = svResult.c_str();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: changes the ConVar string value.
+// Input : *pszTempVal - flOldValue
+//-----------------------------------------------------------------------------
+void ConVar::ChangeStringValue(const char* pszTempVal, float flOldValue)
+{
+ assert(!(m_ConCommandBase.m_nFlags & FCVAR_NEVER_AS_STRING));
+
+ char* pszOldValue = (char*)_malloca(m_Value.m_iStringLength);
+ if (pszOldValue != NULL)
+ {
+ memcpy(pszOldValue, m_Value.m_pszString, m_Value.m_iStringLength);
+ }
+
+ if (pszTempVal)
+ {
+ int len = strlen(pszTempVal) + 1;
+
+ if (len > m_Value.m_iStringLength)
+ {
+ if (m_Value.m_pszString)
+ {
+ // !TODO: Causes issues in tier0.dll, but doesn't in apex.
+ // Not a big issue since we are creating a new string below
+ // anyways to prevent buffer overflow if string is longer
+ // then the old string.
+ // delete[] m_Value.m_pszString;
+ }
+
+ m_Value.m_pszString = new char[len];
+ m_Value.m_iStringLength = len;
+ }
+
+ memcpy((char*)m_Value.m_pszString, pszTempVal, len);
+ }
+ else
+ {
+ m_Value.m_pszString = NULL;
+ }
+
+ pszOldValue = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: sets the ConVar color value from string.
+// Input : *pszValue -
+//-----------------------------------------------------------------------------
+bool ConVar::SetColorFromString(const char* pszValue)
+{
+ bool bColor = false;
+
+ // Try pulling RGBA color values out of the string.
+ int nRGBA[4]{};
+ int nParamsRead = sscanf_s(pszValue, "%i %i %i %i", &(nRGBA[0]), &(nRGBA[1]), &(nRGBA[2]), &(nRGBA[3]));
+
+ if (nParamsRead >= 3)
+ {
+ // This is probably a color!
+ if (nParamsRead == 3)
+ {
+ // Assume they wanted full alpha.
+ nRGBA[3] = 255;
+ }
+
+ if (nRGBA[0] >= 0 && nRGBA[0] <= 255 && nRGBA[1] >= 0 && nRGBA[1] <= 255 && nRGBA[2] >= 0 && nRGBA[2] <= 255 && nRGBA[3] >= 0 &&
+ nRGBA[3] <= 255)
+ {
+ // printf("*** WOW! Found a color!! ***\n");
+
+ // This is definitely a color!
+ bColor = true;
+
+ // Stuff all the values into each byte of our int.
+ unsigned char* pColorElement = ((unsigned char*)&m_Value.m_nValue);
+ pColorElement[0] = nRGBA[0];
+ pColorElement[1] = nRGBA[1];
+ pColorElement[2] = nRGBA[2];
+ pColorElement[3] = nRGBA[3];
+
+ // Copy that value into our float.
+ m_Value.m_fValue = (float)(m_Value.m_nValue);
+ }
+ }
+
+ return bColor;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Checks if ConVar is registered.
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::IsRegistered(void) const { return m_ConCommandBase.m_bRegistered; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns true if this is a command
+// Output : bool
+//-----------------------------------------------------------------------------
+bool ConVar::IsCommand(void) const { return false; }
+
+//-----------------------------------------------------------------------------
+// Purpose: Test each ConVar query before setting the value.
+// Input : *pConVar - nFlags
+// Output : False if change is permitted, true if not.
+//-----------------------------------------------------------------------------
+bool ConVar::IsFlagSet(ConVar* pConVar, int nFlags)
{
// unrestrict FCVAR_DEVELOPMENTONLY and FCVAR_HIDDEN
- if (self && (flags == FCVAR_DEVELOPMENTONLY || flags == FCVAR_HIDDEN))
+ if (pConVar && (nFlags == FCVAR_DEVELOPMENTONLY || nFlags == FCVAR_HIDDEN))
return false;
- return CvarIsFlagSet(self, flags);
+ return CvarIsFlagSet(pConVar, nFlags);
}
-void InitialiseConVars(HMODULE baseAddress)
+//-----------------------------------------------------------------------------
+// Purpose: Check whether to clamp and then perform clamp.
+// Input : flValue -
+// Output : Returns true if value changed.
+//-----------------------------------------------------------------------------
+bool ConVar::ClampValue(float& flValue)
{
- conVarConstructor = (ConVarConstructorType)((char*)baseAddress + 0x416200);
- g_pCvar = new SourceInterface<ICvar>("vstdlib.dll", "VEngineCvar007");
+ if (m_bHasMin && (flValue < m_fMinVal))
+ {
+ flValue = m_fMinVal;
+ return true;
+ }
- HookEnabler hook;
- ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x417FA0, &CvarIsFlagSetHook, reinterpret_cast<LPVOID*>(&CvarIsFlagSet));
-} \ No newline at end of file
+ if (m_bHasMax && (flValue > m_fMaxVal))
+ {
+ flValue = m_fMaxVal;
+ return true;
+ }
+
+ return false;
+}
diff --git a/NorthstarDedicatedTest/convar.h b/NorthstarDedicatedTest/convar.h
index 30ab702b..a00c7f0e 100644
--- a/NorthstarDedicatedTest/convar.h
+++ b/NorthstarDedicatedTest/convar.h
@@ -1,6 +1,9 @@
#pragma once
#include "sourceinterface.h"
-#include <set>
+#include "color.h"
+#include "cvar.h"
+#include "concommand.h"
+
// taken directly from iconvar.h
// The default, no flags at all
@@ -17,14 +20,13 @@
// ConVar only
#define FCVAR_PROTECTED \
(1 << 5) // It's a server cvar, but we don't send the data since it's a password, etc. Sends 1 if it's not bland/zero, 0 otherwise as
- // value
+ // value.
#define FCVAR_SPONLY (1 << 6) // This cvar cannot be changed by clients connected to a multiplayer server.
#define FCVAR_ARCHIVE (1 << 7) // set to cause it to be saved to vars.rc
#define FCVAR_NOTIFY (1 << 8) // notifies players when changed
#define FCVAR_USERINFO (1 << 9) // changes the client's info string
#define FCVAR_PRINTABLEONLY (1 << 10) // This cvar's string cannot contain unprintable characters ( e.g., used for player name etc ).
-
#define FCVAR_GAMEDLL_FOR_REMOTE_CLIENTS \
(1 << 10) // When on concommands this allows remote clients to execute this cmd on the server.
// We are changing the default behavior of concommands to disallow execution by remote clients without
@@ -34,8 +36,7 @@
#define FCVAR_NEVER_AS_STRING (1 << 12) // never try to print that cvar
// 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
-// client, of course )
+// At signon, the values of all such ConVars are sent from the server to the client (skipped for local client, of course )
// If a change is requested it must come from the console (i.e., no remote client changes)
// If a value is changed while a server is active, it's replicated to all connected clients
#define FCVAR_REPLICATED (1 << 13) // server setting enforced on clients, TODO rename to FCAR_SERVER at some time
@@ -66,52 +67,77 @@
// #define FCVAR_AVAILABLE (1<<27)
// #define FCVAR_AVAILABLE (1<<31)
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class ConCommandBase;
class ConCommand;
+class ConVar;
-// still need to map out functions and that for this, would be nice to be able to get actual values out of these in native
-// also i sure do hope this size is right because there's a fairly decent chance it isn't
+//-----------------------------------------------------------------------------
+// Purpose: A console variable
+//-----------------------------------------------------------------------------
class ConVar
{
public:
- // if there are ever crashes caused by modifying custom cvars, check this
- unsigned char unknown[0x40];
- char* m_pszString;
- size_t m_StringLength;
- float m_fValue;
- int32_t m_nValue;
- unsigned char unknown2[0x28];
-
- public:
- virtual void EngineDestructor(void) {}
- virtual bool IsCommand(void) const { return false; }
- virtual bool IsFlagSet(int flag) { return false; }
- virtual void AddFlags(int flags) {}
- virtual void RemoveFlags(int flags) {}
- virtual int GetFlags() const { return 0; }
- virtual const char* GetName(void) const { return nullptr; }
- virtual const char* GetHelpText(void) const { return nullptr; }
- virtual bool IsRegistered(void) const { return false; }
-};
-
-class ICvar
-{
- public:
- struct VTable
+ ConVar(void){};
+ ConVar(const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString);
+ ConVar(
+ const char* pszName, const char* pszDefaultValue, int nFlags, const char* pszHelpString, bool bMin, float fMin, bool bMax,
+ float fMax, void* pCallback);
+ ~ConVar(void);
+
+ const char* GetBaseName(void) const;
+ const char* GetHelpText(void) const;
+
+ void AddFlags(int nFlags);
+ void RemoveFlags(int nFlags);
+
+ bool GetBool(void) const;
+ float GetFloat(void) const;
+ int GetInt(void) const;
+ Color GetColor(void) const;
+ const char* GetString(void) const;
+
+ bool GetMin(float& flMinValue) const;
+ bool GetMax(float& flMaxValue) const;
+ float GetMinValue(void) const;
+ float GetMaxValue(void) const;
+
+ bool HasMin(void) const;
+ bool HasMax(void) const;
+
+ void SetValue(int nValue);
+ void SetValue(float flValue);
+ void SetValue(const char* pszValue);
+ void SetValue(Color clValue);
+
+ void ChangeStringValue(const char* pszTempValue, float flOldValue);
+ bool SetColorFromString(const char* pszValue);
+ bool ClampValue(float& value);
+
+ bool IsRegistered(void) const;
+ bool IsCommand(void) const;
+ static bool IsFlagSet(ConVar* pConVar, int nFlags);
+
+ struct CVValue_t
{
- // void* unknown[10];
- // void(*UnregisterConCommand) (ICvar* cvar, ConCommand* pCommandBase);
- // void* unknown2[28];
- // ConVar*(*FindVar)(const char* var_name); // offset for this is currently very wrong
- char* unknown[112];
- ConCommand* (*FindCommandBase)(ICvar* self, const char* varName); // this offset is also wrong for some reason
+ const char* m_pszString;
+ int64_t m_iStringLength;
+ float m_fValue;
+ int m_nValue;
};
- VTable* m_vtable;
-};
+ ConCommandBase m_ConCommandBase{}; // 0x0000
+ ConVar* m_pParent{}; // 0x0040
+ CVValue_t m_Value{}; // 0x0048
+ bool m_bHasMin{}; // 0x005C
+ float m_fMinVal{}; // 0x0060
+ bool m_bHasMax{}; // 0x0064
+ float m_fMaxVal{}; // 0x0068
+ void* m_pMalloc{}; // 0x0070
+ char m_pPad80[10]{}; // 0x0080
+}; // Size: 0x0080
-ConVar* RegisterConVar(const char* name, const char* defaultValue, int flags, const char* helpString);
-ConVar* FindConVar(const char* name);
void InitialiseConVars(HMODULE baseAddress);
-
-extern std::unordered_map<std::string, ConVar*> g_CustomConvars;
-extern SourceInterface<ICvar>* g_pCvar; \ No newline at end of file
+extern std::unordered_map<std::string, ConVar*> g_CustomConvars; \ No newline at end of file
diff --git a/NorthstarDedicatedTest/cvar.cpp b/NorthstarDedicatedTest/cvar.cpp
new file mode 100644
index 00000000..7f8c35d7
--- /dev/null
+++ b/NorthstarDedicatedTest/cvar.cpp
@@ -0,0 +1,65 @@
+#include "pch.h"
+#include "cvar.h"
+#include "convar.h"
+#include "concommand.h"
+
+//-----------------------------------------------------------------------------
+// Purpose: finds base commands.
+// Input : *pszCommandName -
+//-----------------------------------------------------------------------------
+ConCommandBase* CCvar::FindCommandBase(const char* pszCommandName)
+{
+ static int index = 14;
+ return CallVFunc<ConCommandBase*>(index, this, pszCommandName);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: finds ConVars.
+// Input : *pszVarName -
+//-----------------------------------------------------------------------------
+ConVar* CCvar::FindVar(const char* pszVarName)
+{
+ static int index = 16;
+ return CallVFunc<ConVar*>(index, this, pszVarName);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: finds ConCommands.
+// Input : *pszCommandName -
+//-----------------------------------------------------------------------------
+ConCommand* CCvar::FindCommand(const char* pszCommandName)
+{
+ static int index = 18;
+ return CallVFunc<ConCommand*>(index, this, pszCommandName);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: iterates over all ConVars
+//-----------------------------------------------------------------------------
+CCVarIteratorInternal* CCvar::FactoryInternalIterator()
+{
+ static int index = 41;
+ return CallVFunc<CCVarIteratorInternal*>(index, this);
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: returns all ConVars
+//-----------------------------------------------------------------------------
+std::unordered_map<std::string, ConCommandBase*> CCvar::DumpToMap()
+{
+ std::stringstream ss;
+ CCVarIteratorInternal* itint = FactoryInternalIterator(); // Allocate new InternalIterator.
+
+ std::unordered_map<std::string, ConCommandBase*> allConVars;
+
+ for (itint->SetFirst(); itint->IsValid(); itint->Next()) // Loop through all instances.
+ {
+ ConCommandBase* pCommand = itint->Get();
+ const char* pszCommandName = pCommand->m_pszName;
+ allConVars[pszCommandName] = pCommand;
+ }
+
+ return allConVars;
+}
+SourceInterface<CCvar>* g_pCVarInterface;
+CCvar* g_pCVar; \ No newline at end of file
diff --git a/NorthstarDedicatedTest/cvar.h b/NorthstarDedicatedTest/cvar.h
new file mode 100644
index 00000000..43b13bfb
--- /dev/null
+++ b/NorthstarDedicatedTest/cvar.h
@@ -0,0 +1,37 @@
+#pragma once
+#include "convar.h"
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+class ConCommandBase;
+class ConCommand;
+class ConVar;
+
+//-----------------------------------------------------------------------------
+// Internals for ICVarIterator
+//-----------------------------------------------------------------------------
+class CCVarIteratorInternal // Fully reversed table, just look at the virtual function table and rename the function.
+{
+ public:
+ virtual void SetFirst(void) = 0; // 0
+ virtual void Next(void) = 0; // 1
+ virtual bool IsValid(void) = 0; // 2
+ virtual ConCommandBase* Get(void) = 0; // 3
+};
+
+//-----------------------------------------------------------------------------
+// Default implementation
+//-----------------------------------------------------------------------------
+class CCvar
+{
+ public:
+ ConCommandBase* FindCommandBase(const char* pszCommandName);
+ ConVar* FindVar(const char* pszVarName);
+ ConCommand* FindCommand(const char* pszCommandName);
+ CCVarIteratorInternal* FactoryInternalIterator();
+ std::unordered_map<std::string, ConCommandBase*> DumpToMap();
+};
+
+extern SourceInterface<CCvar>* g_pCVarInterface;
+extern CCvar* g_pCVar; \ No newline at end of file
diff --git a/NorthstarDedicatedTest/dedicated.cpp b/NorthstarDedicatedTest/dedicated.cpp
index 39f683e6..35d45800 100644
--- a/NorthstarDedicatedTest/dedicated.cpp
+++ b/NorthstarDedicatedTest/dedicated.cpp
@@ -42,7 +42,7 @@ void RunServer(CDedicatedExports* dedicated)
// add +map if not present
// don't manually execute this from cbuf as users may have it in their startup args anyway, easier just to run from stuffcmds if present
if (!CommandLine()->CheckParm("+map"))
- CommandLine()->AppendParm("+map", Cvar_match_defaultMap->m_pszString);
+ CommandLine()->AppendParm("+map", Cvar_match_defaultMap->GetString());
// run server autoexec and re-run commandline
Cbuf_AddText(Cbuf_GetCurrentPlayer(), "exec autoexec_ns_server", cmd_source_t::kCommandSrcCode);
@@ -78,8 +78,8 @@ void RunServer(CDedicatedExports* dedicated)
.c_str());
}
- std::this_thread::sleep_for(
- std::chrono::duration<double, std::ratio<1>>(Cvar_base_tickinterval_mp->m_fValue - fmin(Plat_FloatTime() - frameStart, 0.25)));
+ std::this_thread::sleep_for(std::chrono::duration<double, std::ratio<1>>(
+ Cvar_base_tickinterval_mp->GetFloat() - fmin(Plat_FloatTime() - frameStart, 0.25)));
}
}
diff --git a/NorthstarDedicatedTest/gameutils.cpp b/NorthstarDedicatedTest/gameutils.cpp
index f9fd4d95..b8194397 100644
--- a/NorthstarDedicatedTest/gameutils.cpp
+++ b/NorthstarDedicatedTest/gameutils.cpp
@@ -60,9 +60,6 @@ void InitialiseEngineGameUtilFunctions(HMODULE baseAddress)
g_pEngine = *(CEngine**)((char*)baseAddress + 0x7D70C8);
sv_m_State = (server_state_t*)((char*)baseAddress + 0x12A53D48);
- Cvar_hostport = (ConVar*)((char*)baseAddress + 0x13FA6070);
- Cvar_net_datablock_enabled = (ConVar*)((char*)baseAddress + 0x12A4F6D0);
-
GetCurrentPlaylistName = (GetCurrentPlaylistType)((char*)baseAddress + 0x18C640);
SetCurrentPlaylist = (SetCurrentPlaylistType)((char*)baseAddress + 0x18EB20);
SetPlaylistVarOverride = (SetPlaylistVarOverrideType)((char*)baseAddress + 0x18ED00);
@@ -71,10 +68,26 @@ void InitialiseEngineGameUtilFunctions(HMODULE baseAddress)
g_LocalPlayerUserID = (char*)baseAddress + 0x13F8E688;
g_LocalPlayerOriginToken = (char*)baseAddress + 0x13979C80;
+ GetBaseLocalClient = (GetBaseLocalClientType)((char*)baseAddress + 0x78200);
+
+ /* NOTE:
+ g_pCVar->FindVar("convar_name") now works. These are no longer needed.
+ You can also itterate over every ConVar using CCVarIteratorInternal
+ dump the pointers to a vector and access them from there.
+ Example:
+ std::vector<ConVar*> g_pAllConVars;
+ for (auto& map : g_pCVar->DumpToMap())
+ {
+ ConVar* pConVar = g_pCVar->FindVar(map.first.c_str());
+ if (pConVar)
+ {
+ g_pAllConVars.push_back(pConVar);
+ }
+ }*/
+ Cvar_hostport = (ConVar*)((char*)baseAddress + 0x13FA6070);
+ Cvar_net_datablock_enabled = (ConVar*)((char*)baseAddress + 0x12A4F6D0);
Cvar_match_defaultMap = (ConVar*)((char*)baseAddress + 0x8AB530);
Cvar_communities_hostname = (ConVar*)((char*)baseAddress + 0x13157E50);
-
- GetBaseLocalClient = (GetBaseLocalClientType)((char*)baseAddress + 0x78200);
}
void InitialiseServerGameUtilFunctions(HMODULE baseAddress)
diff --git a/NorthstarDedicatedTest/latencyflex.cpp b/NorthstarDedicatedTest/latencyflex.cpp
index 4772b78a..a677b6d2 100644
--- a/NorthstarDedicatedTest/latencyflex.cpp
+++ b/NorthstarDedicatedTest/latencyflex.cpp
@@ -15,7 +15,7 @@ PFN_winelfx_WaitAndBeginFrame m_winelfx_WaitAndBeginFrame{};
void OnRenderStartHook()
{
- if (Cvar_r_latencyflex->m_nValue)
+ if (Cvar_r_latencyflex->GetInt())
m_winelfx_WaitAndBeginFrame();
OnRenderStart();
@@ -41,7 +41,7 @@ void InitialiseLatencyFleX(HMODULE baseAddress)
reinterpret_cast<PFN_winelfx_WaitAndBeginFrame>(reinterpret_cast<void*>(GetProcAddress(m_lfxModule, "winelfx_WaitAndBeginFrame")));
spdlog::info("LatencyFleX initialized.");
- Cvar_r_latencyflex = RegisterConVar("r_latencyflex", "1", FCVAR_ARCHIVE, "Whether or not to use LatencyFleX input latency reduction.");
+ Cvar_r_latencyflex = new ConVar("r_latencyflex", "1", FCVAR_ARCHIVE, "Whether or not to use LatencyFleX input latency reduction.");
HookEnabler hook;
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x1952C0, &OnRenderStartHook, reinterpret_cast<LPVOID*>(&OnRenderStart));
diff --git a/NorthstarDedicatedTest/logging.cpp b/NorthstarDedicatedTest/logging.cpp
index ec46bedc..4d31981e 100644
--- a/NorthstarDedicatedTest/logging.cpp
+++ b/NorthstarDedicatedTest/logging.cpp
@@ -230,7 +230,7 @@ EngineSpewFuncType EngineSpewFunc;
void EngineSpewFuncHook(void* engineServer, SpewType_t type, const char* format, va_list args)
{
- if (!Cvar_spewlog_enable->m_nValue)
+ if (!Cvar_spewlog_enable->GetBool())
return;
const char* typeStr;
@@ -390,8 +390,7 @@ void InitialiseEngineSpewFuncHooks(HMODULE baseAddress)
hook, (char*)baseAddress + 0x1A1530, CClientState_ProcessPrint_Hook,
reinterpret_cast<LPVOID*>(&CClientState_ProcessPrint_Original));
- Cvar_spewlog_enable =
- RegisterConVar("spewlog_enable", "1", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged");
+ Cvar_spewlog_enable = new ConVar("spewlog_enable", "1", FCVAR_NONE, "Enables/disables whether the engine spewfunc should be logged");
}
#include "bitbuf.h"
@@ -422,7 +421,7 @@ void TextMsgHook(BFRead* msg)
char text[256];
msg->ReadString(text, sizeof(text));
- if (!Cvar_cl_showtextmsg->m_nValue)
+ if (!Cvar_cl_showtextmsg->GetBool())
return;
switch (msg_dest)
@@ -451,5 +450,5 @@ void InitialiseClientPrintHooks(HMODULE baseAddress)
// "TextMsg" usermessage
ENABLER_CREATEHOOK(hook, (char*)baseAddress + 0x198710, TextMsgHook, reinterpret_cast<LPVOID*>(&TextMsg_Original));
- Cvar_cl_showtextmsg = RegisterConVar("cl_showtextmsg", "1", FCVAR_NONE, "Enable/disable text messages printing on the screen.");
+ Cvar_cl_showtextmsg = new ConVar("cl_showtextmsg", "1", FCVAR_NONE, "Enable/disable text messages printing on the screen.");
} \ No newline at end of file
diff --git a/NorthstarDedicatedTest/masterserver.cpp b/NorthstarDedicatedTest/masterserver.cpp
index cfb92409..b08ebb90 100644
--- a/NorthstarDedicatedTest/masterserver.cpp
+++ b/NorthstarDedicatedTest/masterserver.cpp
@@ -105,8 +105,8 @@ std::string unescape_unicode(const std::string& str)
void UpdateServerInfoFromUnicodeToUTF8()
{
- g_MasterServerManager->ns_auth_srvName = unescape_unicode(Cvar_ns_server_name->m_pszString);
- g_MasterServerManager->ns_auth_srvDesc = unescape_unicode(Cvar_ns_server_desc->m_pszString);
+ g_MasterServerManager->ns_auth_srvName = unescape_unicode(Cvar_ns_server_name->GetString());
+ g_MasterServerManager->ns_auth_srvDesc = unescape_unicode(Cvar_ns_server_desc->GetString());
}
const char* HttplibErrorToString(httplib::Error error)
@@ -214,7 +214,7 @@ void MasterServerManager::AuthenticateOriginWithMasterServer(char* uid, char* or
std::string readBuffer;
curl_easy_setopt(
curl, CURLOPT_URL,
- fmt::format("{}/client/origin_auth?id={}&token={}", Cvar_ns_masterserver_hostname->m_pszString, uidStr, tokenStr).c_str());
+ fmt::format("{}/client/origin_auth?id={}&token={}", Cvar_ns_masterserver_hostname->GetString(), uidStr, tokenStr).c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
@@ -283,13 +283,13 @@ void MasterServerManager::RequestServerList()
m_requestingServerList = true;
m_scriptRequestingServerList = true;
- spdlog::info("Requesting server list from {}", Cvar_ns_masterserver_hostname->m_pszString);
+ spdlog::info("Requesting server list from {}", Cvar_ns_masterserver_hostname->GetString());
CURL* curl = curl_easy_init();
SetCommonHttpClientOptions(curl);
std::string readBuffer;
- curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/servers", Cvar_ns_masterserver_hostname->m_pszString).c_str());
+ curl_easy_setopt(curl, CURLOPT_URL, fmt::format("{}/client/servers", Cvar_ns_masterserver_hostname->GetString()).c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
@@ -437,7 +437,7 @@ void MasterServerManager::RequestMainMenuPromos()
std::string readBuffer;
curl_easy_setopt(
- curl, CURLOPT_URL, fmt::format("{}/client/mainmenupromos", Cvar_ns_masterserver_hostname->m_pszString).c_str());
+ curl, CURLOPT_URL, fmt::format("{}/client/mainmenupromos", Cvar_ns_masterserver_hostname->GetString()).c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
@@ -555,7 +555,7 @@ void MasterServerManager::AuthenticateWithOwnServer(char* uid, char* playerToken
std::string readBuffer;
curl_easy_setopt(
curl, CURLOPT_URL,
- fmt::format("{}/client/auth_with_self?id={}&playerToken={}", Cvar_ns_masterserver_hostname->m_pszString, uidStr, tokenStr)
+ fmt::format("{}/client/auth_with_self?id={}&playerToken={}", Cvar_ns_masterserver_hostname->GetString(), uidStr, tokenStr)
.c_str());
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWriteToStringBufferCallback);
@@ -696,7 +696,7 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c
curl_easy_setopt(
curl, CURLOPT_URL,
fmt::format(
- "{}/client/auth_with_server?id={}&playerToken={}&server={}&password={}", Cvar_ns_masterserver_hostname->m_pszString,
+ "{}/client/auth_with_server?id={}&playerToken={}&server={}&password={}", Cvar_ns_masterserver_hostname->GetString(),
uidStr, tokenStr, serverIdStr, escapedPassword)
.c_str());
@@ -777,10 +777,10 @@ void MasterServerManager::AuthenticateWithServer(char* uid, char* playerToken, c
void MasterServerManager::AddSelfToServerList(
int port, int authPort, char* name, char* description, char* map, char* playlist, int maxPlayers, char* password)
{
- if (!Cvar_ns_report_server_to_masterserver->m_nValue)
+ if (!Cvar_ns_report_server_to_masterserver->GetBool())
return;
- if (!Cvar_ns_report_sp_server_to_masterserver->m_nValue && !strncmp(map, "sp_", 3))
+ if (!Cvar_ns_report_sp_server_to_masterserver->GetBool() && !strncmp(map, "sp_", 3))
{
m_bRequireClientAuth = false;
return;
@@ -830,7 +830,7 @@ void MasterServerManager::AddSelfToServerList(
curl, CURLOPT_URL,
fmt::format(
"{}/server/add_server?port={}&authPort={}&name={}&description={}&map={}&playlist={}&maxPlayers={}&password={}",
- Cvar_ns_masterserver_hostname->m_pszString, port, authPort, nameEscaped, descEscaped, mapEscaped, playlistEscaped,
+ Cvar_ns_masterserver_hostname->GetString(), port, authPort, nameEscaped, descEscaped, mapEscaped, playlistEscaped,
maxPlayers, passwordEscaped)
.c_str());
@@ -915,7 +915,7 @@ void MasterServerManager::AddSelfToServerList(
char* escapedDescNew = curl_easy_escape(curl, g_MasterServerManager->ns_auth_srvDesc.c_str(), NULL);
char* escapedMapNew = curl_easy_escape(curl, g_pHostState->m_levelName, NULL);
char* escapedPlaylistNew = curl_easy_escape(curl, GetCurrentPlaylistName(), NULL);
- char* escapedPasswordNew = curl_easy_escape(curl, Cvar_ns_server_password->m_pszString, NULL);
+ char* escapedPasswordNew = curl_easy_escape(curl, Cvar_ns_server_password->GetString(), NULL);
int maxPlayers = 6;
char* maxPlayersVar = GetCurrentPlaylistVar("max_players", false);
@@ -928,8 +928,8 @@ void MasterServerManager::AddSelfToServerList(
"{}/server/"
"update_values?id={}&port={}&authPort={}&name={}&description={}&map={}&playlist={}&playerCount={}&"
"maxPlayers={}&password={}",
- Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId, Cvar_hostport->m_nValue,
- Cvar_ns_player_auth_port->m_nValue, escapedNameNew, escapedDescNew, escapedMapNew,
+ Cvar_ns_masterserver_hostname->GetString(), m_ownServerId, Cvar_hostport->GetInt(),
+ Cvar_ns_player_auth_port->GetInt(), escapedNameNew, escapedDescNew, escapedMapNew,
escapedPlaylistNew, g_ServerAuthenticationManager->m_additionalPlayerData.size(), maxPlayers,
escapedPasswordNew)
.c_str());
@@ -1026,7 +1026,7 @@ void MasterServerManager::UpdateServerMapAndPlaylist(char* map, char* playlist,
curl_easy_setopt(
curl, CURLOPT_URL,
fmt::format(
- "{}/server/update_values?id={}&map={}&playlist={}&maxPlayers={}", Cvar_ns_masterserver_hostname->m_pszString,
+ "{}/server/update_values?id={}&map={}&playlist={}&maxPlayers={}", Cvar_ns_masterserver_hostname->GetString(),
m_ownServerId, mapEscaped, playlistEscaped, maxPlayers)
.c_str());
@@ -1066,7 +1066,7 @@ void MasterServerManager::UpdateServerPlayerCount(int playerCount)
curl_easy_setopt(
curl, CURLOPT_URL,
fmt::format(
- "{}/server/update_values?id={}&playerCount={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId, playerCount)
+ "{}/server/update_values?id={}&playerCount={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId, playerCount)
.c_str());
CURLcode result = curl_easy_perform(curl);
@@ -1105,7 +1105,7 @@ void MasterServerManager::WritePlayerPersistentData(char* playerId, char* pdata,
curl_easy_setopt(
curl, CURLOPT_URL,
fmt::format(
- "{}/accounts/write_persistence?id={}&serverId={}", Cvar_ns_masterserver_hostname->m_pszString, strPlayerId,
+ "{}/accounts/write_persistence?id={}&serverId={}", Cvar_ns_masterserver_hostname->GetString(), strPlayerId,
m_ownServerId)
.c_str());
curl_easy_setopt(curl, CURLOPT_POST, 1L);
@@ -1140,7 +1140,7 @@ void MasterServerManager::WritePlayerPersistentData(char* playerId, char* pdata,
void MasterServerManager::RemoveSelfFromServerList()
{
// dont call this if we don't have a server id
- if (!*m_ownServerId || !Cvar_ns_report_server_to_masterserver->m_nValue)
+ if (!*m_ownServerId || !Cvar_ns_report_server_to_masterserver->GetBool())
return;
std::thread requestThread(
@@ -1155,7 +1155,7 @@ void MasterServerManager::RemoveSelfFromServerList()
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
curl_easy_setopt(
curl, CURLOPT_URL,
- fmt::format("{}/server/remove_server?id={}", Cvar_ns_masterserver_hostname->m_pszString, m_ownServerId).c_str());
+ fmt::format("{}/server/remove_server?id={}", Cvar_ns_masterserver_hostname->GetString(), m_ownServerId).c_str());
CURLcode result = curl_easy_perform(curl);
@@ -1197,14 +1197,14 @@ void CHostState__State_NewGameHook(CHostState* hostState)
maxPlayers = std::stoi(maxPlayersVar);
// Copy new server name cvar to source
- Cvar_hostname->m_pszString = Cvar_ns_server_name->m_pszString;
- Cvar_hostname->m_StringLength = Cvar_ns_server_name->m_StringLength;
+ Cvar_hostname->SetValue(Cvar_ns_server_name->GetString());
// This calls the function that converts unicode strings from servername and serverdesc to UTF-8
UpdateServerInfoFromUnicodeToUTF8();
g_MasterServerManager->AddSelfToServerList(
- Cvar_hostport->m_nValue, Cvar_ns_player_auth_port->m_nValue, Cvar_ns_server_name->m_pszString, Cvar_ns_server_desc->m_pszString,
- hostState->m_levelName, (char*)GetCurrentPlaylistName(), maxPlayers, Cvar_ns_server_password->m_pszString);
+ Cvar_hostport->GetInt(), Cvar_ns_player_auth_port->GetInt(), (char*)Cvar_ns_server_name->GetString(),
+ (char*)Cvar_ns_server_desc->GetString(), hostState->m_levelName, (char*)GetCurrentPlaylistName(), maxPlayers,
+ (char*)Cvar_ns_server_password->GetString());
g_ServerAuthenticationManager->StartPlayerAuthServer();
g_ServerAuthenticationManager->m_bNeedLocalAuthForNewgame = false;
}
@@ -1255,15 +1255,15 @@ MasterServerManager::MasterServerManager() : m_pendingConnectionInfo{}, m_ownSer
void InitialiseSharedMasterServer(HMODULE baseAddress)
{
- Cvar_ns_masterserver_hostname = RegisterConVar("ns_masterserver_hostname", "127.0.0.1", FCVAR_NONE, "");
+ Cvar_ns_masterserver_hostname = new ConVar("ns_masterserver_hostname", "127.0.0.1", FCVAR_NONE, "");
// unfortunately lib doesn't let us specify a port and still have https work
- // Cvar_ns_masterserver_port = RegisterConVar("ns_masterserver_port", "8080", FCVAR_NONE, "");
+ // Cvar_ns_masterserver_port = new ConVar("ns_masterserver_port", "8080", FCVAR_NONE, "");
- Cvar_ns_server_name = RegisterConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "");
- Cvar_ns_server_desc = RegisterConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "");
- Cvar_ns_server_password = RegisterConVar("ns_server_password", "", FCVAR_GAMEDLL, "");
- Cvar_ns_report_server_to_masterserver = RegisterConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, "");
- Cvar_ns_report_sp_server_to_masterserver = RegisterConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, "");
+ Cvar_ns_server_name = new ConVar("ns_server_name", "Unnamed Northstar Server", FCVAR_GAMEDLL, "");
+ Cvar_ns_server_desc = new ConVar("ns_server_desc", "Default server description", FCVAR_GAMEDLL, "");
+ Cvar_ns_server_password = new ConVar("ns_server_password", "", FCVAR_GAMEDLL, "");
+ Cvar_ns_report_server_to_masterserver = new ConVar("ns_report_server_to_masterserver", "1", FCVAR_GAMEDLL, "");
+ Cvar_ns_report_sp_server_to_masterserver = new ConVar("ns_report_sp_server_to_masterserver", "0", FCVAR_GAMEDLL, "");
Cvar_hostname = *(ConVar**)((char*)baseAddress + 0x1315bae8);
diff --git a/NorthstarDedicatedTest/modmanager.cpp b/NorthstarDedicatedTest/modmanager.cpp
index 7e6322cb..4f2bb416 100644
--- a/NorthstarDedicatedTest/modmanager.cpp
+++ b/NorthstarDedicatedTest/modmanager.cpp
@@ -284,7 +284,7 @@ void ModManager::LoadMods()
if (g_CustomConvars.find(convar->Name) ==
g_CustomConvars.end()) // make sure convar isn't registered yet, unsure if necessary but idk what behaviour is for defining
// same convar multiple times
- RegisterConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str());
+ new ConVar(convar->Name.c_str(), convar->DefaultValue.c_str(), convar->Flags, convar->HelpString.c_str());
// read vpk paths
if (fs::exists(mod.ModDirectory / "vpk"))
diff --git a/NorthstarDedicatedTest/pch.h b/NorthstarDedicatedTest/pch.h
index 653776ce..29d1fe8d 100644
--- a/NorthstarDedicatedTest/pch.h
+++ b/NorthstarDedicatedTest/pch.h
@@ -12,11 +12,22 @@
// add headers that you want to pre-compile here
#include "memalloc.h"
+
#include <Windows.h>
+#include <Psapi.h>
+#include <set>
+#include <filesystem>
+#include <sstream>
+
#include "logging.h"
#include "include/MinHook.h"
#include "spdlog/spdlog.h"
#include "libcurl/include/curl/curl.h"
#include "hookutils.h"
+template <typename ReturnType, typename... Args> ReturnType CallVFunc(int index, void* thisPtr, Args... args)
+{
+ return (*reinterpret_cast<ReturnType(__fastcall***)(void*, Args...)>(thisPtr))[index](thisPtr, args...);
+}
+
#endif \ No newline at end of file
diff --git a/NorthstarDedicatedTest/playlist.cpp b/NorthstarDedicatedTest/playlist.cpp
index d885d6f0..249230c8 100644
--- a/NorthstarDedicatedTest/playlist.cpp
+++ b/NorthstarDedicatedTest/playlist.cpp
@@ -40,7 +40,7 @@ char Onclc_SetPlaylistVarOverrideHook(void* a1, void* a2)
{
// the private_match playlist is the only situation where there should be any legitimate sending of this netmessage
// todo: check mp_lobby here too
- if (!Cvar_ns_use_clc_SetPlaylistVarOverride->m_nValue || strcmp(GetCurrentPlaylistName(), "private_match"))
+ if (!Cvar_ns_use_clc_SetPlaylistVarOverride->GetBool() || strcmp(GetCurrentPlaylistName(), "private_match"))
return 1;
return Onclc_SetPlaylistVarOverride(a1, a2);
@@ -80,7 +80,7 @@ void InitialisePlaylistHooks(HMODULE baseAddress)
// server this is somewhat restricted on custom servers to prevent it being done outside of private matches, but ideally it should be
// disabled altogether, since the custom menus won't use it anyway this should only really be accepted if you want vanilla client
// compatibility
- Cvar_ns_use_clc_SetPlaylistVarOverride = RegisterConVar(
+ Cvar_ns_use_clc_SetPlaylistVarOverride = new ConVar(
"ns_use_clc_SetPlaylistVarOverride", "0", FCVAR_GAMEDLL, "Whether the server should accept clc_SetPlaylistVarOverride messages");
HookEnabler hook;
diff --git a/NorthstarDedicatedTest/serverauthentication.cpp b/NorthstarDedicatedTest/serverauthentication.cpp
index 57646218..8bbca125 100644
--- a/NorthstarDedicatedTest/serverauthentication.cpp
+++ b/NorthstarDedicatedTest/serverauthentication.cpp
@@ -87,11 +87,11 @@ void ServerAuthenticationManager::StartPlayerAuthServer()
"/authenticate_incoming_player",
[this](const httplib::Request& request, httplib::Response& response)
{
- // can't just do request.remote_addr == Cvar_ns_masterserver_hostname->m_pszString because the cvar can be a url, gotta
+ // can't just do request.remote_addr == Cvar_ns_masterserver_hostname->GetString() because the cvar can be a url, gotta
// resolve an ip from it for comparisons
// unsigned long remoteAddr = inet_addr(request.remote_addr.c_str());
//
- // char* addrPtr = Cvar_ns_masterserver_hostname->m_pszString;
+ // char* addrPtr = Cvar_ns_masterserver_hostname->GetString();
// char* typeStart = strstr(addrPtr, "://");
// if (typeStart)
// addrPtr = typeStart + 3;
@@ -123,7 +123,7 @@ void ServerAuthenticationManager::StartPlayerAuthServer()
response.set_content("{\"success\":true}", "application/json");
});
- m_playerAuthServer.listen("0.0.0.0", Cvar_ns_player_auth_port->m_nValue);
+ m_playerAuthServer.listen("0.0.0.0", Cvar_ns_player_auth_port->GetInt());
});
serverThread.detach();
@@ -191,7 +191,7 @@ bool ServerAuthenticationManager::AuthenticatePlayer(void* player, int64_t uid,
// set persistent data as ready, we use 0x3 internally to mark the client as using local persistence
*((char*)player + 0x4a0) = (char)0x3;
- if (!CVar_ns_auth_allow_insecure->m_nValue) // no auth data and insecure connections aren't allowed, so dc the client
+ if (!CVar_ns_auth_allow_insecure->GetBool()) // no auth data and insecure connections aren't allowed, so dc the client
return false;
// insecure connections are allowed, try reading from disk
@@ -223,7 +223,7 @@ bool ServerAuthenticationManager::AuthenticatePlayer(void* player, int64_t uid,
bool ServerAuthenticationManager::RemovePlayerAuthData(void* player)
{
- if (!Cvar_ns_erase_auth_info->m_nValue)
+ if (!Cvar_ns_erase_auth_info->GetBool())
return false;
// hack for special case where we're on a local server, so we erase our own newly created auth data on disconnect
@@ -256,7 +256,7 @@ void ServerAuthenticationManager::WritePersistentData(void* player)
g_MasterServerManager->WritePlayerPersistentData(
(char*)player + 0xF500, (char*)player + 0x4FA, m_additionalPlayerData[player].pdataSize);
}
- else if (CVar_ns_auth_allow_insecure_write->m_nValue)
+ else if (CVar_ns_auth_allow_insecure_write->GetBool())
{
// todo: write pdata to disk here
}
@@ -270,7 +270,7 @@ bool ServerAuthenticationManager::CheckPlayerChatRatelimit(void* player)
m_additionalPlayerData[player].sayTextLimitCount = 0;
}
- if (m_additionalPlayerData[player].sayTextLimitCount >= Cvar_sv_max_chat_messages_per_sec->m_nValue)
+ if (m_additionalPlayerData[player].sayTextLimitCount >= Cvar_sv_max_chat_messages_per_sec->GetInt())
return false;
m_additionalPlayerData[player].sayTextLimitCount++;
@@ -380,7 +380,7 @@ CCommand__TokenizeType CCommand__Tokenize;
char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const char* pCommandString)
{
- if (CVar_sv_quota_stringcmdspersecond->m_nValue != -1)
+ if (CVar_sv_quota_stringcmdspersecond->GetInt() != -1)
{
// note: this isn't super perfect, legit clients can trigger it in lobby, mostly good enough tho imo
// https://github.com/perilouswithadollarsign/cstrike15_src/blob/f82112a2388b841d72cb62ca48ab1846dfcc11c8/engine/sv_client.cpp#L1513
@@ -393,7 +393,7 @@ char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const c
g_ServerAuthenticationManager->m_additionalPlayerData[self].numClientCommandsInQuota++;
if (g_ServerAuthenticationManager->m_additionalPlayerData[self].numClientCommandsInQuota >
- CVar_sv_quota_stringcmdspersecond->m_nValue)
+ CVar_sv_quota_stringcmdspersecond->GetInt())
{
// too many stringcmds, dc player
CBaseClient__Disconnect(self, 1, "Sent too many stringcmd commands");
@@ -409,7 +409,7 @@ char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const c
if (!CCommand__Tokenize(tempCommand, pCommandString, cmd_source_t::kCommandSrcCode) || !tempCommand.ArgC())
return false;
- ConCommand* command = FindConCommand(tempCommand.Arg(0));
+ ConCommand* command = g_pCVar->FindCommand(tempCommand.Arg(0));
// if the command doesn't exist pass it on to ExecuteStringCommand for script clientcommands and stuff
if (command && !command->IsFlagSet(FCVAR_CLIENTCMD_CAN_EXECUTE))
@@ -422,7 +422,7 @@ char CGameClient__ExecuteStringCommandHook(void* self, uint32_t unknown, const c
return false;
}
- // todo later, basically just limit to CVar_sv_quota_stringcmdspersecond->m_nValue stringcmds per client per second
+ // todo later, basically just limit to CVar_sv_quota_stringcmdspersecond->GetInt() stringcmds per client per second
return CGameClient__ExecuteStringCommand(self, unknown, pCommandString);
}
@@ -453,15 +453,15 @@ char __fastcall CNetChan___ProcessMessagesHook(void* self, void* buf)
(Plat_FloatTime() * 1000) - (startTime * 1000);
if (g_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime >=
- Cvar_net_chan_limit_msec_per_sec->m_nValue)
+ Cvar_net_chan_limit_msec_per_sec->GetInt())
{
spdlog::warn(
"Client {} hit netchan processing limit with {}ms of processing time this second (max is {})", (char*)sender + 0x16,
g_ServerAuthenticationManager->m_additionalPlayerData[sender].netChanProcessingLimitTime,
- Cvar_net_chan_limit_msec_per_sec->m_nValue);
+ Cvar_net_chan_limit_msec_per_sec->GetInt());
// nonzero = kick, 0 = warn
- if (Cvar_net_chan_limit_mode->m_nValue)
+ if (Cvar_net_chan_limit_mode->GetInt())
{
CBaseClient__Disconnect(sender, 1, "Exceeded net channel processing limit");
return false;
@@ -486,7 +486,7 @@ void CBaseClient__SendServerInfoHook(void* self)
bool ProcessConnectionlessPacketHook(void* a1, netpacket_t* packet)
{
if (packet->adr.type == NA_IP &&
- (!(packet->data[4] == 'N' && Cvar_net_datablock_enabled->m_nValue) || !Cvar_net_datablock_enabled->m_nValue))
+ (!(packet->data[4] == 'N' && Cvar_net_datablock_enabled->GetBool()) || !Cvar_net_datablock_enabled->GetBool()))
{
// bad lookup: optimise later tm
UnconnectedPlayerSendData* sendData = nullptr;
@@ -516,10 +516,10 @@ bool ProcessConnectionlessPacketHook(void* a1, netpacket_t* packet)
sendData->packetCount++;
- if (sendData->packetCount >= Cvar_sv_querylimit_per_sec->m_nValue)
+ if (sendData->packetCount >= Cvar_sv_querylimit_per_sec->GetInt())
{
spdlog::warn(
- "Client went over connectionless ratelimit of {} per sec with packet of type {}", Cvar_sv_querylimit_per_sec->m_nValue,
+ "Client went over connectionless ratelimit of {} per sec with packet of type {}", Cvar_sv_querylimit_per_sec->GetInt(),
packet->data[4]);
// timeout for a minute
@@ -547,26 +547,26 @@ void InitialiseServerAuthentication(HMODULE baseAddress)
{
g_ServerAuthenticationManager = new ServerAuthenticationManager;
- Cvar_ns_erase_auth_info = RegisterConVar(
- "ns_erase_auth_info", "1", FCVAR_GAMEDLL, "Whether auth info should be erased from this server on disconnect or crash");
+ Cvar_ns_erase_auth_info =
+ new ConVar("ns_erase_auth_info", "1", FCVAR_GAMEDLL, "Whether auth info should be erased from this server on disconnect or crash");
CVar_ns_auth_allow_insecure =
- RegisterConVar("ns_auth_allow_insecure", "0", FCVAR_GAMEDLL, "Whether this server will allow unauthenicated players to connect");
- CVar_ns_auth_allow_insecure_write = RegisterConVar(
+ new ConVar("ns_auth_allow_insecure", "0", FCVAR_GAMEDLL, "Whether this server will allow unauthenicated players to connect");
+ CVar_ns_auth_allow_insecure_write = new ConVar(
"ns_auth_allow_insecure_write", "0", FCVAR_GAMEDLL,
"Whether the pdata of unauthenticated clients will be written to disk when changed");
// literally just stolen from a fix valve used in csgo
- CVar_sv_quota_stringcmdspersecond = RegisterConVar(
+ CVar_sv_quota_stringcmdspersecond = new ConVar(
"sv_quota_stringcmdspersecond", "60", FCVAR_GAMEDLL,
"How many string commands per second clients are allowed to submit, 0 to disallow all string commands");
// https://blog.counter-strike.net/index.php/2019/07/24922/ but different because idk how to check what current tick number is
Cvar_net_chan_limit_mode =
- RegisterConVar("net_chan_limit_mode", "0", FCVAR_GAMEDLL, "The mode for netchan processing limits: 0 = log, 1 = kick");
- Cvar_net_chan_limit_msec_per_sec = RegisterConVar(
+ new ConVar("net_chan_limit_mode", "0", FCVAR_GAMEDLL, "The mode for netchan processing limits: 0 = log, 1 = kick");
+ Cvar_net_chan_limit_msec_per_sec = new ConVar(
"net_chan_limit_msec_per_sec", "0", FCVAR_GAMEDLL,
"Netchannel processing is limited to so many milliseconds, abort connection if exceeding budget");
- Cvar_ns_player_auth_port = RegisterConVar("ns_player_auth_port", "8081", FCVAR_GAMEDLL, "");
- Cvar_sv_querylimit_per_sec = RegisterConVar("sv_querylimit_per_sec", "15", FCVAR_GAMEDLL, "");
- Cvar_sv_max_chat_messages_per_sec = RegisterConVar("sv_max_chat_messages_per_sec", "5", FCVAR_GAMEDLL, "");
+ Cvar_ns_player_auth_port = new ConVar("ns_player_auth_port", "8081", FCVAR_GAMEDLL, "");
+ Cvar_sv_querylimit_per_sec = new ConVar("sv_querylimit_per_sec", "15", FCVAR_GAMEDLL, "");
+ Cvar_sv_max_chat_messages_per_sec = new ConVar("sv_max_chat_messages_per_sec", "5", FCVAR_GAMEDLL, "");
RegisterConCommand("ns_resetpersistence", ResetPdataCommand, "resets your pdata when you next enter the lobby", FCVAR_NONE);
diff --git a/NorthstarDedicatedTest/sourceconsole.cpp b/NorthstarDedicatedTest/sourceconsole.cpp
index e6f46c13..2ce45baa 100644
--- a/NorthstarDedicatedTest/sourceconsole.cpp
+++ b/NorthstarDedicatedTest/sourceconsole.cpp
@@ -1,4 +1,5 @@
#include "pch.h"
+#include "convar.h"
#include "sourceconsole.h"
#include "sourceinterface.h"
#include "concommand.h"