From 29d118743446c2116d36ddb543f8673b8836944d Mon Sep 17 00:00:00 2001 From: Nicholas Hastings Date: Thu, 6 Jul 2017 13:33:34 -0400 Subject: [PATCH] Cache created entities by reference instead of index in SDKHooks (#634) Fixes #663. --- extensions/sdkhooks/extension.cpp | 55 ++++++++++++++++++++----------- extensions/sdkhooks/extension.h | 9 +++-- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/extensions/sdkhooks/extension.cpp b/extensions/sdkhooks/extension.cpp index c3fbf456..f7a64370 100644 --- a/extensions/sdkhooks/extension.cpp +++ b/extensions/sdkhooks/extension.cpp @@ -103,8 +103,6 @@ SMEXT_LINK(&g_Interface); CGlobalVars *gpGlobals; ke::Vector g_HookList[SDKHook_MAXHOOKS]; -CBitVec m_EntityExists; - IBinTools *g_pBinTools = NULL; ICvar *icvar = NULL; @@ -231,6 +229,8 @@ bool SDKHooks::SDK_OnLoad(char *error, size_t maxlength, bool late) return false; } + memset(m_EntityCache, INVALID_EHANDLE_INDEX, sizeof(m_EntityCache)); + CUtlVector *entListeners = EntListeners(); if (!entListeners) { @@ -270,13 +270,20 @@ bool SDKHooks::SDK_OnLoad(char *error, size_t maxlength, bool late) continue; index = hndl.GetEntryIndex(); - m_EntityExists.Set(index); + if (IsEntityIndexInRange(index)) + { + m_EntityCache[index] = gamehelpers->IndexToReference(index); + } + else + { + g_pSM->LogError(myself, "SDKHooks::HandleEntityCreated - Got entity index out of range (%d)", index); + } } #else for (int i = 0; i < NUM_ENT_ENTRIES; i++) { if (gamehelpers->ReferenceToEntity(i) != NULL) - m_EntityExists.Set(i); + m_EntityCache[i] = gamehelpers->IndexToReference(i); } #endif @@ -411,14 +418,14 @@ void SDKHooks::OnClientPutInServer(int client) { CBaseEntity *pPlayer = gamehelpers->ReferenceToEntity(client); - HandleEntityCreated(pPlayer, client); + HandleEntityCreated(pPlayer, client, gamehelpers->EntityToReference(pPlayer)); } void SDKHooks::OnClientDisconnecting(int client) { CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(client); - HandleEntityDeleted(pEntity, client); + HandleEntityDeleted(pEntity); } void SDKHooks::AddEntityListener(ISMEntityListener *listener) @@ -849,16 +856,26 @@ void SDKHooks::Unhook(int entity, SDKHookType type, IPluginFunction *pCallback) void SDKHooks::OnEntityCreated(CBaseEntity *pEntity) { // Call OnEntityCreated forward - int ref = gamehelpers->EntityToBCompatRef(pEntity); + int ref = gamehelpers->EntityToReference(pEntity); int index = gamehelpers->ReferenceToIndex(ref); // This can be -1 for player ents before any players have connected - if ((unsigned)index == INVALID_EHANDLE_INDEX || m_EntityExists.IsBitSet(index) || (index > 0 && index <= playerhelpers->GetMaxClients())) + if ((unsigned)index == INVALID_EHANDLE_INDEX || (index > 0 && index <= playerhelpers->GetMaxClients())) { return; } - HandleEntityCreated(pEntity, ref); + if (!IsEntityIndexInRange(index)) + { + g_pSM->LogError(myself, "SDKHooks::OnEntityCreated - Got entity index out of range (%d)", index); + return; + } + + // The entity could already exist. The creation notifier fires twice for some paths + if (m_EntityCache[index] != ref) + { + HandleEntityCreated(pEntity, index, ref); + } } #ifdef GAMEDESC_CAN_CHANGE @@ -1633,8 +1650,7 @@ void SDKHooks::Hook_UsePost(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_T void SDKHooks::OnEntityDeleted(CBaseEntity *pEntity) { - int ref = gamehelpers->EntityToBCompatRef(pEntity); - int index = gamehelpers->ReferenceToIndex(ref); + int index = gamehelpers->ReferenceToIndex(gamehelpers->EntityToReference(pEntity)); // This can be -1 for player ents before any players have connected if ((unsigned)index == INVALID_EHANDLE_INDEX || (index > 0 && index <= playerhelpers->GetMaxClients())) @@ -1642,7 +1658,7 @@ void SDKHooks::OnEntityDeleted(CBaseEntity *pEntity) return; } - HandleEntityDeleted(pEntity, ref); + HandleEntityDeleted(pEntity); } void SDKHooks::Hook_VPhysicsUpdate(IPhysicsObject *pPhysics) @@ -1752,9 +1768,10 @@ bool SDKHooks::Hook_WeaponSwitchPost(CBaseCombatWeapon *pWeapon, int viewmodelin RETURN_META_VALUE(MRES_IGNORED, true); } -void SDKHooks::HandleEntityCreated(CBaseEntity *pEntity, int ref) +void SDKHooks::HandleEntityCreated(CBaseEntity *pEntity, int index, cell_t ref) { const char *pName = gamehelpers->GetEntityClassname(pEntity); + cell_t bcompatRef = gamehelpers->EntityToBCompatRef(pEntity); // Send OnEntityCreated to SM listeners SourceHook::List::iterator iter; @@ -1766,15 +1783,17 @@ void SDKHooks::HandleEntityCreated(CBaseEntity *pEntity, int ref) } // Call OnEntityCreated forward - g_pOnEntityCreated->PushCell(ref); + g_pOnEntityCreated->PushCell(bcompatRef); g_pOnEntityCreated->PushString(pName ? pName : ""); g_pOnEntityCreated->Execute(NULL); - m_EntityExists.Set(gamehelpers->ReferenceToIndex(ref)); + m_EntityCache[index] = ref; } -void SDKHooks::HandleEntityDeleted(CBaseEntity *pEntity, int ref) +void SDKHooks::HandleEntityDeleted(CBaseEntity *pEntity) { + cell_t bcompatRef = gamehelpers->EntityToBCompatRef(pEntity); + // Send OnEntityDestroyed to SM listeners SourceHook::List::iterator iter; ISMEntityListener *pListener = NULL; @@ -1785,10 +1804,8 @@ void SDKHooks::HandleEntityDeleted(CBaseEntity *pEntity, int ref) } // Call OnEntityDestroyed forward - g_pOnEntityDestroyed->PushCell(ref); + g_pOnEntityDestroyed->PushCell(bcompatRef); g_pOnEntityDestroyed->Execute(NULL); Unhook(pEntity); - - m_EntityExists.Set(gamehelpers->ReferenceToIndex(ref), false); } diff --git a/extensions/sdkhooks/extension.h b/extensions/sdkhooks/extension.h index cc8c3903..1bd7267b 100644 --- a/extensions/sdkhooks/extension.h +++ b/extensions/sdkhooks/extension.h @@ -311,7 +311,6 @@ public: void Hook_TraceAttack(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr); void Hook_TraceAttackPost(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr); #endif - void Hook_UpdateOnRemove(); void Hook_Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); void Hook_UsePost(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value); void Hook_VPhysicsUpdate(IPhysicsObject *pPhysics); @@ -330,14 +329,18 @@ public: bool Hook_WeaponSwitchPost(CBaseCombatWeapon *pWeapon, int viewmodelindex); private: - void HandleEntityCreated(CBaseEntity *pEntity, int ref); - void HandleEntityDeleted(CBaseEntity *pEntity, int ref); + void HandleEntityCreated(CBaseEntity *pEntity, int index, cell_t ref); + void HandleEntityDeleted(CBaseEntity *pEntity); void Unhook(CBaseEntity *pEntity); void Unhook(IPluginContext *pContext); private: int HandleOnTakeDamageHook(CTakeDamageInfoHack &info, SDKHookType hookType); int HandleOnTakeDamageHookPost(CTakeDamageInfoHack &info, SDKHookType hookType); + +private: + inline bool IsEntityIndexInRange(int i) { return i >= 0 && i < NUM_ENT_ENTRIES; } + cell_t m_EntityCache[NUM_ENT_ENTRIES]; }; extern CGlobalVars *gpGlobals;