From 939b10bd8e9654da78b1cbcf1ca1824adb429b38 Mon Sep 17 00:00:00 2001
From: Nicholas Hastings <nshastings@gmail.com>
Date: Sat, 13 Dec 2014 15:48:51 -0500
Subject: [PATCH 1/3] Refactor SDKTools to not expose gamerules ptr ptr outside
 of vglobals.cpp.

---
 extensions/sdktools/extension.cpp        |  7 ++-
 extensions/sdktools/extension.h          |  5 ++
 extensions/sdktools/gamerulesnatives.cpp | 58 ++++++++++++------------
 extensions/sdktools/vcaller.cpp          | 17 +++----
 extensions/sdktools/vglobals.cpp         | 12 +++--
 extensions/sdktools/vglobals.h           |  2 +-
 6 files changed, 54 insertions(+), 47 deletions(-)

diff --git a/extensions/sdktools/extension.cpp b/extensions/sdktools/extension.cpp
index 061010cf..05a8bb7e 100644
--- a/extensions/sdktools/extension.cpp
+++ b/extensions/sdktools/extension.cpp
@@ -340,6 +340,8 @@ bool SDKTools::RegisterConCommandBase(ConCommandBase *pVar)
 
 bool SDKTools::LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
 {
+	m_bAnyLevelInited = true;
+
 	const char *name;
 	char key[32];
 	int count, n = 1;
@@ -472,10 +474,7 @@ public:
 	
 	virtual void *GetGameRules()
 	{
-		if (!g_pGameRules)
-			return NULL;
-		
-		return *g_pGameRules;
+		return GameRules();
 	}
 } g_SDKTools_API;
 
diff --git a/extensions/sdktools/extension.h b/extensions/sdktools/extension.h
index 97b9f45b..00144e4f 100644
--- a/extensions/sdktools/extension.h
+++ b/extensions/sdktools/extension.h
@@ -107,6 +107,11 @@ public: //ICommandTargetProcessor
 public:
 	bool LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background);
 	void OnServerActivate(edict_t *pEdictList, int edictCount, int clientMax);
+public:
+	bool HasAnyLevelInited() { return m_bAnyLevelInited; }
+
+private:
+	bool m_bAnyLevelInited = false;
 };
 
 extern SDKTools g_SdkTools;
diff --git a/extensions/sdktools/gamerulesnatives.cpp b/extensions/sdktools/gamerulesnatives.cpp
index 8a7ad34e..98cd2a4a 100644
--- a/extensions/sdktools/gamerulesnatives.cpp
+++ b/extensions/sdktools/gamerulesnatives.cpp
@@ -163,7 +163,9 @@ static cell_t GameRules_GetProp(IPluginContext *pContext, const cell_t *params)
 	int bit_count;
 	bool is_unsigned;
 
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	void *pGameRules = GameRules();
+
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
@@ -185,8 +187,6 @@ static cell_t GameRules_GetProp(IPluginContext *pContext, const cell_t *params)
 		bit_count = params[2] * 8;
 	}
 
-	void *pGameRules = *g_pGameRules;
-
 	if (bit_count >= 17)
 	{
 		return *(int32_t *)((intptr_t)pGameRules + offset);
@@ -232,11 +232,13 @@ static cell_t GameRules_SetProp(IPluginContext *pContext, const cell_t *params)
 	if (params[5] == 0)
 		sendChange = false;
 
+	void *pGameRules = GameRules();
+
 	CBaseEntity *pProxy = NULL;
 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity");
 	
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed");
 
 	pContext->LocalToString(params[1], &prop);
@@ -250,8 +252,6 @@ static cell_t GameRules_SetProp(IPluginContext *pContext, const cell_t *params)
 	}
 #endif
 
