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;
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
@ -74,4 +74,6 @@ void ShutdownHelpers();
|
||||
|
||||
bool FindNestedDataTable(SendTable *pTable, const char *name);
|
||||
|
||||
bool CollisionRulesChanged(CBaseEntity *pEntity);
|
||||
|
||||
#endif //_INCLUDE_SDKTOOLS_VHELPERS_H_
|
||||
|
@ -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\" not supported by this mod");
|
||||
return pContext->ThrowNativeError("\"SetEntityCollisionGroup\" Failed to retrieve m_CollisionGroup datamap on entity");
|
||||
}
|
||||
PassInfo pass[2];
|
||||
// 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\" unsupported mod");
|
||||
}
|
||||
}
|
||||
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},
|
||||
};
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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"
|
||||
{
|
||||
|
@ -27,6 +27,12 @@
|
||||
|
||||
"Offsets"
|
||||
{
|
||||
"SetOwnerEntity"
|
||||
{
|
||||
"windows" "17"
|
||||
"linux" "18"
|
||||
"mac" "18"
|
||||
}
|
||||
"GiveNamedItem"
|
||||
{
|
||||
"windows" "401"
|
||||
|
@ -120,6 +120,12 @@
|
||||
{
|
||||
"Offsets"
|
||||
{
|
||||
"SetOwnerEntity"
|
||||
{
|
||||
"windows" "19"
|
||||
"linux" "20"
|
||||
"mac" "20"
|
||||
}
|
||||
/* CTerrorPlayer::GiveNamedItem(char const*, int, bool, CBaseEntity*) */
|
||||
"GiveNamedItem"
|
||||
{
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user