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 <[email protected]> Co-authored-by: Kyle Sanderson <[email protected]>
This commit is contained in:
committed by
Your Name
co-authored by
Kenzzer
Kyle Sanderson
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\" 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},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user