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:
parent
5db571bbc6
commit
8c001872a8
@ -310,6 +310,99 @@ bool FindNestedDataTable(SendTable *pTable, const char *name)
|
|||||||
return false;
|
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)
|
char *UTIL_SendFlagsToString(int flags, int type)
|
||||||
{
|
{
|
||||||
static char str[1024];
|
static char str[1024];
|
||||||
|
@ -74,4 +74,6 @@ void ShutdownHelpers();
|
|||||||
|
|
||||||
bool FindNestedDataTable(SendTable *pTable, const char *name);
|
bool FindNestedDataTable(SendTable *pTable, const char *name);
|
||||||
|
|
||||||
|
bool CollisionRulesChanged(CBaseEntity *pEntity);
|
||||||
|
|
||||||
#endif //_INCLUDE_SDKTOOLS_VHELPERS_H_
|
#endif //_INCLUDE_SDKTOOLS_VHELPERS_H_
|
||||||
|
@ -1566,40 +1566,76 @@ static cell_t GivePlayerAmmo(IPluginContext *pContext, const cell_t *params)
|
|||||||
// SetEntityCollisionGroup(int entity, int collisionGroup)
|
// SetEntityCollisionGroup(int entity, int collisionGroup)
|
||||||
static cell_t SetEntityCollisionGroup(IPluginContext *pContext, const cell_t *params)
|
static cell_t SetEntityCollisionGroup(IPluginContext *pContext, const cell_t *params)
|
||||||
{
|
{
|
||||||
static ICallWrapper *pSetCollisionGroup = NULL;
|
CBaseEntity *pEntity;
|
||||||
if (!pSetCollisionGroup)
|
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;
|
return pContext->ThrowNativeError("\"SetEntityCollisionGroup\" Failed to retrieve m_CollisionGroup datamap on entity");
|
||||||
if (!g_pGameConf->GetMemSig("SetCollisionGroup", &addr) || !addr)
|
}
|
||||||
|
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].type = PassType_Basic;
|
||||||
pass[0].flags = PASSFLAG_BYVAL;
|
pass[0].flags = PASSFLAG_BYVAL;
|
||||||
pass[0].size = sizeof(CBaseEntity *);
|
pass[0].size = sizeof(CBaseEntity *);
|
||||||
|
|
||||||
// Collision Group
|
if (!(pSetOwnerEntity = g_pBinTools->CreateVCall(offset, 0, 0, nullptr, pass, 1)))
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return pContext->ThrowNativeError("\"SetEntityCollisionGroup\" wrapper failed to initialize");
|
return pContext->ThrowNativeError("\"SetOwnerEntity\" wrapper failed to initialize");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CBaseEntity *pEntity;
|
CBaseEntity *pNewOwner = gamehelpers->ReferenceToEntity(params[2]);
|
||||||
ENTINDEX_TO_CBASEENTITY(params[1], pEntity);
|
ArgBuffer<CBaseEntity *, CBaseEntity *> vstk(pEntity, pNewOwner);
|
||||||
|
pSetOwnerEntity->Execute(vstk, nullptr);
|
||||||
ArgBuffer<CBaseEntity *, int> vstk(pEntity, params[2]);
|
|
||||||
|
|
||||||
pSetCollisionGroup->Execute(vstk, nullptr);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sp_nativeinfo_t g_Natives[] =
|
sp_nativeinfo_t g_Natives[] =
|
||||||
@ -1634,5 +1670,7 @@ sp_nativeinfo_t g_Natives[] =
|
|||||||
{"GetPlayerResourceEntity", GetPlayerResourceEntity},
|
{"GetPlayerResourceEntity", GetPlayerResourceEntity},
|
||||||
{"GivePlayerAmmo", GivePlayerAmmo},
|
{"GivePlayerAmmo", GivePlayerAmmo},
|
||||||
{"SetEntityCollisionGroup", SetEntityCollisionGroup},
|
{"SetEntityCollisionGroup", SetEntityCollisionGroup},
|
||||||
|
{"EntityCollisionRulesChanged", EntityCollisionRulesChanged},
|
||||||
|
{"SetEntityOwner", SetEntityOwner},
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
@ -181,12 +181,6 @@
|
|||||||
"linux64" "\x55\x48\x89\xE5\x41\x57\x41\x56\x49\x89\xF6\x41\x55\x41\x54\x49\x89\xCC\x53"
|
"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"
|
"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"
|
"Offsets"
|
||||||
{
|
{
|
||||||
|
"SetOwnerEntity"
|
||||||
|
{
|
||||||
|
"windows" "19"
|
||||||
|
"linux" "20"
|
||||||
|
"linux64" "20"
|
||||||
|
"mac64" "20"
|
||||||
|
}
|
||||||
"GiveNamedItem"
|
"GiveNamedItem"
|
||||||
{
|
{
|
||||||
"windows" "458"
|
"windows" "458"
|
||||||
|
@ -86,12 +86,6 @@
|
|||||||
"linux" "@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f"
|
"linux" "@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f"
|
||||||
"mac" "@_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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 */
|
/* SetUserInfo data */
|
||||||
"#default"
|
"#default"
|
||||||
{
|
{
|
||||||
|
@ -27,6 +27,12 @@
|
|||||||
|
|
||||||
"Offsets"
|
"Offsets"
|
||||||
{
|
{
|
||||||
|
"SetOwnerEntity"
|
||||||
|
{
|
||||||
|
"windows" "17"
|
||||||
|
"linux" "18"
|
||||||
|
"mac" "18"
|
||||||
|
}
|
||||||
"GiveNamedItem"
|
"GiveNamedItem"
|
||||||
{
|
{
|
||||||
"windows" "401"
|
"windows" "401"
|
||||||
|
@ -120,6 +120,12 @@
|
|||||||
{
|
{
|
||||||
"Offsets"
|
"Offsets"
|
||||||
{
|
{
|
||||||
|
"SetOwnerEntity"
|
||||||
|
{
|
||||||
|
"windows" "19"
|
||||||
|
"linux" "20"
|
||||||
|
"mac" "20"
|
||||||
|
}
|
||||||
/* CTerrorPlayer::GiveNamedItem(char const*, int, bool, CBaseEntity*) */
|
/* CTerrorPlayer::GiveNamedItem(char const*, int, bool, CBaseEntity*) */
|
||||||
"GiveNamedItem"
|
"GiveNamedItem"
|
||||||
{
|
{
|
||||||
|
@ -16,6 +16,12 @@
|
|||||||
{
|
{
|
||||||
"Offsets"
|
"Offsets"
|
||||||
{
|
{
|
||||||
|
"SetOwnerEntity"
|
||||||
|
{
|
||||||
|
"windows" "17"
|
||||||
|
"linux" "18"
|
||||||
|
"mac" "18"
|
||||||
|
}
|
||||||
"GiveNamedItem"
|
"GiveNamedItem"
|
||||||
{
|
{
|
||||||
"windows" "408"
|
"windows" "408"
|
||||||
@ -127,12 +133,6 @@
|
|||||||
"linux" "@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f"
|
"linux" "@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f"
|
||||||
"mac" "@_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"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -360,3 +360,20 @@ native int GivePlayerAmmo(int client, int amount, int ammotype, bool suppressSou
|
|||||||
* @error Invalid entity or lack of mod support.
|
* @error Invalid entity or lack of mod support.
|
||||||
*/
|
*/
|
||||||
native void SetEntityCollisionGroup(int entity, int collisionGroup);
|
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);
|
||||||
|
Loading…
Reference in New Issue
Block a user