diff --git a/extensions/tf2/criticals.cpp b/extensions/tf2/criticals.cpp index f072cc36..cf7252bb 100644 --- a/extensions/tf2/criticals.cpp +++ b/extensions/tf2/criticals.cpp @@ -30,217 +30,171 @@ */ #include "criticals.h" +#include "util.h" -IServerGameEnts *gameents = NULL; - -CDetour *calcIsAttackCriticalDetour = NULL; -CDetour *calcIsAttackCriticalMeleeDetour = NULL; -CDetour *calcIsAttackCriticalBowDetour = NULL; +CritManager g_CritManager; IForward *g_critForward = NULL; -enum DetourResult +SH_DECL_MANUALHOOK0(CalcIsAttackCriticalHelper, 0, 0, 0, bool); +SH_DECL_MANUALHOOK0(CalcIsAttackCriticalHelperNoCrits, 0, 0, 0, bool); + +const char TF_WEAPON_DATATABLE[] = "DT_TFWeaponBase"; + +CritManager::CritManager() : + m_enabled(false), + m_hooksSetup(false) { - Result_Ignore, - Result_NoCrit, - Result_Crit, -}; - -int CheckBaseHandle(CBaseHandle &hndl) -{ - if (!hndl.IsValid()) - { - return -1; - } - - int index = hndl.GetEntryIndex(); - - edict_t *pStoredEdict; - - pStoredEdict = engine->PEntityOfEntIndex(index); - - if (pStoredEdict == NULL) - { - return -1; - } - - IServerEntity *pSE = pStoredEdict->GetIServerEntity(); - - if (pSE == NULL) - { - return -1; - } - - if (pSE->GetRefEHandle() != hndl) - { - return -1; - } - - return index; + m_entsHooked.Init(); } -DetourResult DetourCallback(CBaseEntity *pEnt) +bool CritManager::TryEnable() { - edict_t *pEdict = gameents->BaseEntityToEdict((CBaseEntity *)pEnt); - - if (!pEdict) + if (!m_hooksSetup) { - g_pSM->LogMessage(myself, "Entity Error"); - return Result_Ignore; + int offset; + + if (!g_pGameConf->GetOffset("CalcIsAttackCriticalHelper", &offset)) + { + g_pSM->LogError(myself, "Failed to find CalcIsAttackCriticalHelper offset"); + return false; + } + + SH_MANUALHOOK_RECONFIGURE(CalcIsAttackCriticalHelper, offset, 0, 0); + + if (!g_pGameConf->GetOffset("CalcIsAttackCriticalHelperNoCrits", &offset)) + { + g_pSM->LogError(myself, "Failed to find CalcIsAttackCriticalHelperNoCrits offset"); + return false; + } + + SH_MANUALHOOK_RECONFIGURE(CalcIsAttackCriticalHelperNoCrits, offset, 0, 0); + + m_hooksSetup = true; + } + + for (size_t i = playerhelpers->GetMaxClients() + 1; i < MAX_EDICTS; ++i) + { + CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i); + if (pEntity == NULL) + continue; + + IServerUnknown *pUnknown = (IServerUnknown *)pEntity; + IServerNetworkable *pNetworkable = pUnknown->GetNetworkable(); + if (!pNetworkable) + continue; + + if (!UTIL_ContainsDataTable(pNetworkable->GetServerClass()->m_pTable, TF_WEAPON_DATATABLE)) + continue; + + SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); + SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); + + m_entsHooked.Set(i); + } + + m_enabled = true; + + return true; +} + +void CritManager::Disable() +{ + int i = m_entsHooked.FindNextSetBit(playerhelpers->GetMaxClients() + 1); + for (i; i != -1; i = m_entsHooked.FindNextSetBit(i)) + { + CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(i); + SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); + SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); + + m_entsHooked.Set(i, false); + } + + m_enabled = false; +} + +void CritManager::OnEntityCreated(CBaseEntity *pEntity, const char *classname) +{ + if (!m_enabled) + return; + + IServerUnknown *pUnknown = (IServerUnknown *)pEntity; + IServerNetworkable *pNetworkable = pUnknown->GetNetworkable(); + if (!pNetworkable) + return; + + if (!UTIL_ContainsDataTable(pNetworkable->GetServerClass()->m_pTable, TF_WEAPON_DATATABLE)) + return; + + SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); + SH_ADD_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); + + m_entsHooked.Set(gamehelpers->EntityToBCompatRef(pEntity)); +} + +void CritManager::OnEntityDestroyed(CBaseEntity *pEntity) +{ + if (!m_enabled) + return; + + int index = gamehelpers->EntityToBCompatRef(pEntity); + if (index < 0 || index >= MAX_EDICTS) + return; + + if (!m_entsHooked.IsBitSet(index)) + return; + + SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelper, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); + SH_REMOVE_MANUALHOOK(CalcIsAttackCriticalHelperNoCrits, pEntity, SH_MEMBER(&g_CritManager, &CritManager::Hook_CalcIsAttackCriticalHelpers), false); + + m_entsHooked.Set(index, false); +} + +bool CritManager::Hook_CalcIsAttackCriticalHelpers() +{ + CBaseEntity *pWeapon = META_IFACEPTR(CBaseEntity); + + // If there's an invalid ent or invalid networkable here, we've got issues elsewhere. + + IServerNetworkable *pNetWeapon = ((IServerUnknown *)pWeapon)->GetNetworkable(); + ServerClass *pServerClass = pNetWeapon->GetServerClass(); + if (!pServerClass) + { + g_pSM->LogError(myself, "Invalid server class on weapon."); + RETURN_META_VALUE(MRES_IGNORED, false); } sm_sendprop_info_t info; - - if (!gamehelpers->FindSendPropInfo(pEdict->GetNetworkable()->GetServerClass()->GetName(), "m_hOwnerEntity", &info)) + if (!gamehelpers->FindSendPropInfo(pServerClass->GetName(), "m_hOwnerEntity", &info)) { - g_pSM->LogMessage(myself, "Offset Error"); - return Result_Ignore; + g_pSM->LogError(myself, "Could not find m_hOwnerEntity on %s", pServerClass->GetName()); + RETURN_META_VALUE(MRES_IGNORED, false); } - if (!g_critForward) + int returnValue = 0; + + int ownerIndex = -1; + CBaseHandle &hndl = *(CBaseHandle *) ((intptr_t)pWeapon + info.actual_offset); + CBaseEntity *pHandleEntity = gamehelpers->ReferenceToEntity(hndl.GetEntryIndex()); + + if (pHandleEntity != NULL && hndl == reinterpret_cast(pHandleEntity)->GetRefEHandle()) { - g_pSM->LogMessage(myself, "Invalid Forward"); - return Result_Ignore; + ownerIndex = hndl.GetEntryIndex(); } - int returnValue=0; - - CBaseHandle &hndl = *(CBaseHandle *)((uint8_t *)pEnt + info.actual_offset); - int index = CheckBaseHandle(hndl); - - g_critForward->PushCell(index); //Client index - g_critForward->PushCell(engine->IndexOfEdict(pEdict)); // Weapon index - g_critForward->PushString(pEdict->GetClassName()); //Weapon classname + g_critForward->PushCell(ownerIndex); //Client index + g_critForward->PushCell(gamehelpers->EntityToBCompatRef(pWeapon)); // Weapon index + g_critForward->PushString(gamehelpers->GetEntityClassname(pWeapon)); //Weapon classname g_critForward->PushCellByRef(&returnValue); //return value cell_t result = 0; g_critForward->Execute(&result); - if (result) + if (result && returnValue) { - if (returnValue) - { - return Result_Crit; - } - else - { - return Result_NoCrit; - } - } - else - { - return Result_Ignore; - } -} - -DETOUR_DECL_MEMBER0(CalcIsAttackCriticalHelperMelee, bool) -{ - DetourResult result = DetourCallback((CBaseEntity *)this); - - if (result == Result_Ignore) - { - return DETOUR_MEMBER_CALL(CalcIsAttackCriticalHelperMelee)(); - } - else if (result == Result_NoCrit) - { - return 0; - } - else - { - return 1; - } -} - -DETOUR_DECL_MEMBER0(CalcIsAttackCriticalHelper, bool) -{ - DetourResult result = DetourCallback((CBaseEntity *)this); - - if (result == Result_Ignore) - { - return DETOUR_MEMBER_CALL(CalcIsAttackCriticalHelper)(); - } - else if (result == Result_NoCrit) - { - return 0; - } - else - { - return 1; - } -} - -DETOUR_DECL_MEMBER0(CalcIsAttackCriticalHelperBow, bool) -{ - DetourResult result = DetourCallback((CBaseEntity *)this); - - if (result == Result_Ignore) - { - return DETOUR_MEMBER_CALL(CalcIsAttackCriticalHelperBow)(); - } - else if (result == Result_NoCrit) - { - return 0; - } - else - { - return 1; - } -} - -bool InitialiseCritDetours() -{ - calcIsAttackCriticalDetour = DETOUR_CREATE_MEMBER(CalcIsAttackCriticalHelper, "CalcCritical"); - calcIsAttackCriticalMeleeDetour = DETOUR_CREATE_MEMBER(CalcIsAttackCriticalHelperMelee, "CalcCriticalMelee"); - calcIsAttackCriticalBowDetour = DETOUR_CREATE_MEMBER(CalcIsAttackCriticalHelperBow, "CalcCriticalBow"); - - bool HookCreated = false; - - if (calcIsAttackCriticalDetour != NULL) - { - calcIsAttackCriticalDetour->EnableDetour(); - HookCreated = true; - } - - if (calcIsAttackCriticalMeleeDetour != NULL) - { - calcIsAttackCriticalMeleeDetour->EnableDetour(); - HookCreated = true; - } - - if (calcIsAttackCriticalBowDetour != NULL) - { - calcIsAttackCriticalBowDetour->EnableDetour(); - HookCreated = true; - } - - if (HookCreated) - { - return true; - } - - g_pSM->LogError(myself, "No critical hit forwards could be initialized - Disabled critical hit hooks"); - - return false; -} - -void RemoveCritDetours() -{ - if (calcIsAttackCriticalDetour != NULL) - { - calcIsAttackCriticalDetour->Destroy(); - calcIsAttackCriticalDetour = NULL; - } - - if (calcIsAttackCriticalMeleeDetour != NULL) - { - calcIsAttackCriticalMeleeDetour->Destroy(); - calcIsAttackCriticalMeleeDetour = NULL; - } - - if (calcIsAttackCriticalBowDetour != NULL) - { - calcIsAttackCriticalBowDetour->Destroy(); - calcIsAttackCriticalBowDetour = NULL; + RETURN_META_VALUE(MRES_SUPERCEDE, true); } + + RETURN_META_VALUE(MRES_IGNORED, false); } diff --git a/extensions/tf2/criticals.h b/extensions/tf2/criticals.h index e0f5722d..2b00243c 100644 --- a/extensions/tf2/criticals.h +++ b/extensions/tf2/criticals.h @@ -36,12 +36,38 @@ #include #include #include "CDetour/detours.h" +#include "ISDKHooks.h" -bool InitialiseCritDetours(); -void RemoveCritDetours(); +class CritManager : public ISMEntityListener +{ +public: + CritManager(); + +public: + bool TryEnable(); + void Disable(); + + // ISMEntityListener +public: + virtual void OnEntityCreated(CBaseEntity *pEntity, const char *classname); + virtual void OnEntityDestroyed(CBaseEntity *pEntity); + +private: + void HookEntityIfWeapon(CBaseEntity *pEntity); + void UnhookEntityIfHooked(CBaseEntity *pEntity); + +public: + // CritHook + bool Hook_CalcIsAttackCriticalHelpers(); + +private: + bool m_enabled; + bool m_hooksSetup; + CBitVec m_entsHooked; +}; + +extern CritManager g_CritManager; extern IForward *g_critForward; -extern IServerGameEnts *gameents; - #endif //_INCLUDE_SOURCEMOD_CRITICALS_H_ diff --git a/extensions/tf2/extension.cpp b/extensions/tf2/extension.cpp index 2ee558ec..b1893d50 100644 --- a/extensions/tf2/extension.cpp +++ b/extensions/tf2/extension.cpp @@ -41,6 +41,7 @@ #include "gameplayrules.h" #include "teleporter.h" #include "CDetour/detours.h" +#include "ISDKHooks.h" /** * @file extension.cpp @@ -52,6 +53,7 @@ TF2Tools g_TF2Tools; IGameConfig *g_pGameConf = NULL; IBinTools *g_pBinTools = NULL; +ISDKHooks *g_pSDKHooks = NULL; SMEXT_LINK(&g_TF2Tools); @@ -91,6 +93,7 @@ bool TF2Tools::SDK_OnLoad(char *error, size_t maxlength, bool late) } sharesys->AddDependency(myself, "bintools.ext", true, true); + sharesys->AddDependency(myself, "sdkhooks.ext", true, true); char conf_error[255] = ""; if (!gameconfs->LoadGameConfigFile("sm-tf2.games", &g_pGameConf, conf_error, sizeof(conf_error))) @@ -151,8 +154,6 @@ bool TF2Tools::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION); - GET_V_IFACE_CURRENT(GetServerFactory, gameents, IServerGameEnts, INTERFACEVERSION_SERVERGAMEENTS); - GET_V_IFACE_CURRENT(GetEngineFactory, m_GameEventManager, IGameEventManager2, INTERFACEVERSION_GAMEEVENTSMANAGER2); m_GameEventManager->AddListener(this, "teamplay_restart_round", true); @@ -177,11 +178,22 @@ void TF2Tools::SDK_OnUnload() forwards->ReleaseForward(g_waitingPlayersStartForward); forwards->ReleaseForward(g_waitingPlayersEndForward); forwards->ReleaseForward(g_teleportForward); + + if (g_pSDKHooks != NULL) + { + g_pSDKHooks->RemoveEntityListener(&g_CritManager); + } } void TF2Tools::SDK_OnAllLoaded() { SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools); + SM_GET_LATE_IFACE(SDKHOOKS, g_pSDKHooks); + + if (g_pSDKHooks != NULL) + { + g_pSDKHooks->AddEntityListener(&g_CritManager); + } } bool TF2Tools::RegisterConCommandBase(ConCommandBase *pVar) @@ -198,6 +210,7 @@ void TF2Tools::FireGameEvent( IGameEvent *event ) bool TF2Tools::QueryRunning(char *error, size_t maxlength) { SM_CHECK_IFACE(BINTOOLS, g_pBinTools); + SM_GET_LATE_IFACE(SDKHOOKS, g_pSDKHooks); return true; } @@ -209,6 +222,11 @@ bool TF2Tools::QueryInterfaceDrop(SMInterface *pInterface) return false; } + if (pInterface == g_pSDKHooks) + { + return false; + } + return IExtensionInterface::QueryInterfaceDrop(pInterface); } @@ -321,7 +339,7 @@ void TF2Tools::OnPluginLoaded(IPlugin *plugin) { if (!m_CritDetoursEnabled && g_critForward->GetFunctionCount()) { - m_CritDetoursEnabled = InitialiseCritDetours(); + m_CritDetoursEnabled = g_CritManager.TryEnable(); } if (!m_IsHolidayDetourEnabled && g_isHolidayForward->GetFunctionCount()) @@ -353,7 +371,7 @@ void TF2Tools::OnPluginUnloaded(IPlugin *plugin) { if (m_CritDetoursEnabled && !g_critForward->GetFunctionCount()) { - RemoveCritDetours(); + g_CritManager.Disable(); m_CritDetoursEnabled = false; } if (m_IsHolidayDetourEnabled && !g_isHolidayForward->GetFunctionCount()) diff --git a/extensions/tf2/util.cpp b/extensions/tf2/util.cpp index 2ed61f43..1e3a54d9 100644 --- a/extensions/tf2/util.cpp +++ b/extensions/tf2/util.cpp @@ -88,6 +88,38 @@ bool UTIL_FindDataTable(SendTable *pTable, return false; } +bool UTIL_ContainsDataTable(SendTable *pTable, const char *name) +{ + const char *pname = pTable->GetName(); + int props = pTable->GetNumProps(); + SendProp *prop; + SendTable *table; + + if (pname && strcmp(name, pname) == 0) + return true; + + for (int i = 0; i < props; i++) + { + prop = pTable->GetProp(i); + + if ((table = prop->GetDataTable()) != NULL) + { + pname = table->GetName(); + if (pname && strcmp(name, pname) == 0) + { + return true; + } + + if (UTIL_ContainsDataTable(table, name)) + { + return true; + } + } + } + + return false; +} + ServerClass *UTIL_FindServerClass(const char *classname) { ServerClass *sc = gamedll->GetAllServerClasses(); diff --git a/extensions/tf2/util.h b/extensions/tf2/util.h index a79f09d0..a1b48dec 100644 --- a/extensions/tf2/util.h +++ b/extensions/tf2/util.h @@ -49,6 +49,8 @@ bool UTIL_FindDataTable(SendTable *pTable, sm_sendprop_info_t *info, unsigned int offset); +bool UTIL_ContainsDataTable(SendTable *pTable, const char *name); + ServerClass *UTIL_FindServerClass(const char *classname); CBaseEntity *UTIL_GetCBaseEntity(int num, bool onlyPlayers);