diff --git a/extensions/sdktools/extension.cpp b/extensions/sdktools/extension.cpp
index 09129dae..ceb53208 100644
--- a/extensions/sdktools/extension.cpp
+++ b/extensions/sdktools/extension.cpp
@@ -25,6 +25,7 @@
 #include "vcallbuilder.h"
 #include "vnatives.h"
 #include "tempents.h"
+#include "gamerules.h"
 
 /**
  * @file extension.cpp
@@ -102,7 +103,9 @@ bool SDKTools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool
 void SDKTools::SDK_OnAllLoaded()
 {
 	SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
+
 	g_TEManager.Initialize();
+	InitializeGameRules();
 }
 
 bool SDKTools::QueryRunning(char *error, size_t maxlength)
diff --git a/extensions/sdktools/msvc8/sdktools.vcproj b/extensions/sdktools/msvc8/sdktools.vcproj
index 84939d57..1f40fe2f 100644
--- a/extensions/sdktools/msvc8/sdktools.vcproj
+++ b/extensions/sdktools/msvc8/sdktools.vcproj
@@ -187,6 +187,10 @@
 				RelativePath="..\extension.cpp"
 				>
 			</File>
+			<File
+				RelativePath="..\gamerules.cpp"
+				>
+			</File>
 			<File
 				RelativePath="..\tempents.cpp"
 				>
@@ -229,6 +233,10 @@
 				RelativePath="..\extension.h"
 				>
 			</File>
+			<File
+				RelativePath="..\gamerules.h"
+				>
+			</File>
 			<File
 				RelativePath="..\tempents.h"
 				>
diff --git a/extensions/sdktools/vcallbuilder.cpp b/extensions/sdktools/vcallbuilder.cpp
index 7243408b..e91f596c 100644
--- a/extensions/sdktools/vcallbuilder.cpp
+++ b/extensions/sdktools/vcallbuilder.cpp
@@ -80,6 +80,8 @@ ValveCall *CreateValveCall(void *addr,
 
 	ValveCall *vc = new ValveCall;
 
+	vc->type = vcalltype;
+
 	size_t size = 0;
 	vc->stackSize = 0;
 
@@ -109,16 +111,24 @@ ValveCall *CreateValveCall(void *addr,
 	{
 		thisinfo = &thisbuf;
 		thisinfo->type = PassType_Basic;
-		if (vcalltype == ValveCall_Entity)
+		switch (vcalltype)
 		{
+		case ValveCall_Entity:
 			thisinfo->vtype = Valve_CBaseEntity;
+			thisinfo->flags = PASSFLAG_BYVAL;
 			thisinfo->decflags |= VDECODE_FLAG_ALLOWWORLD;
-		} else if (vcalltype == ValveCall_Player) {
+			break;
+		case ValveCall_Player:
 			thisinfo->vtype = Valve_CBasePlayer;
+			thisinfo->flags = PASSFLAG_BYVAL;
+			thisinfo->decflags = 0;
+			break;
+		case ValveCall_GameRules:
+			thisinfo->vtype = Valve_POD;
+			thisinfo->flags = PASSFLAG_ASPOINTER;
 			thisinfo->decflags = 0;
 		}
 		thisinfo->encflags = 0;
-		thisinfo->flags = PASSFLAG_BYVAL;
 		thisinfo->offset = 0;
 		vc->stackSize += sizeof(void *);
 		cv = CallConv_ThisCall;
@@ -227,6 +237,8 @@ ValveCall *CreateValveVCall(unsigned int vtableIdx,
 
 	ValveCall *vc = new ValveCall;
 
+	vc->type = vcalltype;
+
 	size_t size = 0;
 	vc->stackSize = 0;
 
@@ -314,16 +326,25 @@ ValveCall *CreateValveVCall(unsigned int vtableIdx,
 	/* Save the this info for the dynamic decoder */
 	vc->thisinfo = &(vc->vparams[numParams + 1]);
 	vc->thisinfo->type = PassType_Basic;
