/** * 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; CUtlVector g_HookList; CBitVec m_EntityExists; IBinTools *g_pBinTools = NULL; ICvar *icvar = NULL; // 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]; /** * 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_MANUALHOOK0_void(GroundEntChanged, 0, 0, 0); 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); SH_DECL_MANUALHOOK0_void(Spawn, 0, 0, 0); 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_ORANGEBOXVALVE || SOURCE_ENGINE == SE_CSS SH_DECL_MANUALHOOK4_void(TraceAttack, 0, 0, 0, CTakeDamageInfoHack &, const Vector &, CGameTrace *, void *); #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->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; } void *gEntList = gamehelpers->GetGlobalEntityList(); if (!gEntList) { g_pSM->Format(error, maxlength, "Cannot find gEntList pointer"); return false; } int offset = -1; /* 65572 */ if (!g_pGameConf->GetOffset("EntityListeners", &offset)) { g_pSM->Format(error, maxlength, "Cannot find EntityListeners offset"); return false; } CUtlVector *pListeners = (CUtlVector *)((intptr_t)gEntList + offset); pListeners->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 == INVALID_EHANDLE_INDEX || !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_MEMFUNC(IServerGameDLL, LevelInit, gamedll, &g_Interface, &SDKHooks::Hook_LevelInit, false); assert(g_hookOnGetMapEntitiesString == 0); g_hookOnGetMapEntitiesString = SH_ADD_HOOK_MEMFUNC(IVEngineServer, GetMapEntitiesString, engine, &g_Interface, &SDKHooks::Hook_GetMapEntitiesString, false); } #ifdef GAMEDESC_CAN_CHANGE inline void HookGetGameDescription() { assert(g_hookOnGetGameDescription == 0); g_hookOnGetGameDescription = SH_ADD_HOOK_MEMFUNC(IServerGameDLL, GetGameDescription, gamedll, &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 HOOKLOOP Unhook(i); 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"); void *gEntList = gamehelpers->GetGlobalEntityList(); if (gEntList) { int offset = -1; /* 65572 */ if (g_pGameConf->GetOffset("EntityListeners", &offset)) { CUtlVector *pListeners = (CUtlVector *)((intptr_t)gEntList + offset); pListeners->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 g_pCVar = icvar; #endif CONVAR_REGISTER(this); gpGlobals = ismm->GetCGlobals(); return true; } const char *SDKHooks::GetExtensionVerString() { return SM_VERSION_STRING; } const char *SDKHooks::GetExtensionDateString() { return SM_BUILD_TIMESTAMP; } 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) { IPluginContext *plugincontext = plugin->GetBaseContext(); HOOKLOOP { if(g_HookList[i].callback->GetParentContext() == plugincontext) Unhook(i); } 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) { g_pOnEntityCreated->PushCell(client); CBaseEntity *pPlayer = gamehelpers->ReferenceToEntity(client); const char * pName = gamehelpers->GetEntityClassname(pPlayer); g_pOnEntityCreated->PushString(pName ? pName : ""); g_pOnEntityCreated->Execute(NULL); m_EntityExists.Set(client); } 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 */ cell_t SDKHooks::Call(int entity, SDKHookType type, int other) { IPluginFunction *callback = NULL; cell_t res, ret = Pl_Continue; HOOKLOOP { if(g_HookList[i].entity != entity || g_HookList[i].type != type) continue; callback = g_HookList[i].callback; callback->PushCell(entity); if(other > -2) callback->PushCell(other); callback->Execute(&res); if(res > ret) ret = res; } return ret; } cell_t SDKHooks::Call(CBaseEntity *pEnt, SDKHookType type, int other) { return Call(gamehelpers->EntityToBCompatRef(pEnt), type, other); } cell_t SDKHooks::Call(CBaseEntity *pEnt, SDKHookType type, CBaseEntity *other) { return Call(gamehelpers->EntityToBCompatRef(pEnt), type, gamehelpers->EntityToBCompatRef(other)); } 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 = UTIL_GetCBaseEntity(entity); if(!pEnt) return HookRet_InvalidEntity; if(type < 0 || type >= SDKHook_MAXHOOKS) return HookRet_InvalidHookType; if (!!strcmp(g_HookTypes[type].dtReq, "")) { sm_sendprop_info_t spi; IServerUnknown *pUnk = (IServerUnknown *)pEnt; IServerNetworkable *pNet = pUnk->GetNetworkable(); if (pNet && !UTIL_FindDataTable(pNet->GetServerClass()->m_pTable, g_HookTypes[type].dtReq, &spi, 0)) return HookRet_BadEntForHookType; } bool bHooked = false; HOOKLOOP { if (g_HookList[i].entity == entity && g_HookList[i].type == type) { bHooked = true; break; } } if (!bHooked) { switch(type) { case SDKHook_EndTouch: SH_ADD_MANUALHOOK_MEMFUNC(EndTouch, pEnt, &g_Interface, &SDKHooks::Hook_EndTouch, false); break; case SDKHook_EndTouchPost: SH_ADD_MANUALHOOK_MEMFUNC(EndTouch, pEnt, &g_Interface, &SDKHooks::Hook_EndTouchPost, true); break; case SDKHook_FireBulletsPost: SH_ADD_MANUALHOOK_MEMFUNC(FireBullets, pEnt, &g_Interface, &SDKHooks::Hook_FireBulletsPost, true); break; #ifdef GETMAXHEALTH_IS_VIRTUAL case SDKHook_GetMaxHealth: SH_ADD_MANUALHOOK_MEMFUNC(GetMaxHealth, pEnt, &g_Interface, &SDKHooks::Hook_GetMaxHealth, false); break; #endif case SDKHook_GroundEntChangedPost: SH_ADD_MANUALHOOK_MEMFUNC(GroundEntChanged, pEnt, &g_Interface, &SDKHooks::Hook_GroundEntChangedPost, true); break; case SDKHook_OnTakeDamage: SH_ADD_MANUALHOOK_MEMFUNC(OnTakeDamage, pEnt, &g_Interface, &SDKHooks::Hook_OnTakeDamage, false); break; case SDKHook_OnTakeDamagePost: SH_ADD_MANUALHOOK_MEMFUNC(OnTakeDamage, pEnt, &g_Interface, &SDKHooks::Hook_OnTakeDamagePost, true); break; case SDKHook_PreThink: SH_ADD_MANUALHOOK_MEMFUNC(PreThink, pEnt, &g_Interface, &SDKHooks::Hook_PreThink, false); break; case SDKHook_PreThinkPost: SH_ADD_MANUALHOOK_MEMFUNC(PreThink, pEnt, &g_Interface, &SDKHooks::Hook_PreThinkPost, true); break; case SDKHook_PostThink: SH_ADD_MANUALHOOK_MEMFUNC(PostThink, pEnt, &g_Interface, &SDKHooks::Hook_PostThink, false); break; case SDKHook_PostThinkPost: SH_ADD_MANUALHOOK_MEMFUNC(PostThink, pEnt, &g_Interface, &SDKHooks::Hook_PostThinkPost, true); break; case SDKHook_Reload: SH_ADD_MANUALHOOK_MEMFUNC(Reload, pEnt, &g_Interface, &SDKHooks::Hook_Reload, false); break; case SDKHook_ReloadPost: SH_ADD_MANUALHOOK_MEMFUNC(Reload, pEnt, &g_Interface, &SDKHooks::Hook_ReloadPost, true); break; case SDKHook_SetTransmit: SH_ADD_MANUALHOOK_MEMFUNC(SetTransmit, pEnt, &g_Interface, &SDKHooks::Hook_SetTransmit, false); break; case SDKHook_Spawn: SH_ADD_MANUALHOOK_MEMFUNC(Spawn, pEnt, &g_Interface, &SDKHooks::Hook_Spawn, false); break; case SDKHook_SpawnPost: SH_ADD_MANUALHOOK_MEMFUNC(Spawn, pEnt, &g_Interface, &SDKHooks::Hook_SpawnPost, true); break; case SDKHook_StartTouch: SH_ADD_MANUALHOOK_MEMFUNC(StartTouch, pEnt, &g_Interface, &SDKHooks::Hook_StartTouch, false); break; case SDKHook_StartTouchPost: SH_ADD_MANUALHOOK_MEMFUNC(StartTouch, pEnt, &g_Interface, &SDKHooks::Hook_StartTouchPost, true); break; case SDKHook_Think: SH_ADD_MANUALHOOK_MEMFUNC(Think, pEnt, &g_Interface, &SDKHooks::Hook_Think, false); break; case SDKHook_ThinkPost: SH_ADD_MANUALHOOK_MEMFUNC(Think, pEnt, &g_Interface, &SDKHooks::Hook_ThinkPost, true); break; case SDKHook_Touch: SH_ADD_MANUALHOOK_MEMFUNC(Touch, pEnt, &g_Interface, &SDKHooks::Hook_Touch, false); break; case SDKHook_TouchPost: SH_ADD_MANUALHOOK_MEMFUNC(Touch, pEnt, &g_Interface, &SDKHooks::Hook_TouchPost, true); break; case SDKHook_TraceAttack: SH_ADD_MANUALHOOK_MEMFUNC(TraceAttack, pEnt, &g_Interface, &SDKHooks::Hook_TraceAttack, false); break; case SDKHook_TraceAttackPost: SH_ADD_MANUALHOOK_MEMFUNC(TraceAttack, pEnt, &g_Interface, &SDKHooks::Hook_TraceAttackPost, true); break; case SDKHook_Use: SH_ADD_MANUALHOOK_MEMFUNC(Use, pEnt, &g_Interface, &SDKHooks::Hook_Use, false); break; case SDKHook_UsePost: SH_ADD_MANUALHOOK_MEMFUNC(Use, pEnt, &g_Interface, &SDKHooks::Hook_UsePost, true); break; case SDKHook_VPhysicsUpdate: SH_ADD_MANUALHOOK_MEMFUNC(VPhysicsUpdate, pEnt, &g_Interface, &SDKHooks::Hook_VPhysicsUpdate, false); break; case SDKHook_VPhysicsUpdatePost: SH_ADD_MANUALHOOK_MEMFUNC(VPhysicsUpdate, pEnt, &g_Interface, &SDKHooks::Hook_VPhysicsUpdatePost, true); break; case SDKHook_WeaponCanSwitchTo: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_CanSwitchTo, pEnt, &g_Interface, &SDKHooks::Hook_WeaponCanSwitchTo, false); break; case SDKHook_WeaponCanSwitchToPost: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_CanSwitchTo, pEnt, &g_Interface, &SDKHooks::Hook_WeaponCanSwitchToPost, true); break; case SDKHook_WeaponCanUse: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_CanUse, pEnt, &g_Interface, &SDKHooks::Hook_WeaponCanUse, false); break; case SDKHook_WeaponCanUsePost: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_CanUse, pEnt, &g_Interface, &SDKHooks::Hook_WeaponCanUsePost, true); break; case SDKHook_WeaponDrop: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_Drop, pEnt, &g_Interface, &SDKHooks::Hook_WeaponDrop, false); break; case SDKHook_WeaponDropPost: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_Drop, pEnt, &g_Interface, &SDKHooks::Hook_WeaponDropPost, true); break; case SDKHook_WeaponEquip: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_Equip, pEnt, &g_Interface, &SDKHooks::Hook_WeaponEquip, false); break; case SDKHook_WeaponEquipPost: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_Equip, pEnt, &g_Interface, &SDKHooks::Hook_WeaponEquipPost, true); break; case SDKHook_WeaponSwitch: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_Switch, pEnt, &g_Interface, &SDKHooks::Hook_WeaponSwitch, false); break; case SDKHook_WeaponSwitchPost: SH_ADD_MANUALHOOK_MEMFUNC(Weapon_Switch, pEnt, &g_Interface, &SDKHooks::Hook_WeaponSwitchPost, true); break; case SDKHook_ShouldCollide: SH_ADD_MANUALHOOK_MEMFUNC(ShouldCollide, pEnt, &g_Interface, &SDKHooks::Hook_ShouldCollide, false); break; } } // Add hook to hook list HookList hook; hook.entity = entity; hook.type = type; hook.callback = callback; g_HookList.AddToTail(hook); #ifdef SDKHOOKSDEBUG META_CONPRINTF("DEBUG: Adding to hooklist (ent%d, type%s, cb%d). Total hook count %d\n", entity, g_szHookNames[type], callback, g_HookList.Count()); #endif return HookRet_Successful; } void SDKHooks::Unhook(int index) { CBaseEntity *pEnt = gamehelpers->ReferenceToEntity(g_HookList[index].entity); if(!pEnt) return; int iHooks = 0; HOOKLOOP { if (g_HookList[i].entity == g_HookList[index].entity && g_HookList[i].type == g_HookList[index].type) { iHooks++; #ifdef SDKHOOKSDEBUG META_CONPRINTF("DEBUG: Found hook %d on entity %d\n", i, g_HookList[index].entity); #endif } } if (iHooks == 1) { #ifdef SDKHOOKSDEBUG META_CONPRINTF("DEBUG: Removing hook for hooktype %d\n", g_HookList[index].type); #endif switch(g_HookList[index].type) { case SDKHook_EndTouch: SH_REMOVE_MANUALHOOK_MEMFUNC(EndTouch, pEnt, &g_Interface, &SDKHooks::Hook_EndTouch, false); break; case SDKHook_EndTouchPost: SH_REMOVE_MANUALHOOK_MEMFUNC(EndTouch, pEnt, &g_Interface, &SDKHooks::Hook_EndTouchPost, true); break; case SDKHook_FireBulletsPost: SH_REMOVE_MANUALHOOK_MEMFUNC(FireBullets, pEnt, &g_Interface, &SDKHooks::Hook_FireBulletsPost, true); break; #ifdef GETMAXHEALTH_IS_VIRTUAL case SDKHook_GetMaxHealth: SH_REMOVE_MANUALHOOK_MEMFUNC(GetMaxHealth, pEnt, &g_Interface, &SDKHooks::Hook_GetMaxHealth, false); break; #endif case SDKHook_GroundEntChangedPost: SH_REMOVE_MANUALHOOK_MEMFUNC(GroundEntChanged, pEnt, &g_Interface, &SDKHooks::Hook_GroundEntChangedPost, true); break; case SDKHook_OnTakeDamage: SH_REMOVE_MANUALHOOK_MEMFUNC(OnTakeDamage, pEnt, &g_Interface, &SDKHooks::Hook_OnTakeDamage, false); break; case SDKHook_OnTakeDamagePost: SH_REMOVE_MANUALHOOK_MEMFUNC(OnTakeDamage, pEnt, &g_Interface, &SDKHooks::Hook_OnTakeDamagePost, true); break; case SDKHook_PreThink: SH_REMOVE_MANUALHOOK_MEMFUNC(PreThink, pEnt, &g_Interface, &SDKHooks::Hook_PreThink, false); break; case SDKHook_PreThinkPost: SH_REMOVE_MANUALHOOK_MEMFUNC(PreThink, pEnt, &g_Interface, &SDKHooks::Hook_PreThinkPost, true); break; case SDKHook_PostThink: SH_REMOVE_MANUALHOOK_MEMFUNC(PostThink, pEnt, &g_Interface, &SDKHooks::Hook_PostThink, false); break; case SDKHook_PostThinkPost: SH_REMOVE_MANUALHOOK_MEMFUNC(PostThink, pEnt, &g_Interface, &SDKHooks::Hook_PostThinkPost, true); break; case SDKHook_Reload: SH_REMOVE_MANUALHOOK_MEMFUNC(Reload, pEnt, &g_Interface, &SDKHooks::Hook_Reload, false); break; case SDKHook_ReloadPost: SH_REMOVE_MANUALHOOK_MEMFUNC(Reload, pEnt, &g_Interface, &SDKHooks::Hook_ReloadPost, true); break; case SDKHook_SetTransmit: SH_REMOVE_MANUALHOOK_MEMFUNC(SetTransmit, pEnt, &g_Interface, &SDKHooks::Hook_SetTransmit, false); break; case SDKHook_Spawn: SH_REMOVE_MANUALHOOK_MEMFUNC(Spawn, pEnt, &g_Interface, &SDKHooks::Hook_Spawn, false); break; case SDKHook_SpawnPost: SH_REMOVE_MANUALHOOK_MEMFUNC(Spawn, pEnt, &g_Interface, &SDKHooks::Hook_SpawnPost, true); break; case SDKHook_StartTouch: SH_REMOVE_MANUALHOOK_MEMFUNC(StartTouch, pEnt, &g_Interface, &SDKHooks::Hook_StartTouch, false); break; case SDKHook_StartTouchPost: SH_REMOVE_MANUALHOOK_MEMFUNC(StartTouch, pEnt, &g_Interface, &SDKHooks::Hook_StartTouchPost, true); break; case SDKHook_Think: SH_REMOVE_MANUALHOOK_MEMFUNC(Think, pEnt, &g_Interface, &SDKHooks::Hook_Think, false); break; case SDKHook_ThinkPost: SH_REMOVE_MANUALHOOK_MEMFUNC(Think, pEnt, &g_Interface, &SDKHooks::Hook_ThinkPost, true); break; case SDKHook_Touch: SH_REMOVE_MANUALHOOK_MEMFUNC(Touch, pEnt, &g_Interface, &SDKHooks::Hook_Touch, false); break; case SDKHook_TouchPost: SH_REMOVE_MANUALHOOK_MEMFUNC(Touch, pEnt, &g_Interface, &SDKHooks::Hook_TouchPost, true); break; case SDKHook_TraceAttack: SH_REMOVE_MANUALHOOK_MEMFUNC(TraceAttack, pEnt, &g_Interface, &SDKHooks::Hook_TraceAttack, false); break; case SDKHook_TraceAttackPost: SH_REMOVE_MANUALHOOK_MEMFUNC(TraceAttack, pEnt, &g_Interface, &SDKHooks::Hook_TraceAttackPost, true); break; case SDKHook_Use: SH_REMOVE_MANUALHOOK_MEMFUNC(Use, pEnt, &g_Interface, &SDKHooks::Hook_Use, false); break; case SDKHook_UsePost: SH_REMOVE_MANUALHOOK_MEMFUNC(Use, pEnt, &g_Interface, &SDKHooks::Hook_UsePost, true); break; case SDKHook_VPhysicsUpdate: SH_REMOVE_MANUALHOOK_MEMFUNC(VPhysicsUpdate, pEnt, &g_Interface, &SDKHooks::Hook_VPhysicsUpdate, false); break; case SDKHook_VPhysicsUpdatePost: SH_REMOVE_MANUALHOOK_MEMFUNC(VPhysicsUpdate, pEnt, &g_Interface, &SDKHooks::Hook_VPhysicsUpdatePost, true); break; case SDKHook_WeaponCanSwitchTo: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_CanSwitchTo, pEnt, &g_Interface, &SDKHooks::Hook_WeaponCanSwitchTo, false); break; case SDKHook_WeaponCanSwitchToPost: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_CanSwitchTo, pEnt, &g_Interface, &SDKHooks::Hook_WeaponCanSwitchToPost, true); break; case SDKHook_WeaponCanUse: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_CanUse, pEnt, &g_Interface, &SDKHooks::Hook_WeaponCanUse, false); break; case SDKHook_WeaponCanUsePost: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_CanUse, pEnt, &g_Interface, &SDKHooks::Hook_WeaponCanUsePost, true); break; case SDKHook_WeaponDrop: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_Drop, pEnt, &g_Interface, &SDKHooks::Hook_WeaponDrop, false); break; case SDKHook_WeaponDropPost: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_Drop, pEnt, &g_Interface, &SDKHooks::Hook_WeaponDropPost, true); break; case SDKHook_WeaponEquip: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_Equip, pEnt, &g_Interface, &SDKHooks::Hook_WeaponEquip, false); break; case SDKHook_WeaponEquipPost: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_Equip, pEnt, &g_Interface, &SDKHooks::Hook_WeaponEquipPost, true); break; case SDKHook_WeaponSwitch: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_Switch, pEnt, &g_Interface, &SDKHooks::Hook_WeaponSwitch, false); break; case SDKHook_WeaponSwitchPost: SH_REMOVE_MANUALHOOK_MEMFUNC(Weapon_Switch, pEnt, &g_Interface, &SDKHooks::Hook_WeaponSwitchPost, true); break; case SDKHook_ShouldCollide: SH_REMOVE_MANUALHOOK_MEMFUNC(ShouldCollide, pEnt, &g_Interface, &SDKHooks::Hook_ShouldCollide, false); break; default: return; } } g_HookList.Remove(index); } /** * IEntityFactoryDictionary, IServerGameDLL & IVEngineServer Hook Handlers */ void SDKHooks::OnEntityCreated(CBaseEntity *pEntity) { // Call OnEntityCreated forward int entity = gamehelpers->ReferenceToIndex(gamehelpers->EntityToBCompatRef(pEntity)); if (m_EntityExists.IsBitSet(entity) || (entity > 0 && entity <= playerhelpers->GetMaxClients())) { return; } g_pOnEntityCreated->PushCell(gamehelpers->EntityToBCompatRef(pEntity)); const char * pName = gamehelpers->GetEntityClassname(pEntity); g_pOnEntityCreated->PushString(pName ? pName : ""); g_pOnEntityCreated->Execute(NULL); m_EntityExists.Set(entity); } #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) { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(entity); if(!pPlayer) RETURN_META(MRES_IGNORED); IPlayerInfo *pInfo = pPlayer->GetPlayerInfo(); if(!pInfo) RETURN_META(MRES_IGNORED); const char *weapon = pInfo->GetWeaponName(); IPluginFunction *callback = NULL; HOOKLOOP { if(g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_FireBulletsPost) continue; callback = g_HookList[i].callback; callback->PushCell(entity); callback->PushCell(info.m_iShots); callback->PushString(weapon?weapon:""); callback->Execute(NULL); } RETURN_META(MRES_IGNORED); } #ifdef GETMAXHEALTH_IS_VIRTUAL int SDKHooks::Hook_GetMaxHealth() { CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity); int original_max = SH_MCALL(pEntity, GetMaxHealth)(); int entity = gamehelpers->EntityToBCompatRef(pEntity); int new_max = original_max; cell_t res; IPluginFunction *callback = NULL; HOOKLOOP { if (g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_GetMaxHealth) continue; callback = g_HookList[i].callback; callback->PushCell(entity); callback->PushCellByRef(&new_max); callback->Execute(&res); } if (res >= Pl_Changed) RETURN_META_VALUE(MRES_SUPERCEDE, new_max); RETURN_META_VALUE(MRES_IGNORED, original_max); } #endif void SDKHooks::Hook_GroundEntChangedPost() { Call(META_IFACEPTR(CBaseEntity), SDKHook_GroundEntChangedPost); } int SDKHooks::Hook_OnTakeDamage(CTakeDamageInfoHack &info) { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); 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)}; IPluginFunction *callback = NULL; cell_t res, ret = Pl_Continue; HOOKLOOP { if(g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_OnTakeDamage) continue; callback = g_HookList[i].callback; 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_Handled) RETURN_META_VALUE(MRES_SUPERCEDE, 1); if(ret == Pl_Changed) { CBaseEntity *pEntAttacker = gamehelpers->ReferenceToEntity(attacker); if(!pEntAttacker) { callback->GetParentContext()->ThrowNativeError("Entity %d for attacker is invalid", attacker); RETURN_META_VALUE(MRES_IGNORED, 0); } CBaseEntity *pEntInflictor = gamehelpers->ReferenceToEntity(inflictor); if(!pEntInflictor) { callback->GetParentContext()->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])); RETURN_META_VALUE(MRES_HANDLED, 1); } RETURN_META_VALUE(MRES_IGNORED, 0); } int SDKHooks::Hook_OnTakeDamagePost(CTakeDamageInfoHack &info) { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); IPluginFunction *callback = NULL; HOOKLOOP { if(g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_OnTakeDamagePost) continue; callback = g_HookList[i].callback; 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); } RETURN_META_VALUE(MRES_IGNORED, 0); } void SDKHooks::Hook_PreThink() { Call(META_IFACEPTR(CBaseEntity), SDKHook_PreThink); } void SDKHooks::Hook_PreThinkPost() { Call(META_IFACEPTR(CBaseEntity), SDKHook_PreThinkPost); } void SDKHooks::Hook_PostThink() { Call(META_IFACEPTR(CBaseEntity), SDKHook_PostThink); } void SDKHooks::Hook_PostThinkPost() { Call(META_IFACEPTR(CBaseEntity), SDKHook_PostThinkPost); } bool SDKHooks::Hook_Reload() { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); cell_t res; IPluginFunction *callback = NULL; HOOKLOOP { if (g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_Reload) continue; callback = g_HookList[i].callback; callback->PushCell(entity); callback->Execute(&res); } if (res >= Pl_Handled) RETURN_META_VALUE(MRES_SUPERCEDE, false); RETURN_META_VALUE(MRES_IGNORED, true); } bool SDKHooks::Hook_ReloadPost() { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); IPluginFunction *callback = NULL; HOOKLOOP { if (g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_ReloadPost) continue; callback = g_HookList[i].callback; callback->PushCell(entity); callback->PushCell(META_RESULT_OVERRIDE_RET(bool) ? 1 : 0); callback->Execute(NULL); } 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) { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); IPluginFunction *callback = NULL; bool origRet = ((META_RESULT_STATUS >= MRES_OVERRIDE)?(META_RESULT_OVERRIDE_RET(bool)):(META_RESULT_ORIG_RET(bool))); cell_t res; HOOKLOOP { if (g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_ShouldCollide) continue; callback = g_HookList[i].callback; callback->PushCell(entity); callback->PushCell(collisionGroup); callback->PushCell(contentsMask); callback->PushCell(origRet ? 1 : 0); callback->Execute(&res); } bool ret = false; if (res != 0) { ret = true; } RETURN_META_VALUE(MRES_SUPERCEDE, ret); } void SDKHooks::Hook_Spawn() { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); IPluginFunction *callback = NULL; cell_t res; HOOKLOOP { if (g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_Spawn) continue; callback = g_HookList[i].callback; callback->PushCell(entity); callback->Execute(&res); } if (res >= Pl_Handled) RETURN_META(MRES_SUPERCEDE); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_SpawnPost() { 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); } 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_ORANGEBOXVALVE || SOURCE_ENGINE == SE_CSS void SDKHooks::Hook_TraceAttack(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr, void *pUnknownJK) #else void SDKHooks::Hook_TraceAttack(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr) #endif { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); int attacker = info.GetAttacker(); int inflictor = info.GetInflictor(); float damage = info.GetDamage(); int damagetype = info.GetDamageType(); int ammotype = info.GetAmmoType(); IPluginFunction *callback = NULL; cell_t res, ret = Pl_Continue; HOOKLOOP { if(g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_TraceAttack) continue; callback = g_HookList[i].callback; 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_Handled) RETURN_META(MRES_SUPERCEDE); if(ret == Pl_Changed) { CBaseEntity *pEntAttacker = gamehelpers->ReferenceToEntity(attacker); if(!pEntAttacker) { callback->GetParentContext()->ThrowNativeError("Entity %d for attacker is invalid", attacker); RETURN_META(MRES_IGNORED); } CBaseEntity *pEntInflictor = gamehelpers->ReferenceToEntity(inflictor); if(!pEntInflictor) { callback->GetParentContext()->ThrowNativeError("Entity %d for inflictor is invalid", inflictor); RETURN_META(MRES_IGNORED); } info.SetAttacker(gamehelpers->ReferenceToEntity(attacker)); info.SetInflictor(gamehelpers->ReferenceToEntity(inflictor)); info.SetDamage(damage); info.SetDamageType(damagetype); info.SetAmmoType(ammotype); RETURN_META(MRES_HANDLED); } RETURN_META(MRES_IGNORED); } #if SOURCE_ENGINE == SE_ORANGEBOXVALVE || SOURCE_ENGINE == SE_CSS void SDKHooks::Hook_TraceAttackPost(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr, void *pUnknownJK) #else void SDKHooks::Hook_TraceAttackPost(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr) #endif { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); IPluginFunction *callback = NULL; HOOKLOOP { if(g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_TraceAttackPost) continue; callback = g_HookList[i].callback; 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); } RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); int activator = gamehelpers->EntityToBCompatRef(pActivator); int caller = gamehelpers->EntityToBCompatRef(pCaller); cell_t ret; IPluginFunction *callback = NULL; HOOKLOOP { if(g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_Use) continue; callback = g_HookList[i].callback; 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); RETURN_META(MRES_IGNORED); } void SDKHooks::Hook_UsePost(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value) { int entity = gamehelpers->EntityToBCompatRef(META_IFACEPTR(CBaseEntity)); int activator = gamehelpers->EntityToBCompatRef(pActivator); int caller = gamehelpers->EntityToBCompatRef(pCaller); IPluginFunction *callback = NULL; HOOKLOOP { if (g_HookList[i].entity != entity || g_HookList[i].type != SDKHook_UsePost) continue; callback = g_HookList[i].callback; callback->PushCell(entity); callback->PushCell(activator); callback->PushCell(caller); callback->PushCell(useType); callback->PushFloat(value); callback->Execute(NULL); } RETURN_META(MRES_IGNORED); } void SDKHooks::OnEntityDeleted(CBaseEntity *pEntity) { int entity = gamehelpers->EntityToBCompatRef(pEntity); // Call OnEntityDestroyed forward g_pOnEntityDestroyed->PushCell(entity); g_pOnEntityDestroyed->Execute(NULL); SDKHooks::RemoveEntityHooks(pEntity); m_EntityExists.Set(gamehelpers->ReferenceToIndex(entity), false); } void SDKHooks::Hook_VPhysicsUpdate(IPhysicsObject *pPhysics) { Call(META_IFACEPTR(CBaseEntity), SDKHook_VPhysicsUpdate); } 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::RemoveEntityHooks(CBaseEntity *pEnt) { int entity = gamehelpers->EntityToBCompatRef(pEnt); // Remove hooks HOOKLOOP { if(g_HookList[i].entity == entity) { #ifdef SDKHOOKSDEBUG META_CONPRINTF("DEBUG: Removing hook #%d on entity %d (UpdateOnRemove or clientdisconnect)\n", i, entity); #endif Unhook(i); } } } CON_COMMAND(sdkhooks_listhooks, "Lists all current hooks") { META_CONPRINTF(" %-24.23s %-18.17s %s\n", "Plugin", "Type", "Entity"); IPlugin *pPlugin; const sm_plugininfo_t *info; for(int i = g_HookList.Count() - 1; i >= 0; i--) { g_HookList[i].callback->GetParentRuntime()->GetDefaultContext()->GetKey(2, (void **)&pPlugin); info = pPlugin->GetPublicInfo(); META_CONPRINTF("%2d. %-24.23s %-18.17s %d\n", i + 1, info->name[0] ? info->name : pPlugin->GetFilename(), g_HookTypes[g_HookList[i].type].name, g_HookList[i].entity); } }