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"
>
+
+
@@ -229,6 +233,10 @@
RelativePath="..\extension.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.