-	if (vcalltype == ValveCall_Entity)
+	switch (vcalltype)
 	{
+	case ValveCall_Entity:
 		vc->thisinfo->vtype = Valve_CBaseEntity;
+		vc->thisinfo->flags = PASSFLAG_BYVAL;
 		vc->thisinfo->decflags = VDECODE_FLAG_ALLOWWORLD;
-	} else if (vcalltype == ValveCall_Player) {
+		break;
+	case ValveCall_Player:
 		vc->thisinfo->vtype = Valve_CBasePlayer;
+		vc->thisinfo->flags = PASSFLAG_BYVAL;
 		vc->thisinfo->decflags = 0;
+		break;
+	case ValveCall_GameRules:
+		vc->thisinfo->vtype = Valve_POD;
+		vc->thisinfo->flags = PASSFLAG_ASPOINTER;
+		vc->thisinfo->decflags = 0;
+		break;
 	}
 	vc->thisinfo->encflags = 0;
-	vc->thisinfo->flags = PASSFLAG_BYVAL;
 	vc->thisinfo->offset = 0;
 	vc->thisinfo->obj_offset = 0;
 	normSize += sizeof(void *);
diff --git a/extensions/sdktools/vcallbuilder.h b/extensions/sdktools/vcallbuilder.h
index 937dd05c..3aa37e8e 100644
--- a/extensions/sdktools/vcallbuilder.h
+++ b/extensions/sdktools/vcallbuilder.h
@@ -37,6 +37,7 @@ using namespace SourceHook;
 struct ValveCall
 {
 	ICallWrapper *call;			/**< From IBinTools */
+	ValveCallType type;			/**< Call type */
 	ValvePassInfo *vparams;		/**< Valve parameter info */
 	ValvePassInfo *retinfo;		/**< Return buffer info */
 	ValvePassInfo *thisinfo;	/**< Thiscall info */
diff --git a/extensions/sdktools/vcaller.cpp b/extensions/sdktools/vcaller.cpp
index ea2b87a8..4a7d59a6 100644
--- a/extensions/sdktools/vcaller.cpp
+++ b/extensions/sdktools/vcaller.cpp
@@ -23,6 +23,7 @@
 
 #include "extension.h"
 #include "vcallbuilder.h"
+#include "gamerules.h"
 
 enum SDKLibrary
 {
@@ -245,25 +246,50 @@ static cell_t SDKCall(IPluginContext *pContext, const cell_t *params)
 	unsigned int startparam = 2;
 	/* Do we need to write a thispointer?  */
 
-	if (vc->thisinfo && 
-		(vc->thisinfo->vtype == Valve_CBaseEntity
-		 || vc->thisinfo->vtype == Valve_CBasePlayer))
+	if (vc->thisinfo)
 	{
-		if (startparam > numparams)
+		switch (vc->type)
 		{
-			vc->stk_put(ptr);
-			return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
+		case ValveCall_Entity:
+		case ValveCall_Player:
+			{
+				if (startparam > numparams)
+				{
+					vc->stk_put(ptr);
+					return pContext->ThrowNativeError("Expected 1 parameter for entity pointer; found none");
+				}
+
+				if (DecodeValveParam(pContext, 
+					params[startparam],
+					vc,
+					vc->thisinfo,
+					ptr) == Data_Fail)
+				{
+					vc->stk_put(ptr);
+					return 0;
+				}
+				startparam++;
+			}
+			break;
+		case ValveCall_GameRules:
+			{
+				if (g_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");
+				}
+				*(void **)ptr = gamerules;
+			}
+			break;
 		}
-		if (DecodeValveParam(pContext, 
-			params[startparam],
-			vc,
-			vc->thisinfo,
-			ptr) == Data_Fail)
-		{
-			vc->stk_put(ptr);
-			return 0;
-		}
-		startparam++;
 	}
 
 	/* See if we need to skip any more parameters */
diff --git a/extensions/sdktools/vdecoder.h b/extensions/sdktools/vdecoder.h
index 453e98c4..4461bdf4 100644
--- a/extensions/sdktools/vdecoder.h
+++ b/extensions/sdktools/vdecoder.h
@@ -67,9 +67,10 @@ enum DataStatus
  */
 enum ValveCallType
 {
-	ValveCall_Static,	/**< Static call */
-	ValveCall_Entity,	/**< Thiscall (CBaseEntity implicit first parameter) */
-	ValveCall_Player,	/**< Thiscall (CBasePlayer implicit first parameter) */
+	ValveCall_Static,		/**< Static call */
+	ValveCall_Entity,		/**< Thiscall (CBaseEntity implicit first parameter) */
+	ValveCall_Player,		/**< Thiscall (CBasePlayer implicit first parameter) */
+	ValveCall_GameRules,	/**< Thiscall (CGameRules implicit first paramater) */
 };
 
 /**
diff --git a/gamedata/sdktools.games.txt b/gamedata/sdktools.games.txt
index 2031bda0..ca111fc8 100644
--- a/gamedata/sdktools.games.txt
+++ b/gamedata/sdktools.games.txt
@@ -8,9 +8,9 @@
 			"game"		"cstrike"
 			"game"		"dod"
 			"game"		"hl2mp"
-			"game"		"insurgency"
-			"game"		"dystopia_v1"
-			"game"		"sourceforts"
+			"game"		"!Dystopia"
+			"game"		"!Insurgency"
+			"game"		"!SourceForts v1.9.2"
 		}
 
 		"Offsets"
@@ -48,41 +48,50 @@
 			}
 		}
 	}
-
-	/* HL2MP */
-	"hl2mp"
+	
+	/* General GameRules */
+	"#default"
 	{
+		"#supported"
+		{
+			"game"		"cstrike"
+			"game"		"dod"
+			"game"		"garrysmod"
+			"game"		"hl2mp"
+			"game"		"ship"
+			"game"		"!Dystopia"
+			"game"		"!Insurgency"
+			"game"		"!SourceForts v1.9.2"
+		}
+		
 		"Offsets"
 		{
-			"GiveNamedItem"
+			/* Offset into CreateGameRulesObject */
+			"g_pGameRules"
 			{
-				"windows"	"327"
-				"linux"		"328"
+				"windows"	"2"
 			}
-			"RemovePlayerItem"
+		}
+		
+		"Signatures"
+		{
+			/* This signature sometimes has multiple matches, but this
+			 * does not matter as g_pGameRules is involved in all of them.
+			 * The same g_pGameRules offset applies to each match.
+			 *
+			 * Sometimes this block of bytes is at the beginning of the static
+			 * CreateGameRulesObject function and sometimes it is in the middle
+			 * of an entirely different function. This depends on the game.
+			 */
+			"CreateGameRulesObject"
 			{
-				"windows"	"225"
-				"linux"		"226"
+				"library"	"server"
+				"windows"	"\x8B\x0D\x2A\x2A\x2A\x2A\x85\xC9\x74\x2A\x8B\x01\x6A\x01\xFF\x50"
 			}
-			"Weapon_GetSlot"
+			"g_pGameRules"
 			{
-				"windows"	"223"
-				"linux"		"224"
-			}
-			"Ignite"
-			{
-				"windows"	"187"
-				"linux"		"188"
-			}
-			"Extinguish"
-			{
-				"windows"	"188"
-				"linux"		"189"
-			}
-			"Teleport"
-			{
-				"windows"	"97"
-				"linux"		"98"
+				"library"	"server"
+				"linux"		"@g_pGameRules"
 			}
 		}
 	}
@@ -162,91 +171,47 @@
 			}
 		}
 	}