-	void *pGameRules = *g_pGameRules;
-
 	if (bit_count < 1)
 	{
 		bit_count = params[3] * 8;
@@ -304,15 +304,15 @@ static cell_t GameRules_GetPropFloat(IPluginContext *pContext, const cell_t *par
 	int offset;
 	int bit_count;
 
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	void *pGameRules = GameRules();
+
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
 
 	FIND_PROP_SEND(DPT_Float, "float");
 
-	void *pGameRules = *g_pGameRules;
-
 	float val = *(float *)((intptr_t)pGameRules + offset);
 
 	return sp_ftoc(val);
@@ -329,18 +329,19 @@ static cell_t GameRules_SetPropFloat(IPluginContext *pContext, const cell_t *par
 	if (params[4] == 0)
 		sendChange = false;
 
+	void *pGameRules = GameRules();
+
 	CBaseEntity *pProxy = NULL;
 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
 	
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
 
 	FIND_PROP_SEND(DPT_Float, "float");
 
-	void *pGameRules = *g_pGameRules;
 	float newVal = sp_ctof(params[2]);
 
 	*(float *)((intptr_t)pGameRules + offset) = newVal;
@@ -361,15 +362,15 @@ static cell_t GameRules_GetPropEnt(IPluginContext *pContext, const cell_t *param
 	int offset;
 	int bit_count;
 
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	void *pGameRules = GameRules();
+
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
 
 	FIND_PROP_SEND(DPT_Int, "Integer");
 
-	void *pGameRules = *g_pGameRules;
-
 	CBaseHandle &hndl = *(CBaseHandle *)((intptr_t)pGameRules + offset);
 	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(hndl.GetEntryIndex());
 
@@ -392,19 +393,19 @@ static cell_t GameRules_SetPropEnt(IPluginContext *pContext, const cell_t *param
 	if (params[4] == 0)
 		sendChange = false;
 
+	void *pGameRules = GameRules();
+
 	CBaseEntity *pProxy = NULL;
 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
 	
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
 
 	FIND_PROP_SEND(DPT_Int, "integer");
 
-	void *pGameRules = *g_pGameRules;
-
 	CBaseHandle &hndl = *(CBaseHandle *)((intptr_t)pGameRules + offset);
 	CBaseEntity *pOther;
 
@@ -451,15 +452,15 @@ static cell_t GameRules_GetPropVector(IPluginContext *pContext, const cell_t *pa
 	int offset;
 	int bit_count;
 
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	void *pGameRules = GameRules();
+
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
 
 	FIND_PROP_SEND(DPT_Vector, "vector");
 
-	void *pGameRules = *g_pGameRules;
-
 	Vector *v = (Vector *)((intptr_t)pGameRules + offset);
 
 	cell_t *vec;
@@ -483,19 +484,19 @@ static cell_t GameRules_SetPropVector(IPluginContext *pContext, const cell_t *pa
 	if (params[4] == 0)
 		sendChange = false;
 
+	void *pGameRules = GameRules();
+
 	CBaseEntity *pProxy = NULL;
 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
 	
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
 
 	FIND_PROP_SEND(DPT_Vector, "vector");
 
-	void *pGameRules = *g_pGameRules;
-
 	Vector *v = (Vector *)((intptr_t)pGameRules + offset);
 
 	cell_t *vec;
@@ -523,7 +524,9 @@ static cell_t GameRules_GetPropString(IPluginContext *pContext, const cell_t *pa
 	char *prop;
 	int offset;
 
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	void *pGameRules = GameRules();
+
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
@@ -544,8 +547,6 @@ static cell_t GameRules_GetPropString(IPluginContext *pContext, const cell_t *pa
 				DPT_String);
 	}
 
-	void *pGameRules = *g_pGameRules;
-
 	size_t len;
 	const char *src;
 
@@ -585,11 +586,13 @@ static cell_t GameRules_SetPropString(IPluginContext *pContext, const cell_t *pa
 	if (params[3] == 0)
 		sendChange = false;
 
+	void *pGameRules = GameRules();
+
 	CBaseEntity *pProxy = NULL;
 	if (sendChange && ((pProxy = GetGameRulesProxyEnt()) == NULL))
 		return pContext->ThrowNativeError("Couldn't find gamerules proxy entity.");
 	
-	if (!g_pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
+	if (!pGameRules || !g_szGameRulesProxy || !strcmp(g_szGameRulesProxy, ""))
 		return pContext->ThrowNativeError("Gamerules lookup failed.");
 
 	pContext->LocalToString(params[1], &prop);
@@ -610,7 +613,6 @@ static cell_t GameRules_SetPropString(IPluginContext *pContext, const cell_t *pa
 				DPT_String);
 	}
 
-	void *pGameRules = *g_pGameRules;
 	maxlen = DT_MAX_STRING_BUFFERSIZE;
 
 	char *src;
diff --git a/extensions/sdktools/vcaller.cpp b/extensions/sdktools/vcaller.cpp
index 9cf6af65..ebcfe186 100644
--- a/extensions/sdktools/vcaller.cpp
+++ b/extensions/sdktools/vcaller.cpp
@@ -320,20 +320,17 @@ static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
 			break;
 		case ValveCall_GameRules:
 			{
-				if (g_pGameRules == NULL)
+				void *pGameRules = GameRules();
+				if (pGameRules == NULL)
 				{
 					vc->stk_put(ptr);
-					return pContext->ThrowNativeError("GameRules unsupported or not available; file a bug report");
-				}
 
-				void *gamerules = *g_pGameRules;
-
-				if (gamerules == NULL)
-				{
-					vc->stk_put(ptr);
-					return pContext->ThrowNativeError("GameRules not available before map is loaded");
+					if (g_SdkTools.HasAnyLevelInited())
+						return pContext->ThrowNativeError("GameRules unsupported or not available; file a bug report");
+					else
+						return pContext->ThrowNativeError("GameRules not available before map is loaded");
 				}
-				*(void **)ptr = gamerules;
+				*(void **)ptr = pGameRules;
 			}
 			break;
 		case ValveCall_EntityList:
diff --git a/extensions/sdktools/vglobals.cpp b/extensions/sdktools/vglobals.cpp
index 54b533bd..1526dc12 100644
--- a/extensions/sdktools/vglobals.cpp
+++ b/extensions/sdktools/vglobals.cpp
@@ -32,10 +32,14 @@
 #include "extension.h"
 #include "vhelpers.h"
 
-void **g_pGameRules = NULL;
-void *g_EntList = NULL;
+static void **g_ppGameRules = nullptr;
+void *g_EntList = nullptr;
 CBaseHandle g_ResourceEntity;
 
+void *GameRules()
+{
+	return g_ppGameRules ? *g_ppGameRules : g_ppGameRules;
+}
 
 void InitializeValveGlobals()
 {
@@ -52,7 +56,7 @@ void InitializeValveGlobals()
 	char *addr;
 	if (g_pGameConf->GetMemSig("g_pGameRules", (void **)&addr) && addr)
 	{
-		g_pGameRules = reinterpret_cast<void **>(addr);
+		g_ppGameRules = reinterpret_cast<void **>(addr);
 	}
 	else if (g_pGameConf->GetMemSig("CreateGameRulesObject", (void **)&addr) && addr)
 	{
@@ -61,7 +65,7 @@ void InitializeValveGlobals()
 		{
 			return;
 		}
-		g_pGameRules = *reinterpret_cast<void ***>(addr + offset);
+		g_ppGameRules = *reinterpret_cast<void ***>(addr + offset);
 	}
 }
 
diff --git a/extensions/sdktools/vglobals.h b/extensions/sdktools/vglobals.h
index 5cc2081e..773e9d80 100644
--- a/extensions/sdktools/vglobals.h
+++ b/extensions/sdktools/vglobals.h
@@ -32,13 +32,13 @@
 #ifndef _INCLUDE_SDKTOOLS_VGLOBALS_H_
 #define _INCLUDE_SDKTOOLS_VGLOBALS_H_
 
-extern void **g_pGameRules;
 extern void *g_EntList;
 
 extern CBaseHandle g_ResourceEntity;
 
 void InitializeValveGlobals();
 void GetIServer();
+void *GameRules();
 
 void GetResourceEntity();
 

From 88cb74213c06e73fd0f7e0470301ff1ccf15ebbd Mon Sep 17 00:00:00 2001
From: Nicholas Hastings <nshastings@gmail.com>
Date: Sat, 13 Dec 2014 16:35:32 -0500
Subject: [PATCH 2/3] Add support for getting g_pGameRules without a byte
 signature / symbol.

---
 extensions/sdktools/extension.cpp   |  2 +
 extensions/sdktools/vglobals.cpp    | 78 ++++++++++++++++++++++++++++-
 extensions/sdktools/vglobals.h      |  1 +
 gamedata/sdktools.games/game.tf.txt |  1 +
 4 files changed, 81 insertions(+), 1 deletion(-)

diff --git a/extensions/sdktools/extension.cpp b/extensions/sdktools/extension.cpp
index 05a8bb7e..ae3f1f38 100644
--- a/extensions/sdktools/extension.cpp
+++ b/extensions/sdktools/extension.cpp
@@ -342,6 +342,8 @@ bool SDKTools::LevelInit(char const *pMapName, char const *pMapEntities, char co
 {
 	m_bAnyLevelInited = true;
 
+	UpdateValveGlobals();
+
 	const char *name;
 	char key[32];
 	int count, n = 1;
diff --git a/extensions/sdktools/vglobals.cpp b/extensions/sdktools/vglobals.cpp
index 1526dc12..2c581130 100644
--- a/extensions/sdktools/vglobals.cpp
+++ b/extensions/sdktools/vglobals.cpp
@@ -32,13 +32,14 @@
 #include "extension.h"
 #include "vhelpers.h"
 
+static void *s_pGameRules = nullptr;
 static void **g_ppGameRules = nullptr;
 void *g_EntList = nullptr;
 CBaseHandle g_ResourceEntity;
 
 void *GameRules()
 {
-	return g_ppGameRules ? *g_ppGameRules : g_ppGameRules;
+	return g_ppGameRules ? *g_ppGameRules : s_pGameRules;
 }
 
 void InitializeValveGlobals()
@@ -69,6 +70,81 @@ void InitializeValveGlobals()
 	}
 }
 
+static bool UTIL_FindDataTable(SendTable *pTable,
+	const char *name,
+	sm_sendprop_info_t *info,
+	unsigned int offset = 0)
+{
+	const char *pname;
+	int props = pTable->GetNumProps();
+	SendProp *prop;
+	SendTable *table;
+
+	for (int i = 0; i<props; i++)
+	{
+		prop = pTable->GetProp(i);
+
+		if ((table = prop->GetDataTable()) != NULL)
+		{
+			pname = prop->GetName();
+			if (pname && strcmp(name, pname) == 0)
+			{
+				info->prop = prop;
+				info->actual_offset = offset + info->prop->GetOffset();
+				return true;
+			}
+
+			if (UTIL_FindDataTable(table,
+				name,
+				info,
+				offset + prop->GetOffset())
+				)
+			{
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+static ServerClass *UTIL_FindServerClass(const char *classname)
+{
+	ServerClass *sc = gamedll->GetAllServerClasses();
+	while (sc)
+	{
+		if (strcmp(classname, sc->GetName()) == 0)
+		{
+			return sc;
+		}
+		sc = sc->m_pNext;
+	}
+
+	return NULL;
+}
+
+void UpdateValveGlobals()
+{
+	s_pGameRules = nullptr;
+
+	const char *pszNetClass = g_pGameConf->GetKeyValue("GameRulesProxy");
+	const char *pszDTName = g_pGameConf->GetKeyValue("GameRulesDataTable");
+	if (pszNetClass && pszDTName)
+	{
+		ServerClass *sc = UTIL_FindServerClass(pszNetClass);
+		sm_sendprop_info_t info;
+		if (UTIL_FindDataTable(sc->m_pTable, pszDTName, &info))
+		{
+			auto proxyFn = info.prop->GetDataTableProxyFn();
+			if (proxyFn)
+			{
+				CSendProxyRecipients recp;
+				s_pGameRules = proxyFn(nullptr, nullptr, nullptr, &recp, 0);
+			}
+		}
+	}
+}
+
 size_t UTIL_StringToSignature(const char *str, char buffer[], size_t maxlength)
 {
 	size_t real_bytes = 0;
diff --git a/extensions/sdktools/vglobals.h b/extensions/sdktools/vglobals.h
index 773e9d80..1b6e045f 100644
--- a/extensions/sdktools/vglobals.h
+++ b/extensions/sdktools/vglobals.h
@@ -37,6 +37,7 @@ extern void *g_EntList;
 extern CBaseHandle g_ResourceEntity;
 
 void InitializeValveGlobals();
+void UpdateValveGlobals();
 void GetIServer();
 void *GameRules();
 
diff --git a/gamedata/sdktools.games/game.tf.txt b/gamedata/sdktools.games/game.tf.txt
index 62fd79f1..bba489f8 100644
--- a/gamedata/sdktools.games/game.tf.txt
+++ b/gamedata/sdktools.games/game.tf.txt
@@ -111,6 +111,7 @@
 		"Keys"
 		{
 			"GameRulesProxy"	"CTFGameRulesProxy"
+			"GameRulesDataTable" "tf_gamerules_data"
 		}
 	}
 }

From 0d67bcc5baa6ba254944fd56da89e6606cdf745f Mon Sep 17 00:00:00 2001
From: Nicholas Hastings <nshastings@gmail.com>
Date: Sun, 14 Dec 2014 15:21:54 -0500
Subject: [PATCH 3/3] Add null-check on serverclass in gamerules lookup.

---
 extensions/sdktools/vglobals.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/extensions/sdktools/vglobals.cpp b/extensions/sdktools/vglobals.cpp
index 2c581130..1fa7a815 100644
--- a/extensions/sdktools/vglobals.cpp
+++ b/extensions/sdktools/vglobals.cpp
@@ -120,7 +120,7 @@ static ServerClass *UTIL_FindServerClass(const char *classname)
 		sc = sc->m_pNext;
 	}
 
-	return NULL;
+	return nullptr;
 }
 
 void UpdateValveGlobals()
@@ -131,9 +131,10 @@ void UpdateValveGlobals()
 	const char *pszDTName = g_pGameConf->GetKeyValue("GameRulesDataTable");
 	if (pszNetClass && pszDTName)
 	{
-		ServerClass *sc = UTIL_FindServerClass(pszNetClass);
 		sm_sendprop_info_t info;
-		if (UTIL_FindDataTable(sc->m_pTable, pszDTName, &info))
+		ServerClass *sc = UTIL_FindServerClass(pszNetClass);
+
+		if (sc && UTIL_FindDataTable(sc->m_pTable, pszDTName, &info))
 		{
 			auto proxyFn = info.prop->GetDataTableProxyFn();
 			if (proxyFn)