From b38c9824fe158c7291619b1da786c981f796aa92 Mon Sep 17 00:00:00 2001 From: Benoist Date: Mon, 22 Nov 2021 08:03:35 +0100 Subject: [PATCH] sdktools: Add EntityCollisionRulesChanged & SetEntityOwner natives (#1620) * Add EntityCollisionRulesChanged & SetEntityOwner natives * fix win build, and unpushed changes * Fixed bad world loop * Requested changes + csgo offsets * small copy paste mistake * Strip the debug log lines * Tiny clean up in comments * line * try again * sdktools: add default Param for owner. Co-authored-by: Kenzzer Co-authored-by: Kyle Sanderson --- extensions/sdktools/vhelpers.cpp | 93 +++++++++++++++++++++ extensions/sdktools/vhelpers.h | 2 + extensions/sdktools/vnatives.cpp | 80 +++++++++++++----- gamedata/sdktools.games/engine.csgo.txt | 13 +-- gamedata/sdktools.games/engine.css.txt | 6 -- gamedata/sdktools.games/engine.l4d2.txt | 15 ---- gamedata/sdktools.games/game.cstrike.txt | 6 ++ gamedata/sdktools.games/game.left4dead2.txt | 6 ++ gamedata/sdktools.games/game.tf.txt | 12 +-- plugins/include/sdktools_functions.inc | 17 ++++ 10 files changed, 196 insertions(+), 54 deletions(-) diff --git a/extensions/sdktools/vhelpers.cpp b/extensions/sdktools/vhelpers.cpp index 3a463e02..13e35afa 100644 --- a/extensions/sdktools/vhelpers.cpp +++ b/extensions/sdktools/vhelpers.cpp @@ -310,6 +310,99 @@ bool FindNestedDataTable(SendTable *pTable, const char *name) return false; } +// https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/mp/src/game/server/baseentity.cpp#L3396L3404 +// CBaseEntity::SetOwnerEntity offers a more or less direct access to CBaseEntity::CollisionRulesChanged +// The function will return false if something went wrong during call setup/game is unsupported +class VEmptyClass {}; +bool CollisionRulesChanged(CBaseEntity *pEntity) +{ + // CBaseEntity::SetOwnerEntity is a virtual function, and while not many classes override it + // Only CNodeEnt, as confirmed by a valve comment + // https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/mp/src/game/server/baseentity.h#L493 + // In order to keep consitent behaviour across all entities, including CNodeEnt and potential source games that have entity classes overriding this function. + // We are going to fetch the world entity, which doesn't have this function overriden (on all source games hopefully), and obtain the function address + static void *func = nullptr; + static int offsethOwnerEntity = -1; + if (func == nullptr) + { + int offset = -1; + if (!g_pGameConf->GetOffset("SetOwnerEntity", &offset)) + { + return false; + } + +#if SOURCE_ENGINE < SE_ORANGEBOX + CBaseEntity *pWorldEntity = nullptr; + for (int i = 0; i < gpGlobals->maxEntities && pWorldEntity == nullptr; ++i) + { + pWorldEntity = gamehelpers->ReferenceToEntity(i); + } +#else + CBaseEntity *pWorldEntity = ((IServerUnknown *)servertools->FirstEntity())->GetBaseEntity(); +#endif + // Couldn't find the world (what) + if (pWorldEntity == nullptr) + { + return false; + } + + // Retrieve m_hOwnerEntity offset + sm_datatable_info_t offset_data_info; + datamap_t *offsetMap = gamehelpers->GetDataMap(pWorldEntity); + if (gamehelpers->FindDataMapInfo(offsetMap, "m_hOwnerEntity", &offset_data_info)) + { + offsethOwnerEntity = offset_data_info.actual_offset; + } + + if (offsethOwnerEntity == -1) + { + // Well... + return false; + } + + // Hopefully the world vtable... + void **world_vtable = *reinterpret_cast(pWorldEntity); + // Hopefully CBaseEntity::SetOwnerEntity and not an overriden function... + func = world_vtable[offset]; + } + + // Build our member function ptr + union + { + void (VEmptyClass::*mfpnew)(CBaseEntity *pOwner); +#ifndef PLATFORM_POSIX + void *addr; + } u; + u.addr = func; +#else + struct + { + void *addr; + intptr_t adjustor; + } s; + } u; + u.s.addr = func; + u.s.adjustor = 0; +#endif + // Retrieve m_hOwnerEntity + CBaseHandle *hndl = (CBaseHandle *)((uint8_t *)pEntity + offsethOwnerEntity); + CBaseEntity *oldOwner = gamehelpers->ReferenceToEntity(hndl->GetEntryIndex()); + + // Now change the owner to something else, so we fall through the if statement + // when calling CBaseEntity::SetOwnerEntity and only end up calling CBaseEntity::CollisionRulesChanged + if (oldOwner) + { + hndl->Set(nullptr); + } + else + { + hndl->Set((IHandleEntity *)pEntity); + } + + (reinterpret_cast(pEntity)->*u.mfpnew)(oldOwner); + return true; +} + char *UTIL_SendFlagsToString(int flags, int type) { static char str[1024]; diff --git a/extensions/sdktools/vhelpers.h b/extensions/sdktools/vhelpers.h index ad092ea0..47180b95 100644 --- a/extensions/sdktools/vhelpers.h +++ b/extensions/sdktools/vhelpers.h @@ -74,4 +74,6 @@ void ShutdownHelpers(); bool FindNestedDataTable(SendTable *pTable, const char *name); +bool CollisionRulesChanged(CBaseEntity *pEntity); + #endif //_INCLUDE_SDKTOOLS_VHELPERS_H_ diff --git a/extensions/sdktools/vnatives.cpp b/extensions/sdktools/vnatives.cpp index fe80bd7e..1436d2d3 100644 --- a/extensions/sdktools/vnatives.cpp +++ b/extensions/sdktools/vnatives.cpp @@ -1566,40 +1566,76 @@ static cell_t GivePlayerAmmo(IPluginContext *pContext, const cell_t *params) // SetEntityCollisionGroup(int entity, int collisionGroup) static cell_t SetEntityCollisionGroup(IPluginContext *pContext, const cell_t *params) { - static ICallWrapper *pSetCollisionGroup = NULL; - if (!pSetCollisionGroup) + CBaseEntity *pEntity; + ENTINDEX_TO_CBASEENTITY(params[1], pEntity); + + int offsetCollisionGroup = -1; + // Retrieve m_hOwnerEntity offset + sm_datatable_info_t offset_data_info; + datamap_t *offsetMap = gamehelpers->GetDataMap(pEntity); + if (!offsetMap || !gamehelpers->FindDataMapInfo(offsetMap, "m_CollisionGroup", &offset_data_info)) { - void *addr; - if (!g_pGameConf->GetMemSig("SetCollisionGroup", &addr) || !addr) + return pContext->ThrowNativeError("\"SetEntityCollisionGroup\" Failed to retrieve m_CollisionGroup datamap on entity"); + } + offsetCollisionGroup = offset_data_info.actual_offset; + + // Reimplementation of CBaseEntity::SetCollisionGroup + // https://github.com/ValveSoftware/source-sdk-2013/blob/0d8dceea4310fde5706b3ce1c70609d72a38efdf/sp/src/game/shared/baseentity_shared.cpp#L2477L2484 + int *collisionGroup = (int *)((uint8_t *)pEntity + offsetCollisionGroup); + if ((*collisionGroup) != params[2]) + { + *collisionGroup = params[2]; + // Returns false if CollisionRulesChanged hack isn't supported for this mod + if (!CollisionRulesChanged(pEntity)) { - return pContext->ThrowNativeError("\"SetEntityCollisionGroup\" not supported by this mod"); + return pContext->ThrowNativeError("\"SetEntityCollisionGroup\" unsupported mod"); } - PassInfo pass[2]; - // Entity + } + return 1; +} + +static cell_t EntityCollisionRulesChanged(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + ENTINDEX_TO_CBASEENTITY(params[1], pEntity); + // Returns false if CollisionRulesChanged hack isn't supported for this mod + if (!CollisionRulesChanged(pEntity)) + { + return pContext->ThrowNativeError("\"EntityCollisionRulesChanged\" unsupported mod"); + } + return 1; +} + +static cell_t SetEntityOwner(IPluginContext *pContext, const cell_t *params) +{ + CBaseEntity *pEntity; + ENTINDEX_TO_CBASEENTITY(params[1], pEntity); + + static ICallWrapper *pSetOwnerEntity = NULL; + if (!pSetOwnerEntity) + { + int offset = -1; + if (!g_pGameConf->GetOffset("SetOwnerEntity", &offset)) + { + return pContext->ThrowNativeError("\"SetOwnerEntity\" not supported by this mod"); + } + + PassInfo pass[1]; pass[0].type = PassType_Basic; pass[0].flags = PASSFLAG_BYVAL; pass[0].size = sizeof(CBaseEntity *); - // Collision Group - pass[1].type = PassType_Basic; - pass[1].flags = PASSFLAG_BYVAL; - pass[1].size = sizeof(int); - - if (!(pSetCollisionGroup = g_pBinTools->CreateCall(addr, CallConv_ThisCall, NULL, pass, 2))) + if (!(pSetOwnerEntity = g_pBinTools->CreateVCall(offset, 0, 0, nullptr, pass, 1))) { - return pContext->ThrowNativeError("\"SetEntityCollisionGroup\" wrapper failed to initialize"); + return pContext->ThrowNativeError("\"SetOwnerEntity\" wrapper failed to initialize"); } } - CBaseEntity *pEntity; - ENTINDEX_TO_CBASEENTITY(params[1], pEntity); - - ArgBuffer vstk(pEntity, params[2]); - - pSetCollisionGroup->Execute(vstk, nullptr); + CBaseEntity *pNewOwner = gamehelpers->ReferenceToEntity(params[2]); + ArgBuffer vstk(pEntity, pNewOwner); + pSetOwnerEntity->Execute(vstk, nullptr); return 1; - } sp_nativeinfo_t g_Natives[] = @@ -1634,5 +1670,7 @@ sp_nativeinfo_t g_Natives[] = {"GetPlayerResourceEntity", GetPlayerResourceEntity}, {"GivePlayerAmmo", GivePlayerAmmo}, {"SetEntityCollisionGroup", SetEntityCollisionGroup}, + {"EntityCollisionRulesChanged", EntityCollisionRulesChanged}, + {"SetEntityOwner", SetEntityOwner}, {NULL, NULL}, }; diff --git a/gamedata/sdktools.games/engine.csgo.txt b/gamedata/sdktools.games/engine.csgo.txt index 71c4f0a6..2ff919e5 100644 --- a/gamedata/sdktools.games/engine.csgo.txt +++ b/gamedata/sdktools.games/engine.csgo.txt @@ -181,12 +181,6 @@ "linux64" "\x55\x48\x89\xE5\x41\x57\x41\x56\x49\x89\xF6\x41\x55\x41\x54\x49\x89\xCC\x53" "mac64" "\x55\x48\x89\xE5\x41\x57\x41\x56\x41\x55\x41\x54\x53\x48\x81\xEC\x88\x01\x00\x00\xF3\x0F\x11\x85\x8C\xFE\xFF\xFF" } - "SetCollisionGroup" - { - "library" "server" - "windows" "\x55\x8B\xEC\x53\x8B\xD9\x56\x57\x8B\x7D\x08\x39\xBB\x54\x01\x00\x00\x74\x40\x80\x79\x58\x00\x74" - "linux" "\x55\x89\xE5\x83\xEC\x18\x89\x5D\xF8\x8B\x5D\x08\x89\x75\xFC\x8B\x75\x0C\x39\xB3\x5C\x01\x00\x00" - } } } @@ -248,6 +242,13 @@ } "Offsets" { + "SetOwnerEntity" + { + "windows" "19" + "linux" "20" + "linux64" "20" + "mac64" "20" + } "GiveNamedItem" { "windows" "458" diff --git a/gamedata/sdktools.games/engine.css.txt b/gamedata/sdktools.games/engine.css.txt index 1e906ce2..332753ba 100644 --- a/gamedata/sdktools.games/engine.css.txt +++ b/gamedata/sdktools.games/engine.css.txt @@ -86,12 +86,6 @@ "linux" "@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f" "mac" "@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f" } - "SetCollisionGroup" - { - "library" "server" - "windows" "\x55\x8b\xec\x53\x8b\x5d\x08\x56\x57\x8b\xf9\x39\x9f\xe0\x01\x00\x00\x74\x4f\x8b" - "linux" "@_ZN11CBaseEntity17SetCollisionGroupEi" - } } } diff --git a/gamedata/sdktools.games/engine.l4d2.txt b/gamedata/sdktools.games/engine.l4d2.txt index 40c60d32..d3a28af8 100644 --- a/gamedata/sdktools.games/engine.l4d2.txt +++ b/gamedata/sdktools.games/engine.l4d2.txt @@ -37,21 +37,6 @@ } } - /* CBaseEntity::SetCollisionGroup */ - "#default" - { - "Signatures" - { - "SetCollisionGroup" - { - "library" "server" - "windows" "\x55\x8B\xEC\x56\x57\x8B\x7D\x08\x8B\xF1\x39\xBE\x2A\x2A\x00\x00\x74\x33" - "linux" "@_ZN11CBaseEntity17SetCollisionGroupEi" - /* Windows found by string "Warning, funcladder with blocked bottom point" in CFuncLadder::Spawn, this function has XRef to */ - } - } - } - /* SetUserInfo data */ "#default" { diff --git a/gamedata/sdktools.games/game.cstrike.txt b/gamedata/sdktools.games/game.cstrike.txt index ff77ee50..193f6901 100644 --- a/gamedata/sdktools.games/game.cstrike.txt +++ b/gamedata/sdktools.games/game.cstrike.txt @@ -27,6 +27,12 @@ "Offsets" { + "SetOwnerEntity" + { + "windows" "17" + "linux" "18" + "mac" "18" + } "GiveNamedItem" { "windows" "401" diff --git a/gamedata/sdktools.games/game.left4dead2.txt b/gamedata/sdktools.games/game.left4dead2.txt index 6a732afb..2de98d36 100644 --- a/gamedata/sdktools.games/game.left4dead2.txt +++ b/gamedata/sdktools.games/game.left4dead2.txt @@ -120,6 +120,12 @@ { "Offsets" { + "SetOwnerEntity" + { + "windows" "19" + "linux" "20" + "mac" "20" + } /* CTerrorPlayer::GiveNamedItem(char const*, int, bool, CBaseEntity*) */ "GiveNamedItem" { diff --git a/gamedata/sdktools.games/game.tf.txt b/gamedata/sdktools.games/game.tf.txt index b3d38f80..7fd71264 100644 --- a/gamedata/sdktools.games/game.tf.txt +++ b/gamedata/sdktools.games/game.tf.txt @@ -16,6 +16,12 @@ { "Offsets" { + "SetOwnerEntity" + { + "windows" "17" + "linux" "18" + "mac" "18" + } "GiveNamedItem" { "windows" "408" @@ -127,12 +133,6 @@ "linux" "@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f" "mac" "@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f" } - "SetCollisionGroup" - { - "library" "server" - "windows" "\x55\x8B\xEC\x53\x8B\x5D\x08\x56\x57\x8B\xF9\x39\x9F\xF0\x01\x00\x00" - "linux" "@_ZN11CBaseEntity17SetCollisionGroupEi" - } } } } diff --git a/plugins/include/sdktools_functions.inc b/plugins/include/sdktools_functions.inc index 3f6f0f71..78ed2e6d 100644 --- a/plugins/include/sdktools_functions.inc +++ b/plugins/include/sdktools_functions.inc @@ -360,3 +360,20 @@ native int GivePlayerAmmo(int client, int amount, int ammotype, bool suppressSou * @error Invalid entity or lack of mod support. */ native void SetEntityCollisionGroup(int entity, int collisionGroup); + +/** + * Recaculates entity collision rules (CBaseEntity::CollisionRulesChanged). + * + * @param entity The entity index. + * @error Invalid entity or lack of mod support. + */ +native void EntityCollisionRulesChanged(int entity); + +/** + * Sets an entity's owner (CBaseEntity::SetEntityOwner). + * + * @param entity The entity index. + * @param owner The owner entity index, can be invalid. + * @error Invalid entity or lack of mod support. + */ +native void SetEntityOwner(int entity, int owner=INVALID_ENT_REFERENCE);