-
-	/* Insurgency */
-	"insurgency"
+	
+	/* Half-Life 2: Deathmatch */
+	"hl2mp"
 	{
 		"Offsets"
 		{
-			/* CBasePlayer */
-			"Ignite"
-			{
-				"windows"	"174"
-				"linux"		"175"
-			}
-			"Extinguish"
-			{
-				"windows"	"175"
-				"linux"		"176"
-			}
-			"Teleport"
-			{
-				"windows"	"90"
-				"linux"		"91"
-			}
-
-			/* Temp Entities */
-			"s_pTempEntities"
-			{
-				"linux"		"28"
-			}
-		}
-	}
-
-	/* SourceForts 1.9.2
-	 * :TODO: use description instead...
-	 */
-	"sourceforts"
-	{
-		"Offsets"
-		{
-			/* CBasePlayer */
 			"GiveNamedItem"
 			{
-				"windows"	"294"
-				"linux"		"295"
+				"windows"	"327"
+				"linux"		"328"
 			}
 			"RemovePlayerItem"
 			{
-				"windows"	"207"
-				"linux"		"208"
+				"windows"	"225"
+				"linux"		"226"
 			}
 			"Weapon_GetSlot"
 			{
-				"windows"	"205"
-				"linux"		"206"
+				"windows"	"223"
+				"linux"		"224"
 			}
 			"Ignite"
 			{
-				"windows"	"170"
-				"linux"		"171"
+				"windows"	"187"
+				"linux"		"188"
 			}
 			"Extinguish"
 			{
-				"windows"	"171"
-				"linux"		"172"
+				"windows"	"188"
+				"linux"		"189"
 			}
 			"Teleport"
 			{
-				"windows"	"90"
-				"linux"		"91"
-			}
-
-			/* Temp Entities */
-			"s_pTempEntities"
-			{
-				"linux"		"29"
-			}
-			"TE_GetServerClass"
-			{
-				"windows"	"0"
-				"linux"		"0"
+				"windows"	"97"
+				"linux"		"98"
 			}
 		}
 	}
