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:
Benoist
2021-11-21 23:03:35 -08:00
committed by GitHub
co-authored by Kenzzer Kyle Sanderson
parent 4e4c2a7bb0
commit b38c9824fe
10 changed files with 196 additions and 54 deletions
+93
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];
+2
View File
@@ -74,4 +74,6 @@ void ShutdownHelpers();
bool FindNestedDataTable(SendTable *pTable, const char *name);
bool CollisionRulesChanged(CBaseEntity *pEntity);
#endif //_INCLUDE_SDKTOOLS_VHELPERS_H_
+59 -21
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},
};