/** * vim: set ts=4 : * ============================================================================= * Source SDK Hooks Extension * Copyright (C) 2010-2012 Nicholas Hastings * Copyright (C) 2009-2010 Erik Minekus * ============================================================================= * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License, version 3.0, as published by the * Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . * * As a special exception, AlliedModders LLC gives you permission to link the * code of this program (as well as its derivative works) to "Half-Life 2," the * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software * by the Valve Corporation. You must obey the GNU General Public License in * all respects for all other code used. Additionally, AlliedModders LLC grants * this exception to all derivative works. AlliedModders LLC defines further * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), * or . * * Version: $Id$ */ #include #include "extension.h" #include "compat_wrappers.h" #include "macros.h" #include "natives.h" #include #include //#define SDKHOOKSDEBUG /** * Globals */ // Order MUST match SDKHookType enum HookTypeData g_HookTypes[SDKHook_MAXHOOKS] = { // Hook name DT required Supported (always false til later) {"EndTouch", "", false}, {"FireBulletsPost", "", false}, {"OnTakeDamage", "", false}, {"OnTakeDamagePost", "", false}, {"PreThink", "DT_BasePlayer", false}, {"PostThink", "DT_BasePlayer", false}, {"SetTransmit", "", false}, {"Spawn", "", false}, {"StartTouch", "", false}, {"Think", "", false}, {"Touch", "", false}, {"TraceAttack", "", false}, {"TraceAttackPost", "", false}, {"WeaponCanSwitchTo", "DT_BaseCombatCharacter", false}, {"WeaponCanUse", "DT_BaseCombatCharacter", false}, {"WeaponDrop", "DT_BaseCombatCharacter", false}, {"WeaponEquip", "DT_BaseCombatCharacter", false}, {"WeaponSwitch", "DT_BaseCombatCharacter", false}, {"ShouldCollide", "", false}, {"PreThinkPost", "DT_BasePlayer", false}, {"PostThinkPost", "DT_BasePlayer", false}, {"ThinkPost", "", false}, {"EndTouchPost", "", false}, {"GroundEntChangedPost", "", false}, {"SpawnPost", "", false}, {"StartTouchPost", "", false}, {"TouchPost", "", false}, {"VPhysicsUpdate", "", false}, {"VPhysicsUpdatePost", "", false}, {"WeaponCanSwitchToPost", "DT_BaseCombatCharacter", false}, {"WeaponCanUsePost", "DT_BaseCombatCharacter", false}, {"WeaponDropPost", "DT_BaseCombatCharacter", false}, {"WeaponEquipPost", "DT_BaseCombatCharacter", false}, {"WeaponSwitchPost", "DT_BaseCombatCharacter", false}, {"Use", "", false}, {"UsePost", "", false}, {"Reload", "DT_BaseCombatWeapon", false}, {"ReloadPost", "DT_BaseCombatWeapon", false}, {"GetMaxHealth", "", false}, }; SDKHooks g_Interface; SMEXT_LINK(&g_Interface); CGlobalVars *gpGlobals; ke::Vector g_HookList[SDKHook_MAXHOOKS]; CBitVec m_EntityExists; IBinTools *g_pBinTools = NULL; ICvar *icvar = NULL; #if SOURCE_ENGINE >= SE_ORANGEBOX IServerTools *servertools = NULL; #endif // global hooks and forwards IForward *g_pOnEntityCreated = NULL; IForward *g_pOnEntityDestroyed = NULL; #ifdef GAMEDESC_CAN_CHANGE int g_hookOnGetGameDescription = 0; IForward *g_pOnGetGameNameDescription = NULL; #endif int g_hookOnGetMapEntitiesString = 0; int g_hookOnLevelInit = 0; IForward *g_pOnLevelInit = NULL; IGameConfig *g_pGameConf = NULL; char g_szMapEntities[2097152]; CUtlVector *EntListeners() { void *gEntList = gamehelpers->GetGlobalEntityList(); if (gEntList) { int offset = -1; /* 65572 */ if (g_pGameConf->GetOffset("EntityListeners", &offset)) { return (CUtlVector *)((intptr_t) gEntList + offset); } } else { void *entListeners; if (g_pGameConf->GetAddress("EntityListenersPtr", &entListeners)) { return (CUtlVector *)entListeners; } } return NULL; } /** * IServerGameDLL & IVEngineServer Hooks */ SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, const char *, const char *, const char *, const char *, bool, bool); #ifdef GAMEDESC_CAN_CHANGE SH_DECL_HOOK0(IServerGameDLL, GetGameDescription, SH_NOATTRIB, 0, const char *); #endif SH_DECL_HOOK0(IVEngineServer, GetMapEntitiesString, SH_NOATTRIB, 0, const char *); /** * CBaseEntity Hooks */ SH_DECL_MANUALHOOK1_void(EndTouch, 0, 0, 0, CBaseEntity *); SH_DECL_MANUALHOOK1_void(FireBullets, 0, 0, 0, FireBulletsInfo_t const&); #ifdef GETMAXHEALTH_IS_VIRTUAL SH_DECL_MANUALHOOK0(GetMaxHealth, 0, 0, 0, int); #endif SH_DECL_MANUALHOOK1_void(GroundEntChanged, 0, 0, 0, void *); SH_DECL_MANUALHOOK1(OnTakeDamage, 0, 0, 0, int, CTakeDamageInfoHack &); SH_DECL_MANUALHOOK0_void(PreThink, 0, 0, 0); SH_DECL_MANUALHOOK0_void(PostThink, 0, 0, 0); SH_DECL_MANUALHOOK0(Reload, 0, 0, 0, bool); SH_DECL_MANUALHOOK2_void(SetTransmit, 0, 0, 0, CCheckTransmitInfo *, bool); SH_DECL_MANUALHOOK2(ShouldCollide, 0, 0, 0, bool, int, int); #if SOURCE_ENGINE == SE_DOTA SH_DECL_MANUALHOOK1_void(Spawn, 0, 0, 0, CEntityKeyValues *); #else SH_DECL_MANUALHOOK0_void(Spawn, 0, 0, 0); #endif SH_DECL_MANUALHOOK1_void(StartTouch, 0, 0, 0, CBaseEntity *); SH_DECL_MANUALHOOK0_void(Think, 0, 0, 0); SH_DECL_MANUALHOOK1_void(Touch, 0, 0, 0, CBaseEntity *); #if SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013 SH_DECL_MANUALHOOK4_void(TraceAttack, 0, 0, 0, CTakeDamageInfoHack &, const Vector &, CGameTrace *, CDmgAccumulator *); #else SH_DECL_MANUALHOOK3_void(TraceAttack, 0, 0, 0, CTakeDamageInfoHack &, const Vector &, CGameTrace *); #endif SH_DECL_MANUALHOOK4_void(Use, 0, 0, 0, CBaseEntity *, CBaseEntity *, USE_TYPE, float); SH_DECL_MANUALHOOK1_void(VPhysicsUpdate, 0, 0, 0, IPhysicsObject *); SH_DECL_MANUALHOOK1(Weapon_CanSwitchTo, 0, 0, 0, bool, CBaseCombatWeapon *); SH_DECL_MANUALHOOK1(Weapon_CanUse, 0, 0, 0, bool, CBaseCombatWeapon *); SH_DECL_MANUALHOOK3_void(Weapon_Drop, 0, 0, 0, CBaseCombatWeapon *, const Vector *, const Vector *); SH_DECL_MANUALHOOK1_void(Weapon_Equip, 0, 0, 0, CBaseCombatWeapon *); SH_DECL_MANUALHOOK2(Weapon_Switch, 0, 0, 0, bool, CBaseCombatWeapon *, int); /** * Forwards */ bool SDKHooks::SDK_OnLoad(char *error, size_t maxlength, bool late) { char buffer[256]; g_pSM->BuildPath(Path_SM, buffer, sizeof(buffer)-1, "/extensions/sdkhooks.ext." PLATFORM_LIB_EXT); if (libsys->PathExists(buffer) && libsys->IsPathFile(buffer)) { g_pSM->Format(error, maxlength-1, "SDKHooks 2.x cannot load while old version (sdkhooks.ext." PLATFORM_LIB_EXT ") is still in extensions dir"); return false; } g_pSM->BuildPath(Path_SM, buffer, sizeof(buffer)-1, "/gamedata/sdkhooks.games.txt"); if (libsys->PathExists(buffer) && libsys->IsPathFile(buffer)) { g_pSM->Format(error, maxlength-1, "SDKHooks 2.x cannot load while old gamedata file (sdkhooks.games.txt) is still in gamedata dir"); return false; } sharesys->AddDependency(myself, "bintools.ext", true, true); sharesys->AddNatives(myself, g_Natives); sharesys->RegisterLibrary(myself, "sdkhooks"); sharesys->AddInterface(myself, &g_Interface); sharesys->AddCapabilityProvider(myself, this, "SDKHook_DmgCustomInOTD"); sharesys->AddCapabilityProvider(myself, this, "SDKHook_LogicalEntSupport"); playerhelpers->AddClientListener(&g_Interface); plsys->AddPluginsListener(&g_Interface); g_pOnEntityCreated = forwards->CreateForward("OnEntityCreated", ET_Ignore, 2, NULL, Param_Cell, Param_String); g_pOnEntityDestroyed = forwards->CreateForward("OnEntityDestroyed", ET_Ignore, 1, NULL, Param_Cell); #ifdef GAMEDESC_CAN_CHANGE g_pOnGetGameNameDescription = forwards->CreateForward("OnGetGameDescription", ET_Hook, 2, NULL, Param_String); #endif g_pOnLevelInit = forwards->CreateForward("OnLevelInit", ET_Hook, 2, NULL, Param_String, Param_String); buffer[0] = '\0'; if (!gameconfs->LoadGameConfigFile("sdkhooks.games", &g_pGameConf, buffer, sizeof(buffer))) { if (buffer[0]) { g_pSM->Format(error, maxlength, "Could not read sdkhooks.games gamedata: %s", buffer); } return false; } CUtlVector *entListeners = EntListeners(); if (!entListeners) { g_pSM->Format(error, maxlength, "Failed to setup entity listeners"); return false; } entListeners->AddToTail(this); SetupHooks(); #if SOURCE_ENGINE >= SE_ORANGEBOX int index; CBaseHandle hndl; for (IHandleEntity *pEnt = (IHandleEntity *)servertools->FirstEntity(); pEnt; pEnt = (IHandleEntity *)servertools->NextEntity((CBaseEntity *)pEnt)) { hndl = pEnt->GetRefEHandle(); if (!hndl.IsValid()) continue; index = hndl.GetEntryIndex(); m_EntityExists.Set(index); } #else for (int i = 0; i < NUM_ENT_ENTRIES; i++) { if (gamehelpers->ReferenceToEntity(i) != NULL) m_EntityExists.Set(i); } #endif return true; } inline void HookLevelInit() { assert(g_hookOnLevelInit == 0); g_hookOnLevelInit = SH_ADD_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(&g_Interface, &SDKHooks::Hook_LevelInit), false); assert(g_hookOnGetMapEntitiesString == 0); g_hookOnGetMapEntitiesString = SH_ADD_HOOK(IVEngineServer, GetMapEntitiesString, engine, SH_MEMBER(&g_Interface, &SDKHooks::Hook_GetMapEntitiesString), false); } #ifdef GAMEDESC_CAN_CHANGE inline void HookGetGameDescription() { assert(g_hookOnGetGameDescription == 0); g_hookOnGetGameDescription = SH_ADD_HOOK(IServerGameDLL, GetGameDescription, gamedll, SH_MEMBER(&g_Interface, &SDKHooks::Hook_GetGameDescription), false); } #endif void SDKHooks::SDK_OnAllLoaded() { SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools); if (!g_pBinTools) { g_pSM->LogError(myself, "Could not find interface: " SMINTERFACE_BINTOOLS_NAME); return; } if (g_pOnLevelInit->GetFunctionCount() > 0) HookLevelInit(); #ifdef GAMEDESC_CAN_CHANGE if (g_pOnGetGameNameDescription->GetFunctionCount() > 0) HookGetGameDescription(); #endif } #define KILL_HOOK_IF_ACTIVE(hook) \ if (hook != 0) \ { \ SH_REMOVE_HOOK_ID(hook); \ } void SDKHooks::SDK_OnUnload() { // Remove left over hooks Unhook(reinterpret_cast(NULL)); KILL_HOOK_IF_ACTIVE(g_hookOnLevelInit); KILL_HOOK_IF_ACTIVE(g_hookOnGetMapEntitiesString); #ifdef GAMEDESC_CAN_CHANGE KILL_HOOK_IF_ACTIVE(g_hookOnGetGameDescription); #endif forwards->ReleaseForward(g_pOnEntityCreated); forwards->ReleaseForward(g_pOnEntityDestroyed); #ifdef GAMEDESC_CAN_CHANGE forwards->ReleaseForward(g_pOnGetGameNameDescription); #endif forwards->ReleaseForward(g_pOnLevelInit); plsys->RemovePluginsListener(&g_Interface); playerhelpers->RemoveClientListener(&g_Interface); sharesys->DropCapabilityProvider(myself, this, "SDKHook_DmgCustomInOTD"); sharesys->DropCapabilityProvider(myself, this, "SDKHook_LogicalEntSupport"); CUtlVector *entListeners = EntListeners(); entListeners->FindAndRemove(this); gameconfs->CloseGameConfigFile(g_pGameConf); } bool SDKHooks::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late) { GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION); #if SOURCE_ENGINE >= SE_ORANGEBOX GET_V_IFACE_ANY(GetServerFactory, servertools, IServerTools, VSERVERTOOLS_INTERFACE_VERSION); g_pCVar = icvar; #endif CONVAR_REGISTER(this); gpGlobals = ismm->GetCGlobals(); return true; } const char *SDKHooks::GetExtensionVerString() { return SOURCEMOD_VERSION; } const char *SDKHooks::GetExtensionDateString() { return SOURCEMOD_BUILD_TIME; } void SDKHooks::OnPluginLoaded(IPlugin *plugin) { if (g_pOnLevelInit->GetFunctionCount() > 0 && g_hookOnLevelInit == 0) HookLevelInit(); #ifdef GAMEDESC_CAN_CHANGE if (g_pOnGetGameNameDescription->GetFunctionCount() > 0 && g_hookOnGetGameDescription == 0) HookGetGameDescription(); #endif } void SDKHooks::OnPluginUnloaded(IPlugin *plugin) { Unhook(plugin->GetBaseContext()); if (g_pOnLevelInit->GetFunctionCount() == 0) { KILL_HOOK_IF_ACTIVE(g_hookOnLevelInit); KILL_HOOK_IF_ACTIVE(g_hookOnGetMapEntitiesString); } #ifdef GAMEDESC_CAN_CHANGE if (g_pOnGetGameNameDescription->GetFunctionCount() == 0) KILL_HOOK_IF_ACTIVE(g_hookOnGetGameDescription); #endif } void SDKHooks::OnClientPutInServer(int client) { CBaseEntity *pPlayer = gamehelpers->ReferenceToEntity(client); HandleEntityCreated(pPlayer, client); } void SDKHooks::OnClientDisconnecting(int client) { CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(client); HandleEntityDeleted(pEntity, client); } void SDKHooks::AddEntityListener(ISMEntityListener *listener) { m_EntListeners.push_back(listener); } void SDKHooks::RemoveEntityListener(ISMEntityListener *listener) { m_EntListeners.remove(listener); } bool SDKHooks::RegisterConCommandBase(ConCommandBase *pVar) { /* Always call META_REGCVAR instead of going through the engine. */ return META_REGCVAR(pVar); } FeatureStatus SDKHooks::GetFeatureStatus(FeatureType type, const char *name) { return FeatureStatus_Available; } /** * Functions */ static void CopyEntityVector(const ke::Vector &source, ke::Vector &destination, int entity) { for (size_t iter = 0; iter < source.length(); ++iter) { if (source[iter].entity != entity) { continue; } destination.append(source[iter].callback); } } cell_t SDKHooks::Call(int entity, SDKHookType type, int other) { return Call(gamehelpers->ReferenceToEntity(entity), type, gamehelpers->ReferenceToEntity(other)); } cell_t SDKHooks::Call(CBaseEntity *pEnt, SDKHookType type, int other) { return Call(pEnt, type, gamehelpers->ReferenceToEntity(other)); } cell_t SDKHooks::Call(CBaseEntity *pEnt, SDKHookType type, CBaseEntity *pOther) { cell_t ret = Pl_Continue; CVTableHook vhook(pEnt); ke::Vector &vtablehooklist = g_HookList[type]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEnt); int other = gamehelpers->EntityToBCompatRef(pOther); ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCell(other); cell_t res; callback->Execute(&res); if(res > ret) { ret = res; } } break; } return ret; } void SDKHooks::SetupHooks() { int offset; // gamedata pre post // (pre is not necessarily a prehook, just named without "Post" appeneded) CHECKOFFSET(EndTouch, true, true); CHECKOFFSET(FireBullets, false, true); CHECKOFFSET(GroundEntChanged, false, true); CHECKOFFSET(OnTakeDamage, true, true); CHECKOFFSET(PreThink, true, true); CHECKOFFSET(PostThink, true, true); CHECKOFFSET(Reload, true, true); CHECKOFFSET(SetTransmit, true, false); CHECKOFFSET(ShouldCollide, true, false); CHECKOFFSET(Spawn, true, true); CHECKOFFSET(StartTouch, true, true); CHECKOFFSET(Think, true, true); CHECKOFFSET(Touch, true, true); CHECKOFFSET(TraceAttack, true, true); CHECKOFFSET(Use, true, true); CHECKOFFSET_W(CanSwitchTo, true, true); CHECKOFFSET_W(CanUse, true, true); CHECKOFFSET_W(Drop, true, true); CHECKOFFSET_W(Equip, true, true); CHECKOFFSET_W(Switch, true, true); CHECKOFFSET(VPhysicsUpdate, true, true); // this one is in a class all its own -_- offset = 0; g_pGameConf->GetOffset("GroundEntChanged", &offset); if (offset > 0) { SH_MANUALHOOK_RECONFIGURE(GroundEntChanged, offset, 0, 0); g_HookTypes[SDKHook_GroundEntChangedPost].supported = true; } #ifdef GETMAXHEALTH_IS_VIRTUAL CHECKOFFSET(GetMaxHealth, true, false); #endif } HookReturn SDKHooks::Hook(int entity, SDKHookType type, IPluginFunction *callback) { if(!g_HookTypes[type].supported) return HookRet_NotSupported; CBaseEntity *pEnt = gamehelpers->ReferenceToEntity(entity); if(!pEnt) return HookRet_InvalidEntity; if(type < 0 || type >= SDKHook_MAXHOOKS) return HookRet_InvalidHookType; if (!!strcmp(g_HookTypes[type].dtReq, "")) { IServerUnknown *pUnk = (IServerUnknown *)pEnt; IServerNetworkable *pNet = pUnk->GetNetworkable(); if (pNet && !UTIL_ContainsDataTable(pNet->GetServerClass()->m_pTable, g_HookTypes[type].dtReq)) return HookRet_BadEntForHookType; } size_t entry; CVTableHook vhook(pEnt); ke::Vector &vtablehooklist = g_HookList[type]; for (entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook == vtablehooklist[entry]->vtablehook) { break; } } if (entry == vtablehooklist.length()) { int hookid = 0; switch(type) { case SDKHook_EndTouch: hookid = SH_ADD_MANUALVPHOOK(EndTouch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_EndTouch), false); break; case SDKHook_EndTouchPost: hookid = SH_ADD_MANUALVPHOOK(EndTouch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_EndTouchPost), true); break; case SDKHook_FireBulletsPost: hookid = SH_ADD_MANUALVPHOOK(FireBullets, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_FireBulletsPost), true); break; #ifdef GETMAXHEALTH_IS_VIRTUAL case SDKHook_GetMaxHealth: hookid = SH_ADD_MANUALVPHOOK(GetMaxHealth, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_GetMaxHealth), false); break; #endif case SDKHook_GroundEntChangedPost: hookid = SH_ADD_MANUALVPHOOK(GroundEntChanged, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_GroundEntChangedPost), true); break; case SDKHook_OnTakeDamage: hookid = SH_ADD_MANUALVPHOOK(OnTakeDamage, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_OnTakeDamage), false); break; case SDKHook_OnTakeDamagePost: hookid = SH_ADD_MANUALVPHOOK(OnTakeDamage, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_OnTakeDamagePost), true); break; case SDKHook_PreThink: hookid = SH_ADD_MANUALVPHOOK(PreThink, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_PreThink), false); break; case SDKHook_PreThinkPost: hookid = SH_ADD_MANUALVPHOOK(PreThink, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_PreThinkPost), true); break; case SDKHook_PostThink: hookid = SH_ADD_MANUALVPHOOK(PostThink, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_PostThink), false); break; case SDKHook_PostThinkPost: hookid = SH_ADD_MANUALVPHOOK(PostThink, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_PostThinkPost), true); break; case SDKHook_Reload: hookid = SH_ADD_MANUALVPHOOK(Reload, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Reload), false); break; case SDKHook_ReloadPost: hookid = SH_ADD_MANUALVPHOOK(Reload, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_ReloadPost), true); break; case SDKHook_SetTransmit: hookid = SH_ADD_MANUALVPHOOK(SetTransmit, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_SetTransmit), false); break; case SDKHook_Spawn: hookid = SH_ADD_MANUALVPHOOK(Spawn, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Spawn), false); break; case SDKHook_SpawnPost: hookid = SH_ADD_MANUALVPHOOK(Spawn, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_SpawnPost), true); break; case SDKHook_StartTouch: hookid = SH_ADD_MANUALVPHOOK(StartTouch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_StartTouch), false); break; case SDKHook_StartTouchPost: hookid = SH_ADD_MANUALVPHOOK(StartTouch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_StartTouchPost), true); break; case SDKHook_Think: hookid = SH_ADD_MANUALVPHOOK(Think, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Think), false); break; case SDKHook_ThinkPost: hookid = SH_ADD_MANUALVPHOOK(Think, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_ThinkPost), true); break; case SDKHook_Touch: hookid = SH_ADD_MANUALVPHOOK(Touch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Touch), false); break; case SDKHook_TouchPost: hookid = SH_ADD_MANUALVPHOOK(Touch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_TouchPost), true); break; case SDKHook_TraceAttack: hookid = SH_ADD_MANUALVPHOOK(TraceAttack, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_TraceAttack), false); break; case SDKHook_TraceAttackPost: hookid = SH_ADD_MANUALVPHOOK(TraceAttack, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_TraceAttackPost), true); break; case SDKHook_Use: hookid = SH_ADD_MANUALVPHOOK(Use, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Use), false); break; case SDKHook_UsePost: hookid = SH_ADD_MANUALVPHOOK(Use, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_UsePost), true); break; case SDKHook_VPhysicsUpdate: hookid = SH_ADD_MANUALVPHOOK(VPhysicsUpdate, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_VPhysicsUpdate), false); break; case SDKHook_VPhysicsUpdatePost: hookid = SH_ADD_MANUALVPHOOK(VPhysicsUpdate, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_VPhysicsUpdatePost), true); break; case SDKHook_WeaponCanSwitchTo: hookid = SH_ADD_MANUALVPHOOK(Weapon_CanSwitchTo, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponCanSwitchTo), false); break; case SDKHook_WeaponCanSwitchToPost: hookid = SH_ADD_MANUALVPHOOK(Weapon_CanSwitchTo, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponCanSwitchToPost), true); break; case SDKHook_WeaponCanUse: hookid = SH_ADD_MANUALVPHOOK(Weapon_CanUse, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponCanUse), false); break; case SDKHook_WeaponCanUsePost: hookid = SH_ADD_MANUALVPHOOK(Weapon_CanUse, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponCanUsePost), true); break; case SDKHook_WeaponDrop: hookid = SH_ADD_MANUALVPHOOK(Weapon_Drop, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponDrop), false); break; case SDKHook_WeaponDropPost: hookid = SH_ADD_MANUALVPHOOK(Weapon_Drop, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponDropPost), true); break; case SDKHook_WeaponEquip: hookid = SH_ADD_MANUALVPHOOK(Weapon_Equip, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponEquip), false); break; case SDKHook_WeaponEquipPost: hookid = SH_ADD_MANUALVPHOOK(Weapon_Equip, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponEquipPost), true); break; case SDKHook_WeaponSwitch: hookid = SH_ADD_MANUALVPHOOK(Weapon_Switch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponSwitch), false); break; case SDKHook_WeaponSwitchPost: hookid = SH_ADD_MANUALVPHOOK(Weapon_Switch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponSwitchPost), true); break; case SDKHook_ShouldCollide: hookid = SH_ADD_MANUALVPHOOK(ShouldCollide, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_ShouldCollide), false); break; } vhook.SetHookID(hookid); CVTableList *vtablelist = new CVTableList; vtablelist->vtablehook = new CVTableHook(vhook); vtablehooklist.append(vtablelist); } // Add hook to hook list HookList hook; hook.entity = gamehelpers->EntityToBCompatRef(pEnt); hook.callback = callback; vtablehooklist[entry]->hooks.append(hook); return HookRet_Successful; } void SDKHooks::Unhook(CBaseEntity *pEntity) { if (pEntity == NULL) { return; } int entity = gamehelpers->EntityToBCompatRef(pEntity); for (size_t type = 0; type < SDKHook_MAXHOOKS; ++type) { ke::Vector &vtablehooklist = g_HookList[type]; for (size_t listentry = 0; listentry < vtablehooklist.length(); ++listentry) { ke::Vector &pawnhooks = vtablehooklist[listentry]->hooks; for (size_t entry = 0; entry < pawnhooks.length(); ++entry) { if (entity != pawnhooks[entry].entity) { continue; } pawnhooks.remove(entry--); } if (pawnhooks.length() == 0) { delete vtablehooklist[listentry]; vtablehooklist.remove(listentry--); } } } } void SDKHooks::Unhook(IPluginContext *pContext) { for (size_t type = 0; type < SDKHook_MAXHOOKS; ++type) { ke::Vector &vtablehooklist = g_HookList[type]; for (size_t listentry = 0; listentry < vtablehooklist.length(); ++listentry) { ke::Vector &pawnhooks = vtablehooklist[listentry]->hooks; for (size_t entry = 0; entry < pawnhooks.length(); ++entry) { if (pContext != NULL && pContext != pawnhooks[entry].callback->GetParentRuntime()->GetDefaultContext()) { continue; } pawnhooks.remove(entry--); } if (pawnhooks.length() == 0) { delete vtablehooklist[listentry]; vtablehooklist.remove(listentry--); } } } } void SDKHooks::Unhook(int entity, SDKHookType type, IPluginFunction *pCallback) { CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(entity); if (pEntity == NULL) { return; } CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[type]; for (size_t listentry = 0; listentry < vtablehooklist.length(); ++listentry) { if (vhook != vtablehooklist[listentry]->vtablehook) { continue; } entity = gamehelpers->EntityToBCompatRef(pEntity); ke::Vector &pawnhooks = vtablehooklist[listentry]->hooks; for (size_t entry = 0; entry < pawnhooks.length(); ++entry) { HookList &hookentry = pawnhooks[entry]; if (entity != hookentry.entity || pCallback != hookentry.callback) { continue; } pawnhooks.remove(entry--); } if (pawnhooks.length() == 0) { delete vtablehooklist[listentry]; vtablehooklist.remove(listentry); } break; } } /** * IEntityFactoryDictionary, IServerGameDLL & IVEngineServer Hook Handlers */ void SDKHooks::OnEntityCreated(CBaseEntity *pEntity) { // Call OnEntityCreated forward int ref = gamehelpers->EntityToBCompatRef(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())) { return; } HandleEntityCreated(pEntity, ref); } #ifdef GAMEDESC_CAN_CHANGE const char *SDKHooks::Hook_GetGameDescription() { static char szGameDesc[64]; cell_t result = Pl_Continue; g_pSM->Format(szGameDesc, sizeof(szGameDesc), "%s", SH_CALL(gamedll, &IServerGameDLL::GetGameDescription)()); // Call OnGetGameDescription forward g_pOnGetGameNameDescription->PushStringEx(szGameDesc, sizeof(szGameDesc), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); g_pOnGetGameNameDescription->Execute(&result); if(result == Pl_Changed) RETURN_META_VALUE(MRES_SUPERCEDE, szGameDesc); RETURN_META_VALUE(MRES_IGNORED, NULL); } #endif const char *SDKHooks::Hook_GetMapEntitiesString() { if(g_szMapEntities[0]) RETURN_META_VALUE(MRES_SUPERCEDE, g_szMapEntities); RETURN_META_VALUE(MRES_IGNORED, NULL); } bool SDKHooks::Hook_LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background) { strcpy(g_szMapEntities, pMapEntities); cell_t result = Pl_Continue; // Call OnLevelInit forward g_pOnLevelInit->PushString(pMapName); g_pOnLevelInit->PushStringEx(g_szMapEntities, sizeof(g_szMapEntities), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK); g_pOnLevelInit->Execute(&result); if(result >= Pl_Handled) RETURN_META_VALUE(MRES_SUPERCEDE, false); if(result == Pl_Changed) RETURN_META_VALUE_NEWPARAMS(MRES_HANDLED, true, &IServerGameDLL::LevelInit, (pMapName, g_szMapEntities, pOldLevel, pLandmarkName, loadGame, background)); RETURN_META_VALUE(MRES_IGNORED, true); } /** * CBaseEntity Hook Handlers */ void SDKHooks::Hook_EndTouch(CBaseEntity *pOther) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_EndTouch, pOther); if(result >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_EndTouchPost(CBaseEntity *pOther) { Call(META_IFACEPTR(CBaseEntity), SDKHook_EndTouchPost, pOther); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_FireBulletsPost(const FireBulletsInfo_t &info) { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); int entity = gamehelpers->EntityToBCompatRef(pEntity); IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(entity); if(!pPlayer) RETURN_META(MRES_IGNORED); IPlayerInfo *pInfo = pPlayer->GetPlayerInfo(); if(!pInfo) RETURN_META(MRES_IGNORED); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_FireBulletsPost]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } const char *weapon = pInfo->GetWeaponName(); ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCell(info.m_iShots); callback->PushString(weapon?weapon:""); callback->Execute(NULL); } break; } RETURN_META(MRES_IGNORED); } #ifdef GETMAXHEALTH_IS_VIRTUAL int SDKHooks::Hook_GetMaxHealth() { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); int original_max = SH_MCALL(pEntity, GetMaxHealth)(); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_GetMaxHealth]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); int new_max = original_max; cell_t res = Pl_Continue; ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCellByRef(&new_max); callback->Execute(&res); } if (res >= Pl_Changed) RETURN_META_VALUE(MRES_SUPERCEDE, new_max); break; } RETURN_META_VALUE(MRES_IGNORED, original_max); } #endif void SDKHooks::Hook_GroundEntChangedPost(void *pVar) { Call(META_IFACEPTR(CBaseEntity), SDKHook_GroundEntChangedPost); } int SDKHooks::Hook_OnTakeDamage(CTakeDamageInfoHack &info) { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_OnTakeDamage]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); int attacker = info.GetAttacker(); int inflictor = info.GetInflictor(); float damage = info.GetDamage(); int damagetype = info.GetDamageType(); int weapon = info.GetWeapon(); Vector force = info.GetDamageForce(); cell_t damageForce[3] = {sp_ftoc(force.x), sp_ftoc(force.y), sp_ftoc(force.z)}; Vector pos = info.GetDamagePosition(); cell_t damagePosition[3] = {sp_ftoc(pos.x), sp_ftoc(pos.y), sp_ftoc(pos.z)}; cell_t res, ret = Pl_Continue; ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCellByRef(&attacker); callback->PushCellByRef(&inflictor); callback->PushFloatByRef(&damage); callback->PushCellByRef(&damagetype); callback->PushCellByRef(&weapon); callback->PushArray(damageForce, 3, SM_PARAM_COPYBACK); callback->PushArray(damagePosition, 3, SM_PARAM_COPYBACK); callback->PushCell(info.GetDamageCustom()); callback->Execute(&res); if(res >= ret) { ret = res; if(ret == Pl_Changed) { CBaseEntity *pEntAttacker = gamehelpers->ReferenceToEntity(attacker); if(!pEntAttacker) { callback->GetParentRuntime()->GetDefaultContext()->ThrowNativeError("Entity %d for attacker is invalid", attacker); RETURN_META_VALUE(MRES_IGNORED, 0); } CBaseEntity *pEntInflictor = gamehelpers->ReferenceToEntity(inflictor); if(!pEntInflictor) { callback->GetParentRuntime()->GetDefaultContext()->ThrowNativeError("Entity %d for inflictor is invalid", inflictor); RETURN_META_VALUE(MRES_IGNORED, 0); } info.SetAttacker(pEntAttacker); info.SetInflictor(pEntInflictor); info.SetDamage(damage); info.SetDamageType(damagetype); info.SetWeapon(gamehelpers->ReferenceToEntity(weapon)); info.SetDamageForce( sp_ctof(damageForce[0]), sp_ctof(damageForce[1]), sp_ctof(damageForce[2])); info.SetDamagePosition( sp_ctof(damagePosition[0]), sp_ctof(damagePosition[1]), sp_ctof(damagePosition[2])); } } } if(ret >= Pl_Handled) RETURN_META_VALUE(MRES_SUPERCEDE, 1); if(ret == Pl_Changed) RETURN_META_VALUE(MRES_HANDLED, 1); break; } RETURN_META_VALUE(MRES_IGNORED, 0); } int SDKHooks::Hook_OnTakeDamagePost(CTakeDamageInfoHack &info) { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_OnTakeDamagePost]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCell(info.GetAttacker()); callback->PushCell(info.GetInflictor()); callback->PushFloat(info.GetDamage()); callback->PushCell(info.GetDamageType()); callback->PushCell(info.GetWeapon()); Vector force = info.GetDamageForce(); cell_t damageForce[3] = {sp_ftoc(force.x), sp_ftoc(force.y), sp_ftoc(force.z)}; callback->PushArray(damageForce, 3); Vector pos = info.GetDamagePosition(); cell_t damagePosition[3] = {sp_ftoc(pos.x), sp_ftoc(pos.y), sp_ftoc(pos.z)}; callback->PushArray(damagePosition, 3); callback->PushCell(info.GetDamageCustom()); callback->Execute(NULL); } break; } RETURN_META_VALUE(MRES_IGNORED, 0); } void SDKHooks::Hook_PreThink() { Call(META_IFACEPTR(CBaseEntity), SDKHook_PreThink); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_PreThinkPost() { Call(META_IFACEPTR(CBaseEntity), SDKHook_PreThinkPost); } void SDKHooks::Hook_PostThink() { Call(META_IFACEPTR(CBaseEntity), SDKHook_PostThink); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_PostThinkPost() { Call(META_IFACEPTR(CBaseEntity), SDKHook_PostThinkPost); } bool SDKHooks::Hook_Reload() { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_Reload]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); cell_t res = Pl_Continue; ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->Execute(&res); } if (res >= Pl_Handled) RETURN_META_VALUE(MRES_SUPERCEDE, false); break; } RETURN_META_VALUE(MRES_IGNORED, true); } bool SDKHooks::Hook_ReloadPost() { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_ReloadPost]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); cell_t origreturn = META_RESULT_ORIG_RET(bool) ? 1 : 0; ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCell(origreturn); callback->Execute(NULL); } break; } return true; } void SDKHooks::Hook_SetTransmit(CCheckTransmitInfo *pInfo, bool bAlways) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_SetTransmit, gamehelpers->IndexOfEdict(pInfo->m_pClientEnt)); if(result >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_IGNORED); } bool SDKHooks::Hook_ShouldCollide(int collisionGroup, int contentsMask) { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_ShouldCollide]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); cell_t origRet = ((META_RESULT_STATUS >= MRES_OVERRIDE)?(META_RESULT_OVERRIDE_RET(bool)):(META_RESULT_ORIG_RET(bool))) ? 1 : 0; cell_t res = 0; ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCell(collisionGroup); callback->PushCell(contentsMask); callback->PushCell(origRet); callback->Execute(&res); } bool ret = false; if (res != 0) { ret = true; } RETURN_META_VALUE(MRES_SUPERCEDE, ret); } RETURN_META_VALUE(MRES_IGNORED, true); } #if SOURCE_ENGINE == SE_DOTA void SDKHooks::Hook_Spawn(CEntityKeyValues *kv) #else void SDKHooks::Hook_Spawn() #endif { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_Spawn]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); cell_t res = Pl_Continue; ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->Execute(&res); } if (res >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); break; } RETURN_META(MRES_IGNORED); } #if SOURCE_ENGINE == SE_DOTA void SDKHooks::Hook_SpawnPost(CEntityKeyValues *kv) #else void SDKHooks::Hook_SpawnPost() #endif { Call(META_IFACEPTR(CBaseEntity), SDKHook_SpawnPost); } void SDKHooks::Hook_StartTouch(CBaseEntity *pOther) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_StartTouch, pOther); if(result >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_StartTouchPost(CBaseEntity *pOther) { Call(META_IFACEPTR(CBaseEntity), SDKHook_StartTouchPost, pOther); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_Think() { Call(META_IFACEPTR(CBaseEntity), SDKHook_Think); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_ThinkPost() { Call(META_IFACEPTR(CBaseEntity), SDKHook_ThinkPost); } void SDKHooks::Hook_Touch(CBaseEntity *pOther) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_Touch, pOther); if(result >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_TouchPost(CBaseEntity *pOther) { Call(META_IFACEPTR(CBaseEntity), SDKHook_TouchPost, pOther); RETURN_META(MRES_IGNORED); } #if SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013 void SDKHooks::Hook_TraceAttack(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator) #else void SDKHooks::Hook_TraceAttack(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr) #endif { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_TraceAttack]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); int attacker = info.GetAttacker(); int inflictor = info.GetInflictor(); float damage = info.GetDamage(); int damagetype = info.GetDamageType(); int ammotype = info.GetAmmoType(); cell_t res, ret = Pl_Continue; ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCellByRef(&attacker); callback->PushCellByRef(&inflictor); callback->PushFloatByRef(&damage); callback->PushCellByRef(&damagetype); callback->PushCellByRef(&ammotype); callback->PushCell(ptr->hitbox); callback->PushCell(ptr->hitgroup); callback->Execute(&res); if(res > ret) { ret = res; if(ret == Pl_Changed) { CBaseEntity *pEntAttacker = gamehelpers->ReferenceToEntity(attacker); if(!pEntAttacker) { callback->GetParentRuntime()->GetDefaultContext()->ThrowNativeError("Entity %d for attacker is invalid", attacker); RETURN_META(MRES_IGNORED); } CBaseEntity *pEntInflictor = gamehelpers->ReferenceToEntity(inflictor); if(!pEntInflictor) { callback->GetParentRuntime()->GetDefaultContext()->ThrowNativeError("Entity %d for inflictor is invalid", inflictor); RETURN_META(MRES_IGNORED); } info.SetAttacker(pEntAttacker); info.SetInflictor(pEntInflictor); info.SetDamage(damage); info.SetDamageType(damagetype); info.SetAmmoType(ammotype); } } } if(ret >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); if(ret == Pl_Changed) RETURN_META(MRES_HANDLED); break; } RETURN_META(MRES_IGNORED); } #if SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013 void SDKHooks::Hook_TraceAttackPost(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator) #else void SDKHooks::Hook_TraceAttackPost(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr) #endif { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_TraceAttackPost]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCell(info.GetAttacker()); callback->PushCell(info.GetInflictor()); callback->PushFloat(info.GetDamage()); callback->PushCell(info.GetDamageType()); callback->PushCell(info.GetAmmoType()); callback->PushCell(ptr->hitbox); callback->PushCell(ptr->hitgroup); callback->Execute(NULL); } break; } RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_Use]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); int activator = gamehelpers->EntityToBCompatRef(pActivator); int caller = gamehelpers->EntityToBCompatRef(pCaller); cell_t ret = Pl_Continue; ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCell(activator); callback->PushCell(caller); callback->PushCell(useType); callback->PushFloat(value); callback->Execute(&ret); } if (ret >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); break; } RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_UsePost(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); CVTableHook vhook(pEntity); ke::Vector &vtablehooklist = g_HookList[SDKHook_UsePost]; for (size_t entry = 0; entry < vtablehooklist.length(); ++entry) { if (vhook != vtablehooklist[entry]->vtablehook) { continue; } int entity = gamehelpers->EntityToBCompatRef(pEntity); int activator = gamehelpers->EntityToBCompatRef(pActivator); int caller = gamehelpers->EntityToBCompatRef(pCaller); ke::Vector pawnlist; CopyEntityVector(vtablehooklist[entry]->hooks, pawnlist, entity); for (entry = 0; entry < pawnlist.length(); ++entry) { IPluginFunction *callback = pawnlist[entry]; callback->PushCell(entity); callback->PushCell(activator); callback->PushCell(caller); callback->PushCell(useType); callback->PushFloat(value); callback->Execute(NULL); } break; } RETURN_META(MRES_IGNORED); } void SDKHooks::OnEntityDeleted(CBaseEntity *pEntity) { int ref = gamehelpers->EntityToBCompatRef(pEntity); int index = gamehelpers->ReferenceToIndex(ref); // This can be -1 for player ents before any players have connected if ((unsigned)index == INVALID_EHANDLE_INDEX || (index > 0 && index <= playerhelpers->GetMaxClients())) { return; } HandleEntityDeleted(pEntity, ref); } void SDKHooks::Hook_VPhysicsUpdate(IPhysicsObject *pPhysics) { Call(META_IFACEPTR(CBaseEntity), SDKHook_VPhysicsUpdate); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_VPhysicsUpdatePost(IPhysicsObject *pPhysics) { Call(META_IFACEPTR(CBaseEntity), SDKHook_VPhysicsUpdatePost); } bool SDKHooks::Hook_WeaponCanSwitchTo(CBaseCombatWeapon *pWeapon) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponCanSwitchTo, pWeapon); if(result >= Pl_Handled) RETURN_META_VALUE(MRES_SUPERCEDE, false); RETURN_META_VALUE(MRES_IGNORED, true); } bool SDKHooks::Hook_WeaponCanSwitchToPost(CBaseCombatWeapon *pWeapon) { Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponCanSwitchToPost, pWeapon); RETURN_META_VALUE(MRES_IGNORED, true); } bool SDKHooks::Hook_WeaponCanUse(CBaseCombatWeapon *pWeapon) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponCanUse, pWeapon); if(result >= Pl_Handled) RETURN_META_VALUE(MRES_SUPERCEDE, false); RETURN_META_VALUE(MRES_IGNORED, true); } bool SDKHooks::Hook_WeaponCanUsePost(CBaseCombatWeapon *pWeapon) { Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponCanUsePost, pWeapon); RETURN_META_VALUE(MRES_IGNORED, true); } void SDKHooks::Hook_WeaponDrop(CBaseCombatWeapon *pWeapon, const Vector *pvecTarget, const Vector *pVelocity) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponDrop, pWeapon); if(result >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_WeaponDropPost(CBaseCombatWeapon *pWeapon, const Vector *pvecTarget, const Vector *pVelocity) { Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponDropPost, pWeapon); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_WeaponEquip(CBaseCombatWeapon *pWeapon) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponEquip, pWeapon); if(result >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_WeaponEquipPost(CBaseCombatWeapon *pWeapon) { Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponEquipPost, pWeapon); RETURN_META(MRES_IGNORED); } bool SDKHooks::Hook_WeaponSwitch(CBaseCombatWeapon *pWeapon, int viewmodelindex) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponSwitch, pWeapon); if(result >= Pl_Handled) RETURN_META_VALUE(MRES_SUPERCEDE, false); RETURN_META_VALUE(MRES_IGNORED, true); } bool SDKHooks::Hook_WeaponSwitchPost(CBaseCombatWeapon *pWeapon, int viewmodelindex) { cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponSwitchPost, pWeapon); RETURN_META_VALUE(MRES_IGNORED, true); } void SDKHooks::HandleEntityCreated(CBaseEntity *pEntity, int ref) { const char *pName = gamehelpers->GetEntityClassname(pEntity); // Send OnEntityCreated to SM listeners SourceHook::List::iterator iter; ISMEntityListener *pListener = NULL; for (iter = m_EntListeners.begin(); iter != m_EntListeners.end(); iter++) { pListener = (*iter); pListener->OnEntityCreated(pEntity, pName ? pName : ""); } // Call OnEntityCreated forward g_pOnEntityCreated->PushCell(ref); g_pOnEntityCreated->PushString(pName ? pName : ""); g_pOnEntityCreated->Execute(NULL); m_EntityExists.Set(gamehelpers->ReferenceToIndex(ref)); } void SDKHooks::HandleEntityDeleted(CBaseEntity *pEntity, int ref) { // Send OnEntityDestroyed to SM listeners SourceHook::List::iterator iter; ISMEntityListener *pListener = NULL; for (iter = m_EntListeners.begin(); iter != m_EntListeners.end(); iter++) { pListener = (*iter); pListener->OnEntityDestroyed(pEntity); } // Call OnEntityDestroyed forward g_pOnEntityDestroyed->PushCell(ref); g_pOnEntityDestroyed->Execute(NULL); Unhook(pEntity); m_EntityExists.Set(gamehelpers->ReferenceToIndex(ref), false); }