-
+	
 	/* Dsytopia */
-	"dystopia_v1"
+	"!Dystopia"
 	{
 
 		"Offsets"
@@ -304,6 +269,98 @@
 				"library"	"server"
 				"windows"	"\x81\xEC\x84\x00\x00\x00\x56\x8B\xF1\x8B\x46\x6C\x57\x8D\x7E\x6C\x8D\x4C\x24\x08\x83\xC8\x20\x51\x89\x44\x24\x0C\xE8\x2A\x2A\x2A"
 			}
+
+			/* Dystopia always has to be different, doesn't it
+			 *
+			 * This is very similar to the general signature, except that
+			 * it does "mov edx, [eax+2Ch]" before making a call rather than
+			 * doing "call dword ptr [eax+2Ch]"
+			 */
+			"CreateGameRulesObject"
+			{
+				"library"	"server"
+				"windows"	"\x8B\x0D\x2A\x2A\x2A\x2A\x85\xC9\x74\x2A\x8B\x01\x8B\x50\x2A\x6A\x01\xFF\xD2"
+			}
+		}
+	}
+
+	/* Insurgency */
+	"!Insurgency"
+	{
+		"Offsets"
+		{
+			/* CBasePlayer */
+			"Ignite"
+			{
+				"windows"	"174"
+				"linux"		"175"
+			}
+			"Extinguish"
+			{
+				"windows"	"175"
+				"linux"		"176"
+			}
+			"Teleport"
+			{
+				"windows"	"90"
+				"linux"		"91"
+			}
+
+			/* Temp Entities */
+			"s_pTempEntities"
+			{
+				"linux"		"28"
+			}
+		}
+	}
+
+	/* SourceForts 1.9.2 */
+	"!SourceForts v1.9.2"
+	{
+		"Offsets"
+		{
+			/* CBasePlayer */
+			"GiveNamedItem"
+			{
+				"windows"	"294"
+				"linux"		"295"
+			}
+			"RemovePlayerItem"
+			{
+				"windows"	"207"
+				"linux"		"208"
+			}
+			"Weapon_GetSlot"
+			{
+				"windows"	"205"
+				"linux"		"206"
+			}
+			"Ignite"
+			{
+				"windows"	"170"
+				"linux"		"171"
+			}
+			"Extinguish"
+			{
+				"windows"	"171"
+				"linux"		"172"
+			}
+			"Teleport"
+			{
+				"windows"	"90"
+				"linux"		"91"
+			}
+
+			/* Temp Entities */
+			"s_pTempEntities"
+			{
+				"linux"		"29"
+			}
+			"TE_GetServerClass"
+			{
+				"windows"	"0"
+				"linux"		"0"
+			}
 		}
 	}
 }
diff --git a/plugins/include/sdktools.inc b/plugins/include/sdktools.inc
index bba67ece..c2f8f6f6 100644
--- a/plugins/include/sdktools.inc
+++ b/plugins/include/sdktools.inc
@@ -31,6 +31,7 @@ enum SDKCallType
 	SDKCall_Static,		/**< Static call */
 	SDKCall_Entity,		/**< CBaseEntity call */
 	SDKCall_Player,		/**< CBasePlayer call */
+	SDKCall_GameRules,	/**< CGameRules call */
 };
 
 enum SDKLibrary
@@ -147,6 +148,7 @@ native Handle:EndPrepSDKCall();
  * Calls an SDK function with the given parameters.
  *
  * If the call type is Entity or Player, the index MUST ALWAYS be the FIRST parameter passed.
+ * If the call type is GameRules, then nothing special needs to be passed.
  * If the return value is a Vector or QAngles, the SECOND parameter must be a Float[3].
  * If the return value is a string, the THIRD parameter must be a String buffer, and the
  *  FOURTH parameter must be the maximum length.