From 542b7673d6ebaff55381f4858154af05c2b136ac Mon Sep 17 00:00:00 2001 From: Mikusch Date: Mon, 3 Jan 2022 14:13:54 +0100 Subject: [PATCH] Add LookupEntityAttachment & GetEntityAttachment natives (#1653) Using the virtual `CBaseAnimating::GetAttachment(int, matrix3x4_t &)` was a deliberate choice because virtual offsets are generally easier to maintain than signatures. The `matrix3x4_t` is converted to world position and world angles internally. Some of the other overloads are also inlined on a few games, making this the best choice. Since this call can only be used on classes inheriting `CBaseAnimating`, we check if the `DT_BaseAnimating` SendTable exists on the entity, throwing a native error if it doesn't. This safeguard could be greatly improved with a call to `CBaseEntity::GetBaseAnimating`, but would require more gamedata (maybe something to consider for the future?) --- extensions/sdktools/vnatives.cpp | 114 ++++++++++++++++++++ gamedata/sdktools.games/engine.csgo.txt | 22 ++++ gamedata/sdktools.games/engine.css.txt | 15 +++ gamedata/sdktools.games/engine.l4d.txt | 25 +++++ gamedata/sdktools.games/game.cstrike.txt | 6 ++ gamedata/sdktools.games/game.left4dead2.txt | 21 ++++ gamedata/sdktools.games/game.tf.txt | 21 ++++ plugins/include/sdktools_functions.inc | 22 ++++ 8 files changed, 246 insertions(+) diff --git a/extensions/sdktools/vnatives.cpp b/extensions/sdktools/vnatives.cpp index 1436d2d3..be0ce97f 100644 --- a/extensions/sdktools/vnatives.cpp +++ b/extensions/sdktools/vnatives.cpp @@ -53,6 +53,11 @@ SourceHook::List g_CallWraps; return pContext->ThrowNativeError("Entity %d (%d) is not a CBaseEntity", gamehelpers->ReferenceToIndex(ref), ref); \ } +#define SET_VECTOR(addr, vec) \ + addr[0] = sp_ftoc(vec.x); \ + addr[1] = sp_ftoc(vec.y); \ + addr[2] = sp_ftoc(vec.z); + inline void InitPass(ValvePassInfo &info, ValveType vtype, PassType type, unsigned int flags, unsigned int decflags=0) { info.decflags = decflags; @@ -1638,6 +1643,113 @@ static cell_t SetEntityOwner(IPluginContext *pContext, const cell_t *params) return 1; } +static cell_t LookupEntityAttachment(IPluginContext* pContext, const cell_t* params) +{ + CBaseEntity* pEntity; + ENTINDEX_TO_CBASEENTITY(params[1], pEntity); + + ServerClass* pClass = ((IServerUnknown*)pEntity)->GetNetworkable()->GetServerClass(); + if (!FindNestedDataTable(pClass->m_pTable, "DT_BaseAnimating")) + { + return pContext->ThrowNativeError("Entity %d (%d) is not a CBaseAnimating", gamehelpers->ReferenceToIndex(params[1]), params[1]); + } + + static ICallWrapper* pLookupAttachment = NULL; + if (!pLookupAttachment) + { + void* addr; + if (!g_pGameConf->GetMemSig("LookupAttachment", &addr)) + { + return pContext->ThrowNativeError("\"LookupAttachment\" not supported by this mod"); + } + + PassInfo retpass; + retpass.type = PassType_Basic; + retpass.flags = PASSFLAG_BYVAL; + retpass.size = sizeof(int); + + PassInfo pass[1]; + pass[0].type = PassType_Basic; + pass[0].flags = PASSFLAG_BYVAL; + pass[0].size = sizeof(char*); + + if (!(pLookupAttachment = g_pBinTools->CreateCall(addr, CallConv_ThisCall, &retpass, pass, 1))) + { + return pContext->ThrowNativeError("\"LookupAttachment\" wrapper failed to initialize"); + } + } + + char* buffer; + pContext->LocalToString(params[2], &buffer); + ArgBuffer vstk(pEntity, buffer); + + int ret; + pLookupAttachment->Execute(vstk, &ret); + + return ret; +} + +static cell_t GetEntityAttachment(IPluginContext* pContext, const cell_t* params) +{ + CBaseEntity* pEntity; + ENTINDEX_TO_CBASEENTITY(params[1], pEntity); + + ServerClass* pClass = ((IServerUnknown*)pEntity)->GetNetworkable()->GetServerClass(); + if (!FindNestedDataTable(pClass->m_pTable, "DT_BaseAnimating")) + { + return pContext->ThrowNativeError("Entity %d (%d) is not a CBaseAnimating", gamehelpers->ReferenceToIndex(params[1]), params[1]); + } + + static ICallWrapper* pGetAttachment = NULL; + if (!pGetAttachment) + { + int offset = -1; + if (!g_pGameConf->GetOffset("GetAttachment", &offset)) + { + return pContext->ThrowNativeError("\"GetAttachment\" not supported by this mod"); + } + + PassInfo retpass; + retpass.type = PassType_Basic; + retpass.flags = PASSFLAG_BYVAL; + retpass.size = sizeof(bool); + + PassInfo pass[2]; + pass[0].type = PassType_Basic; + pass[0].flags = PASSFLAG_BYVAL; + pass[0].size = sizeof(int); + pass[1].type = PassType_Basic; + pass[1].flags = PASSFLAG_BYVAL; + pass[1].size = sizeof(matrix3x4_t*); + + if (!(pGetAttachment = g_pBinTools->CreateVCall(offset, 0, 0, &retpass, pass, 2))) + { + return pContext->ThrowNativeError("\"GetAttachment\" wrapper failed to initialize"); + } + } + + matrix3x4_t attachmentToWorld; + ArgBuffer vstk(pEntity, params[2], &attachmentToWorld); + + bool ret; + pGetAttachment->Execute(vstk, &ret); + + // GetAttachment returns a matrix3x4_t but plugins can't do anything with this. + // Convert it to world origin and world angles. + QAngle absAngles; + Vector absOrigin; + MatrixAngles(attachmentToWorld, absAngles, absOrigin); + + cell_t* pOrigin, * pAngles; + pContext->LocalToPhysAddr(params[3], &pOrigin); + pContext->LocalToPhysAddr(params[4], &pAngles); + + SET_VECTOR(pOrigin, absOrigin); + SET_VECTOR(pAngles, absAngles); + + return ret; +} + sp_nativeinfo_t g_Natives[] = { {"ExtinguishEntity", ExtinguishEntity}, @@ -1672,5 +1784,7 @@ sp_nativeinfo_t g_Natives[] = {"SetEntityCollisionGroup", SetEntityCollisionGroup}, {"EntityCollisionRulesChanged", EntityCollisionRulesChanged}, {"SetEntityOwner", SetEntityOwner}, + {"LookupEntityAttachment", LookupEntityAttachment}, + {"GetEntityAttachment", GetEntityAttachment}, {NULL, NULL}, }; diff --git a/gamedata/sdktools.games/engine.csgo.txt b/gamedata/sdktools.games/engine.csgo.txt index 2ff919e5..a046f0a6 100644 --- a/gamedata/sdktools.games/engine.csgo.txt +++ b/gamedata/sdktools.games/engine.csgo.txt @@ -184,6 +184,21 @@ } } + /* CBaseAnimating::LookupAttachment */ + "#default" + { + "Signatures" + { + "LookupAttachment" + { + "library" "server" + "windows" "\x55\x8B\xEC\x57\x8B\xF9\x83\xBF\xC0\x04\x00\x00\x00\x75\x2A\xA1\x2A\x2A\x2A\x2A\x56\x8B\x30\x8B\x07\xFF\x50\x18\x8B\x0D\x2A\x2A\x2A\x2A\x50\xFF\x56\x04\x5E\x85\xC0\x74\x2A\x8B\xCF\xE8\x2A\x2A\x2A\x2A\x8B\x8F\xC0\x04\x00\x00\x5F\x85\xC9\x74\x2A\x83\x39\x00\x74\x2A\x8B\x55\x08\xE8\x2A\x2A\x2A\x2A" + "linux" "\x55\x89\xE5\x53\x83\xEC\x14\x8B\x5D\x08\x8B\x8B\xD8\x04\x00\x00\x85\xC9\x74\x2A\x8B\x83\xD8\x04\x00\x00\x85\xC0\x74\x2A\x8B\x10\x85\xD2\x74\x2A\x8B\x55\x0C\x89\x04\x24" + "linux64" "\x55\x48\x89\xE5\x53\x48\x89\xFB\x48\x83\xEC\x18\x48\x8B\xBF\x28\x06\x00\x00\x48\x85\xFF\x74\x2A\x48\x83\x3F\x00\x74\x2A\xE8\x2A\x2A\x2A\x2A" + } + } + } + /* SetUserInfo data */ "#default" { @@ -354,6 +369,13 @@ "linux64" "280" "mac64" "280" } + "GetAttachment" + { + "windows" "222" + "linux" "223" + "linux64" "223" + "mac64" "223" + } } "Signatures" { diff --git a/gamedata/sdktools.games/engine.css.txt b/gamedata/sdktools.games/engine.css.txt index 332753ba..e2c4c5c1 100644 --- a/gamedata/sdktools.games/engine.css.txt +++ b/gamedata/sdktools.games/engine.css.txt @@ -89,6 +89,21 @@ } } + /* CBaseAnimating::LookupAttachment */ + "#default" + { + "Signatures" + { + "LookupAttachment" + { + "library" "server" + "windows" "\x55\x8B\xEC\x56\x8B\xF1\x80\xBE\x31\x03\x00\x00\x00\x75\x2A\x83\xBE\x50\x04\x00\x00\x00\x75\x2A\xE8\x2A\x2A\x2A\x2A\x85\xC0\x74\x2A\x8B\xCE\xE8\x2A\x2A\x2A\x2A\x8B\x86\x50\x04\x00\x00\x85\xC0\x74\x2A\x83\x38\x00\x74\x2A\xFF\x75\x08\x50\xE8\x2A\x2A\x2A\x2A\x83\xC4\x08\x40" + "linux" "@_ZN14CBaseAnimating16LookupAttachmentEPKc" + "mac" "@_ZN14CBaseAnimating16LookupAttachmentEPKc" + } + } + } + /* SetUserInfo data */ "#default" { diff --git a/gamedata/sdktools.games/engine.l4d.txt b/gamedata/sdktools.games/engine.l4d.txt index 7918fc4f..87d4deaa 100644 --- a/gamedata/sdktools.games/engine.l4d.txt +++ b/gamedata/sdktools.games/engine.l4d.txt @@ -167,6 +167,25 @@ } } } + + /* CBaseAnimating::LookupAttachment */ + "#default" + { + "#supported" + { + "game" "left4dead" + } + "Signatures" + { + "LookupAttachment" + { + "library" "server" + "windows" "\x56\x8B\xF1\x83\xBE\x68\x04\x00\x00\x00\x75\x2A\xE8\x2A\x2A\x2A\x2A\x85\xC0\x74\x2A\x8B\xCE\xE8\x2A\x2A\x2A\x2A\x8B\x86\x68\x04\x00\x00\x85\xC0\x5E\x74\x2A\x83\x38\x00\x75\x2A\x33\xC0\xC2\x04\x00\x8B\x4C\x24\x04" + "linux" "@_ZN14CBaseAnimating16LookupAttachmentEPKc" + "mac" "@_ZN14CBaseAnimating16LookupAttachmentEPKc" + } + } + } /* SetUserInfo data */ "#default" @@ -306,6 +325,12 @@ "linux" "428" "mac" "428" } + "GetAttachment" + { + "windows" "202" + "linux" "203" + "mac" "203" + } } } } diff --git a/gamedata/sdktools.games/game.cstrike.txt b/gamedata/sdktools.games/game.cstrike.txt index 193f6901..923d16d7 100644 --- a/gamedata/sdktools.games/game.cstrike.txt +++ b/gamedata/sdktools.games/game.cstrike.txt @@ -123,6 +123,12 @@ "linux" "253" "mac" "253" } + "GetAttachment" + { + "windows" "205" + "linux" "206" + "mac" "206" + } } } } diff --git a/gamedata/sdktools.games/game.left4dead2.txt b/gamedata/sdktools.games/game.left4dead2.txt index 2de98d36..71fbc78c 100644 --- a/gamedata/sdktools.games/game.left4dead2.txt +++ b/gamedata/sdktools.games/game.left4dead2.txt @@ -79,6 +79,21 @@ } } + /* CBaseAnimating::LookupAttachment */ + "#default" + { + "Signatures" + { + "LookupAttachment" + { + "library" "server" + "windows" "\x55\x8B\xEC\x56\x8B\xF1\x83\xBE\xD0\x13\x00\x00\x00\x75\x2A\xE8\x2A\x2A\x2A\x2A\x85\xC0\x74\x2A\x8B\xCE\xE8\x2A\x2A\x2A\x2A\x8B\x86\xD0\x13\x00\x00\x5E\x85\xC0\x74\x2A\x83\x38\x00\x75\x2A\x33\xC0\x5D\xC2\x04\x00\x8B\x4D\x08" + "linux" "@_ZN14CBaseAnimating16LookupAttachmentEPKc" + "mac" "@_ZN14CBaseAnimating16LookupAttachmentEPKc" + } + } + } + /* IServer interface pointer */ "#default" { @@ -217,6 +232,12 @@ "linux" "275" "mac" "275" } + "GetAttachment" + { + "windows" "219" + "linux" "220" + "mac" "220" + } } "Keys" diff --git a/gamedata/sdktools.games/game.tf.txt b/gamedata/sdktools.games/game.tf.txt index 7fd71264..750946dc 100644 --- a/gamedata/sdktools.games/game.tf.txt +++ b/gamedata/sdktools.games/game.tf.txt @@ -112,6 +112,12 @@ "linux" "260" "mac" "260" } + "GetAttachment" + { + "windows" "212" + "linux" "213" + "mac" "213" + } } "Keys" @@ -135,4 +141,19 @@ } } } + + /* CBaseAnimating::LookupAttachment */ + "#default" + { + "Signatures" + { + "LookupAttachment" + { + "library" "server" + "windows" "\x55\x8B\xEC\x56\x8B\xF1\x80\xBE\x41\x03\x00\x00\x00\x75\x2A\x83\xBE\x6C\x04\x00\x00\x00\x75\x2A\xE8\x2A\x2A\x2A\x2A\x85\xC0\x74\x2A\x8B\xCE\xE8\x2A\x2A\x2A\x2A\x8B\x86\x6C\x04\x00\x00\x85\xC0\x74\x2A\x83\x38\x00\x74\x2A\xFF\x75\x08\x50\xE8\x2A\x2A\x2A\x2A\x83\xC4\x08\x40" + "linux" "@_ZN14CBaseAnimating16LookupAttachmentEPKc" + "mac" "@_ZN14CBaseAnimating16LookupAttachmentEPKc" + } + } + } } diff --git a/plugins/include/sdktools_functions.inc b/plugins/include/sdktools_functions.inc index 78ed2e6d..2255f6b3 100644 --- a/plugins/include/sdktools_functions.inc +++ b/plugins/include/sdktools_functions.inc @@ -377,3 +377,25 @@ native void EntityCollisionRulesChanged(int entity); * @error Invalid entity or lack of mod support. */ native void SetEntityOwner(int entity, int owner=INVALID_ENT_REFERENCE); + +/** + * Returns the index number of a given named attachment. + * + * @param entity The entity index. + * @param name The attachment name. + * @return An attachment index, or 0 if the attachment name is invalid or unused. + * @error Invalid entity or lack of mod support. + */ +native int LookupEntityAttachment(int entity, const char[] name); + +/** + * Returns the world location and world angles of an attachment. + * + * @param entity The entity index. + * @param attachment The attachment index. + * @param origin Destination vector to store the attachment's origin vector. + * @param angles Destination vector to store the attachment's position angle. + * @return True on success, otherwise false. + * @error Invalid entity or lack of mod support. + */ +native bool GetEntityAttachment(int entity, int attachment, float origin[3], float angles[3]);