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

* <dvander> try again

* sdktools: add default Param for owner.

Co-authored-by: Kenzzer <kenzzer@users.noreply.github.com>
Co-authored-by: Kyle Sanderson <kyle.leet@gmail.com>
This commit is contained in:
Benoist 2021-11-22 08:03:35 +01:00 committed by GitHub
parent 4e4c2a7bb0
commit b38c9824fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 196 additions and 54 deletions

View File

@ -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<void***>(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<VEmptyClass*>(pEntity)->*u.mfpnew)(oldOwner);
return true;
}
char *UTIL_SendFlagsToString(int flags, int type)
{
static char str[1024];

View File

@ -74,4 +74,6 @@ void ShutdownHelpers();
bool FindNestedDataTable(SendTable *pTable, const char *name);
bool CollisionRulesChanged(CBaseEntity *pEntity);
#endif //_INCLUDE_SDKTOOLS_VHELPERS_H_

View File

@ -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<CBaseEntity *, int> vstk(pEntity, params[2]);
pSetCollisionGroup->Execute(vstk, nullptr);
CBaseEntity *pNewOwner = gamehelpers->ReferenceToEntity(params[2]);
ArgBuffer<CBaseEntity *, CBaseEntity *> 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},
};

View File

@ -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"

View File

@ -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"
}
}
}

View File

@ -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"
{

View File

@ -27,6 +27,12 @@
"Offsets"
{
"SetOwnerEntity"
{
"windows" "17"
"linux" "18"
"mac" "18"
}
"GiveNamedItem"
{
"windows" "401"

View File

@ -120,6 +120,12 @@
{
"Offsets"
{
"SetOwnerEntity"
{
"windows" "19"
"linux" "20"
"mac" "20"
}
/* CTerrorPlayer::GiveNamedItem(char const*, int, bool, CBaseEntity*) */
"GiveNamedItem"
{

View File

@ -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"
}
}
}
}

View File

@ -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);