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.