extensions/sdkhooks/extension.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Sat May 10 22:21:42 2014 -0400 (2014-05-10)
changeset 4357 82898b97ddc4
parent 4356 aad66fb7e628
child 4374 a7a9f550d4e6
permissions -rw-r--r--
Fix passing invalid ent to OnEntity forwards (bug 6119, r=KyleS).
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * Source SDK Hooks Extension
     5  * Copyright (C) 2010-2012 Nicholas Hastings
     6  * Copyright (C) 2009-2010 Erik Minekus
     7  * =============================================================================
     8  *
     9  * This program is free software; you can redistribute it and/or modify it under
    10  * the terms of the GNU General Public License, version 3.0, as published by the
    11  * Free Software Foundation.
    12  * 
    13  * This program is distributed in the hope that it will be useful, but WITHOUT
    14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    15  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    16  * details.
    17  *
    18  * You should have received a copy of the GNU General Public License along with
    19  * this program.  If not, see <http://www.gnu.org/licenses/>.
    20  *
    21  * As a special exception, AlliedModders LLC gives you permission to link the
    22  * code of this program (as well as its derivative works) to "Half-Life 2," the
    23  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    24  * by the Valve Corporation.  You must obey the GNU General Public License in
    25  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    26  * this exception to all derivative works.  AlliedModders LLC defines further
    27  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    28  * or <http://www.sourcemod.net/license.php>.
    29  *
    30  * Version: $Id$
    31  */
    32 
    33 #include <sourcemod_version.h>
    34 #include "extension.h"
    35 #include "compat_wrappers.h"
    36 #include "macros.h"
    37 #include "natives.h"
    38 #include <sm_platform.h>
    39 #include <const.h>
    40 
    41 
    42 //#define SDKHOOKSDEBUG
    43 
    44 /**
    45  * Globals
    46  */
    47 
    48 // Order MUST match SDKHookType enum
    49 HookTypeData g_HookTypes[SDKHook_MAXHOOKS] = 
    50 {
    51 //   Hook name                DT required               Supported (always false til later)
    52 	{"EndTouch",              "",                       false},
    53 	{"FireBulletsPost",       "",                       false},
    54 	{"OnTakeDamage",          "",                       false},
    55 	{"OnTakeDamagePost",      "",                       false},
    56 	{"PreThink",              "DT_BasePlayer",          false},
    57 	{"PostThink",             "DT_BasePlayer",          false},
    58 	{"SetTransmit",           "",                       false},
    59 	{"Spawn",                 "",                       false},
    60 	{"StartTouch",            "",                       false},
    61 	{"Think",                 "",                       false},
    62 	{"Touch",                 "",                       false},
    63 	{"TraceAttack",           "",                       false},
    64 	{"TraceAttackPost",       "",                       false},
    65 	{"WeaponCanSwitchTo",     "DT_BaseCombatCharacter", false},
    66 	{"WeaponCanUse",          "DT_BaseCombatCharacter", false},
    67 	{"WeaponDrop",            "DT_BaseCombatCharacter", false},
    68 	{"WeaponEquip",           "DT_BaseCombatCharacter", false},
    69 	{"WeaponSwitch",          "DT_BaseCombatCharacter", false},
    70 	{"ShouldCollide",         "",                       false},
    71 	{"PreThinkPost",          "DT_BasePlayer",          false},
    72 	{"PostThinkPost",         "DT_BasePlayer",          false},
    73 	{"ThinkPost",             "",                       false},
    74 	{"EndTouchPost",          "",                       false},
    75 	{"GroundEntChangedPost",  "",                       false},
    76 	{"SpawnPost",             "",                       false},
    77 	{"StartTouchPost",        "",                       false},
    78 	{"TouchPost",             "",                       false},
    79 	{"VPhysicsUpdate",        "",                       false},
    80 	{"VPhysicsUpdatePost",    "",                       false},
    81 	{"WeaponCanSwitchToPost", "DT_BaseCombatCharacter", false},
    82 	{"WeaponCanUsePost",      "DT_BaseCombatCharacter", false},
    83 	{"WeaponDropPost",        "DT_BaseCombatCharacter", false},
    84 	{"WeaponEquipPost",       "DT_BaseCombatCharacter", false},
    85 	{"WeaponSwitchPost",      "DT_BaseCombatCharacter", false},
    86 	{"Use",                   "",                       false},
    87 	{"UsePost",               "",                       false},
    88 	{"Reload",                "DT_BaseCombatWeapon",    false},
    89 	{"ReloadPost",            "DT_BaseCombatWeapon",    false},
    90 	{"GetMaxHealth",          "",                       false},
    91 };
    92 
    93 SDKHooks g_Interface;
    94 SMEXT_LINK(&g_Interface);
    95 
    96 CGlobalVars *gpGlobals;
    97 ke::Vector<CVTableList *> g_HookList[SDKHook_MAXHOOKS];
    98 
    99 CBitVec<NUM_ENT_ENTRIES> m_EntityExists;
   100 
   101 IBinTools *g_pBinTools = NULL;
   102 ICvar *icvar = NULL;
   103 
   104 #if SOURCE_ENGINE >= SE_ORANGEBOX
   105 IServerTools *servertools = NULL;
   106 #endif
   107 
   108 // global hooks and forwards
   109 IForward *g_pOnEntityCreated = NULL;
   110 IForward *g_pOnEntityDestroyed = NULL;
   111 
   112 #ifdef GAMEDESC_CAN_CHANGE
   113 int g_hookOnGetGameDescription = 0;
   114 IForward *g_pOnGetGameNameDescription = NULL;
   115 #endif
   116 
   117 int g_hookOnGetMapEntitiesString = 0;
   118 int g_hookOnLevelInit = 0;
   119 IForward *g_pOnLevelInit = NULL;
   120 
   121 IGameConfig *g_pGameConf = NULL;
   122 
   123 char g_szMapEntities[2097152];
   124 
   125 CUtlVector<IEntityListener *> *EntListeners()
   126 {
   127 	void *gEntList = gamehelpers->GetGlobalEntityList();
   128 	if (gEntList)
   129 	{
   130 		int offset = -1; /* 65572 */
   131 		if (g_pGameConf->GetOffset("EntityListeners", &offset))
   132 		{
   133 			return (CUtlVector<IEntityListener *> *)((intptr_t) gEntList + offset);
   134 		}
   135 	}
   136 	else
   137 	{
   138 		void *entListeners;
   139 		if (g_pGameConf->GetAddress("EntityListenersPtr", &entListeners))
   140 		{
   141 			return (CUtlVector<IEntityListener *> *)entListeners;
   142 		}
   143 	}
   144 
   145 	return NULL;
   146 }
   147 
   148 
   149 /**
   150  * IServerGameDLL & IVEngineServer Hooks
   151  */
   152 SH_DECL_HOOK6(IServerGameDLL, LevelInit, SH_NOATTRIB, 0, bool, const char *, const char *, const char *, const char *, bool, bool);
   153 #ifdef GAMEDESC_CAN_CHANGE
   154 SH_DECL_HOOK0(IServerGameDLL, GetGameDescription, SH_NOATTRIB, 0, const char *);
   155 #endif
   156 SH_DECL_HOOK0(IVEngineServer, GetMapEntitiesString, SH_NOATTRIB, 0, const char *);
   157 
   158 
   159 /**
   160  * CBaseEntity Hooks
   161  */
   162 SH_DECL_MANUALHOOK1_void(EndTouch, 0, 0, 0, CBaseEntity *);
   163 SH_DECL_MANUALHOOK1_void(FireBullets, 0, 0, 0, FireBulletsInfo_t const&);
   164 #ifdef GETMAXHEALTH_IS_VIRTUAL
   165 SH_DECL_MANUALHOOK0(GetMaxHealth, 0, 0, 0, int);
   166 #endif
   167 SH_DECL_MANUALHOOK1_void(GroundEntChanged, 0, 0, 0, void *);
   168 SH_DECL_MANUALHOOK1(OnTakeDamage, 0, 0, 0, int, CTakeDamageInfoHack &);
   169 SH_DECL_MANUALHOOK0_void(PreThink, 0, 0, 0);
   170 SH_DECL_MANUALHOOK0_void(PostThink, 0, 0, 0);
   171 SH_DECL_MANUALHOOK0(Reload, 0, 0, 0, bool);
   172 SH_DECL_MANUALHOOK2_void(SetTransmit, 0, 0, 0, CCheckTransmitInfo *, bool);
   173 SH_DECL_MANUALHOOK2(ShouldCollide, 0, 0, 0, bool, int, int);
   174 #if SOURCE_ENGINE == SE_DOTA
   175 SH_DECL_MANUALHOOK1_void(Spawn, 0, 0, 0, CEntityKeyValues *);
   176 #else
   177 SH_DECL_MANUALHOOK0_void(Spawn, 0, 0, 0);
   178 #endif
   179 SH_DECL_MANUALHOOK1_void(StartTouch, 0, 0, 0, CBaseEntity *);
   180 SH_DECL_MANUALHOOK0_void(Think, 0, 0, 0);
   181 SH_DECL_MANUALHOOK1_void(Touch, 0, 0, 0, CBaseEntity *);
   182 #if SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013
   183 SH_DECL_MANUALHOOK4_void(TraceAttack, 0, 0, 0, CTakeDamageInfoHack &, const Vector &, CGameTrace *, CDmgAccumulator *);
   184 #else
   185 SH_DECL_MANUALHOOK3_void(TraceAttack, 0, 0, 0, CTakeDamageInfoHack &, const Vector &, CGameTrace *);
   186 #endif
   187 SH_DECL_MANUALHOOK4_void(Use, 0, 0, 0, CBaseEntity *, CBaseEntity *, USE_TYPE, float);
   188 SH_DECL_MANUALHOOK1_void(VPhysicsUpdate, 0, 0, 0, IPhysicsObject *);
   189 SH_DECL_MANUALHOOK1(Weapon_CanSwitchTo, 0, 0, 0, bool, CBaseCombatWeapon *);
   190 SH_DECL_MANUALHOOK1(Weapon_CanUse, 0, 0, 0, bool, CBaseCombatWeapon *);
   191 SH_DECL_MANUALHOOK3_void(Weapon_Drop, 0, 0, 0, CBaseCombatWeapon *, const Vector *, const Vector *);
   192 SH_DECL_MANUALHOOK1_void(Weapon_Equip, 0, 0, 0, CBaseCombatWeapon *);
   193 SH_DECL_MANUALHOOK2(Weapon_Switch, 0, 0, 0, bool, CBaseCombatWeapon *, int);
   194 
   195 
   196 /**
   197  * Forwards
   198  */
   199 bool SDKHooks::SDK_OnLoad(char *error, size_t maxlength, bool late)
   200 {
   201 	char buffer[256];
   202 	g_pSM->BuildPath(Path_SM, buffer, sizeof(buffer)-1, "/extensions/sdkhooks.ext." PLATFORM_LIB_EXT);
   203 	if (libsys->PathExists(buffer) && libsys->IsPathFile(buffer))
   204 	{
   205 		g_pSM->Format(error, maxlength-1, "SDKHooks 2.x cannot load while old version (sdkhooks.ext." PLATFORM_LIB_EXT ") is still in extensions dir");
   206 		return false;
   207 	}
   208 
   209 	g_pSM->BuildPath(Path_SM, buffer, sizeof(buffer)-1, "/gamedata/sdkhooks.games.txt");
   210 	if (libsys->PathExists(buffer) && libsys->IsPathFile(buffer))
   211 	{
   212 		g_pSM->Format(error, maxlength-1, "SDKHooks 2.x cannot load while old gamedata file (sdkhooks.games.txt) is still in gamedata dir");
   213 		return false;
   214 	}
   215 
   216 	sharesys->AddDependency(myself, "bintools.ext", true, true);
   217 	sharesys->AddNatives(myself, g_Natives);
   218 	sharesys->RegisterLibrary(myself, "sdkhooks");
   219 	sharesys->AddInterface(myself, &g_Interface);
   220 	sharesys->AddCapabilityProvider(myself, this, "SDKHook_DmgCustomInOTD");
   221 	sharesys->AddCapabilityProvider(myself, this, "SDKHook_LogicalEntSupport");
   222 
   223 	playerhelpers->AddClientListener(&g_Interface);
   224 	
   225 	plsys->AddPluginsListener(&g_Interface);
   226 
   227 	g_pOnEntityCreated = forwards->CreateForward("OnEntityCreated", ET_Ignore, 2, NULL, Param_Cell, Param_String);
   228 	g_pOnEntityDestroyed = forwards->CreateForward("OnEntityDestroyed", ET_Ignore, 1, NULL, Param_Cell);
   229 #ifdef GAMEDESC_CAN_CHANGE
   230 	g_pOnGetGameNameDescription = forwards->CreateForward("OnGetGameDescription", ET_Hook, 2, NULL, Param_String);
   231 #endif
   232 	g_pOnLevelInit = forwards->CreateForward("OnLevelInit", ET_Hook, 2, NULL, Param_String, Param_String);
   233 
   234 	buffer[0] = '\0';
   235 	if (!gameconfs->LoadGameConfigFile("sdkhooks.games", &g_pGameConf, buffer, sizeof(buffer)))
   236 	{
   237 		if (buffer[0])
   238 		{
   239 			g_pSM->Format(error, maxlength, "Could not read sdkhooks.games gamedata: %s", buffer);
   240 		}
   241 		
   242 		return false;
   243 	}
   244 
   245 	CUtlVector<IEntityListener *> *entListeners = EntListeners();
   246 	if (!entListeners)
   247 	{
   248 		g_pSM->Format(error, maxlength, "Failed to setup entity listeners");
   249 		return false;
   250 	}
   251 
   252 	entListeners->AddToTail(this);
   253 
   254 	SetupHooks();
   255 
   256 #if SOURCE_ENGINE >= SE_ORANGEBOX
   257 	int index;
   258 	CBaseHandle hndl;
   259 	for (IHandleEntity *pEnt = (IHandleEntity *)servertools->FirstEntity(); pEnt; pEnt = (IHandleEntity *)servertools->NextEntity((CBaseEntity *)pEnt))
   260 	{
   261 		hndl = pEnt->GetRefEHandle();
   262 		if (!hndl.IsValid())
   263 			continue;
   264 		
   265 		index = hndl.GetEntryIndex();
   266 		m_EntityExists.Set(index);
   267 	}
   268 #else
   269 	for (int i = 0; i < NUM_ENT_ENTRIES; i++)
   270 	{
   271 		if (gamehelpers->ReferenceToEntity(i) != NULL)
   272 			m_EntityExists.Set(i);
   273 	}
   274 #endif
   275 
   276 	return true;
   277 }
   278 
   279 inline void HookLevelInit()
   280 {
   281 	assert(g_hookOnLevelInit == 0);
   282 	g_hookOnLevelInit = SH_ADD_HOOK(IServerGameDLL, LevelInit, gamedll, SH_MEMBER(&g_Interface, &SDKHooks::Hook_LevelInit), false);
   283 	assert(g_hookOnGetMapEntitiesString == 0);
   284 	g_hookOnGetMapEntitiesString = SH_ADD_HOOK(IVEngineServer, GetMapEntitiesString, engine, SH_MEMBER(&g_Interface, &SDKHooks::Hook_GetMapEntitiesString), false);
   285 }
   286 
   287 #ifdef GAMEDESC_CAN_CHANGE
   288 inline void HookGetGameDescription()
   289 {
   290 	assert(g_hookOnGetGameDescription == 0);
   291 	g_hookOnGetGameDescription = SH_ADD_HOOK(IServerGameDLL, GetGameDescription, gamedll, SH_MEMBER(&g_Interface, &SDKHooks::Hook_GetGameDescription), false);
   292 }
   293 #endif
   294 
   295 void SDKHooks::SDK_OnAllLoaded()
   296 {
   297 	SM_GET_LATE_IFACE(BINTOOLS, g_pBinTools);
   298 
   299 	if (!g_pBinTools)
   300 	{
   301 		g_pSM->LogError(myself, "Could not find interface: " SMINTERFACE_BINTOOLS_NAME);
   302 		return;
   303 	}
   304 
   305 	if (g_pOnLevelInit->GetFunctionCount() > 0)
   306 		HookLevelInit();
   307 #ifdef GAMEDESC_CAN_CHANGE
   308 	if (g_pOnGetGameNameDescription->GetFunctionCount() > 0)
   309 		HookGetGameDescription();
   310 #endif
   311 }
   312 
   313 #define KILL_HOOK_IF_ACTIVE(hook) \
   314 	if (hook != 0) \
   315 	{ \
   316 		SH_REMOVE_HOOK_ID(hook); \
   317 	}
   318 
   319 void SDKHooks::SDK_OnUnload()
   320 {
   321 	// Remove left over hooks
   322 	Unhook(reinterpret_cast<SourcePawn::IPluginContext *>(NULL));
   323 
   324 	KILL_HOOK_IF_ACTIVE(g_hookOnLevelInit);
   325 	KILL_HOOK_IF_ACTIVE(g_hookOnGetMapEntitiesString);
   326 
   327 #ifdef GAMEDESC_CAN_CHANGE
   328 	KILL_HOOK_IF_ACTIVE(g_hookOnGetGameDescription);
   329 #endif
   330 
   331 	forwards->ReleaseForward(g_pOnEntityCreated);
   332 	forwards->ReleaseForward(g_pOnEntityDestroyed);
   333 #ifdef GAMEDESC_CAN_CHANGE
   334 	forwards->ReleaseForward(g_pOnGetGameNameDescription);
   335 #endif
   336 	forwards->ReleaseForward(g_pOnLevelInit);
   337 
   338 	plsys->RemovePluginsListener(&g_Interface);
   339 
   340 	playerhelpers->RemoveClientListener(&g_Interface);
   341 
   342 	sharesys->DropCapabilityProvider(myself, this, "SDKHook_DmgCustomInOTD");
   343 	sharesys->DropCapabilityProvider(myself, this, "SDKHook_LogicalEntSupport");
   344 
   345 	CUtlVector<IEntityListener *> *entListeners = EntListeners();
   346 	entListeners->FindAndRemove(this);
   347 
   348 	gameconfs->CloseGameConfigFile(g_pGameConf);
   349 }
   350 
   351 bool SDKHooks::SDK_OnMetamodLoad(ISmmAPI *ismm, char *error, size_t maxlen, bool late)
   352 {
   353 	GET_V_IFACE_CURRENT(GetEngineFactory, icvar, ICvar, CVAR_INTERFACE_VERSION);
   354 
   355 #if SOURCE_ENGINE >= SE_ORANGEBOX
   356 	GET_V_IFACE_ANY(GetServerFactory, servertools, IServerTools, VSERVERTOOLS_INTERFACE_VERSION);
   357 	g_pCVar = icvar;
   358 #endif
   359 	CONVAR_REGISTER(this);
   360 	
   361 	gpGlobals = ismm->GetCGlobals();
   362 
   363 	return true;
   364 }
   365 
   366 const char *SDKHooks::GetExtensionVerString()
   367 {
   368 	return SOURCEMOD_VERSION;
   369 }
   370 
   371 const char *SDKHooks::GetExtensionDateString()
   372 {
   373 	return SOURCEMOD_BUILD_TIME;
   374 }
   375 
   376 void SDKHooks::OnPluginLoaded(IPlugin *plugin)
   377 {
   378 	if (g_pOnLevelInit->GetFunctionCount() > 0 && g_hookOnLevelInit == 0)
   379 		HookLevelInit();
   380 
   381 #ifdef GAMEDESC_CAN_CHANGE
   382 	if (g_pOnGetGameNameDescription->GetFunctionCount() > 0 && g_hookOnGetGameDescription == 0)
   383 		HookGetGameDescription();
   384 #endif
   385 }
   386 
   387 void SDKHooks::OnPluginUnloaded(IPlugin *plugin)
   388 {
   389 	Unhook(plugin->GetBaseContext());
   390 
   391 	if (g_pOnLevelInit->GetFunctionCount() == 0)
   392 	{
   393 		KILL_HOOK_IF_ACTIVE(g_hookOnLevelInit);
   394 		KILL_HOOK_IF_ACTIVE(g_hookOnGetMapEntitiesString);
   395 	}
   396 
   397 #ifdef GAMEDESC_CAN_CHANGE
   398 	if (g_pOnGetGameNameDescription->GetFunctionCount() == 0)
   399 		KILL_HOOK_IF_ACTIVE(g_hookOnGetGameDescription);
   400 #endif
   401 }
   402 
   403 void SDKHooks::OnClientPutInServer(int client)
   404 {
   405 	CBaseEntity *pPlayer = gamehelpers->ReferenceToEntity(client);
   406 
   407 	HandleEntityCreated(pPlayer, client);
   408 }
   409 
   410 void SDKHooks::OnClientDisconnecting(int client)
   411 {
   412 	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(client);
   413 	
   414 	HandleEntityDeleted(pEntity, client);
   415 }
   416 
   417 void SDKHooks::AddEntityListener(ISMEntityListener *listener)
   418 {
   419 	m_EntListeners.push_back(listener);
   420 }
   421 
   422 void SDKHooks::RemoveEntityListener(ISMEntityListener *listener)
   423 {
   424 	m_EntListeners.remove(listener);
   425 }
   426 
   427 bool SDKHooks::RegisterConCommandBase(ConCommandBase *pVar)
   428 {
   429 	/* Always call META_REGCVAR instead of going through the engine. */
   430 	return META_REGCVAR(pVar);
   431 }
   432 
   433 FeatureStatus SDKHooks::GetFeatureStatus(FeatureType type, const char *name)
   434 {
   435 	return FeatureStatus_Available;
   436 }
   437 
   438 /**
   439  * Functions
   440  */
   441 cell_t SDKHooks::Call(int entity, SDKHookType type, int other)
   442 {
   443 	return Call(gamehelpers->ReferenceToEntity(entity), type, gamehelpers->ReferenceToEntity(other));
   444 }
   445 
   446 cell_t SDKHooks::Call(CBaseEntity *pEnt, SDKHookType type, int other)
   447 {
   448 	return Call(pEnt, type, gamehelpers->ReferenceToEntity(other));
   449 }
   450 
   451 cell_t SDKHooks::Call(CBaseEntity *pEnt, SDKHookType type, CBaseEntity *pOther)
   452 {
   453 	cell_t ret = Pl_Continue;
   454 
   455 	CVTableHook vhook(pEnt);
   456 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[type];
   457 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
   458 	{
   459 		if (vhook != vtablehooklist[entry]->vtablehook)
   460 		{
   461 			continue;
   462 		}
   463 
   464 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
   465 		int entity = gamehelpers->EntityToBCompatRef(pEnt);
   466 		int other = gamehelpers->EntityToBCompatRef(pOther);
   467 		for (entry = 0; entry < pawnlist.length(); ++entry)
   468 		{
   469 			if (entity != pawnlist[entry].entity)
   470 			{
   471 				continue;
   472 			}
   473 
   474 			IPluginFunction *callback = pawnlist[entry].callback;
   475 			callback->PushCell(entity);
   476 			callback->PushCell(other);
   477 
   478 			cell_t res;
   479 			callback->Execute(&res);
   480 			if(res > ret)
   481 			{
   482 				ret = res;
   483 			}
   484 		}
   485 
   486 		break;
   487 	}
   488 
   489 	return ret;
   490 }
   491 
   492 void SDKHooks::SetupHooks()
   493 {
   494 	int offset;
   495 
   496 	//			gamedata          pre    post
   497 	// (pre is not necessarily a prehook, just named without "Post" appeneded)
   498 
   499 	CHECKOFFSET(EndTouch,         true,  true);
   500 	CHECKOFFSET(FireBullets,      false, true);
   501 	CHECKOFFSET(GroundEntChanged, false, true);
   502 	CHECKOFFSET(OnTakeDamage,     true,  true);
   503 	CHECKOFFSET(PreThink,         true,  true);
   504 	CHECKOFFSET(PostThink,        true,  true);
   505 	CHECKOFFSET(Reload,           true,  true);
   506 	CHECKOFFSET(SetTransmit,      true,  false);
   507 	CHECKOFFSET(ShouldCollide,    true,  false);
   508 	CHECKOFFSET(Spawn,            true,  true);
   509 	CHECKOFFSET(StartTouch,       true,  true);
   510 	CHECKOFFSET(Think,            true,  true);
   511 	CHECKOFFSET(Touch,            true,  true);
   512 	CHECKOFFSET(TraceAttack,      true,  true);
   513 	CHECKOFFSET(Use,              true,  true);
   514 	CHECKOFFSET_W(CanSwitchTo,    true,  true);
   515 	CHECKOFFSET_W(CanUse,         true,  true);
   516 	CHECKOFFSET_W(Drop,           true,  true);
   517 	CHECKOFFSET_W(Equip,          true,  true);
   518 	CHECKOFFSET_W(Switch,         true,  true);
   519 	CHECKOFFSET(VPhysicsUpdate,   true,  true);
   520 
   521 	// this one is in a class all its own -_-
   522 	offset = 0;
   523 	g_pGameConf->GetOffset("GroundEntChanged", &offset);
   524 	if (offset > 0)
   525 	{
   526 		SH_MANUALHOOK_RECONFIGURE(GroundEntChanged, offset, 0, 0);
   527 		g_HookTypes[SDKHook_GroundEntChangedPost].supported = true;
   528 	}
   529 
   530 #ifdef GETMAXHEALTH_IS_VIRTUAL
   531 	CHECKOFFSET(GetMaxHealth,	true,	false);
   532 #endif
   533 }
   534 
   535 HookReturn SDKHooks::Hook(int entity, SDKHookType type, IPluginFunction *callback)
   536 {
   537 	if(!g_HookTypes[type].supported)
   538 		return HookRet_NotSupported;
   539 
   540 	CBaseEntity *pEnt = gamehelpers->ReferenceToEntity(entity);
   541 	if(!pEnt)
   542 		return HookRet_InvalidEntity;
   543 	if(type < 0 || type >= SDKHook_MAXHOOKS)
   544 		return HookRet_InvalidHookType;
   545 
   546 	if (!!strcmp(g_HookTypes[type].dtReq, ""))
   547 	{
   548 		IServerUnknown *pUnk = (IServerUnknown *)pEnt;
   549 
   550 		IServerNetworkable *pNet = pUnk->GetNetworkable();
   551 		if (pNet && !UTIL_ContainsDataTable(pNet->GetServerClass()->m_pTable, g_HookTypes[type].dtReq))
   552 			return HookRet_BadEntForHookType;
   553 	}
   554 
   555 	size_t entry;
   556 	CVTableHook vhook(pEnt);
   557 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[type];
   558 	for (entry = 0; entry < vtablehooklist.length(); ++entry)
   559 	{
   560 		if (vhook == vtablehooklist[entry]->vtablehook)
   561 		{
   562 			break;
   563 		}
   564 	}
   565 
   566 	if (entry == vtablehooklist.length())
   567 	{
   568 		int hookid = 0;
   569 		switch(type)
   570 		{
   571 			case SDKHook_EndTouch:
   572 				hookid = SH_ADD_MANUALVPHOOK(EndTouch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_EndTouch), false);
   573 				break;
   574 			case SDKHook_EndTouchPost:
   575 				hookid = SH_ADD_MANUALVPHOOK(EndTouch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_EndTouchPost), true);
   576 				break;
   577 			case SDKHook_FireBulletsPost:
   578 				hookid = SH_ADD_MANUALVPHOOK(FireBullets, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_FireBulletsPost), true);
   579 				break;
   580 #ifdef GETMAXHEALTH_IS_VIRTUAL
   581 			case SDKHook_GetMaxHealth:
   582 				hookid = SH_ADD_MANUALVPHOOK(GetMaxHealth, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_GetMaxHealth), false);
   583 				break;
   584 #endif
   585 			case SDKHook_GroundEntChangedPost:
   586 				hookid = SH_ADD_MANUALVPHOOK(GroundEntChanged, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_GroundEntChangedPost), true);
   587 				break;
   588 			case SDKHook_OnTakeDamage:
   589 				hookid = SH_ADD_MANUALVPHOOK(OnTakeDamage, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_OnTakeDamage), false);
   590 				break;
   591 			case SDKHook_OnTakeDamagePost:
   592 				hookid = SH_ADD_MANUALVPHOOK(OnTakeDamage, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_OnTakeDamagePost), true);
   593 				break;
   594 			case SDKHook_PreThink:
   595 				hookid = SH_ADD_MANUALVPHOOK(PreThink, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_PreThink), false);
   596 				break;
   597 			case SDKHook_PreThinkPost:
   598 				hookid = SH_ADD_MANUALVPHOOK(PreThink, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_PreThinkPost), true);
   599 				break;
   600 			case SDKHook_PostThink:
   601 				hookid = SH_ADD_MANUALVPHOOK(PostThink, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_PostThink), false);
   602 				break;
   603 			case SDKHook_PostThinkPost:
   604 				hookid = SH_ADD_MANUALVPHOOK(PostThink, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_PostThinkPost), true);
   605 				break;
   606 			case SDKHook_Reload:
   607 				hookid = SH_ADD_MANUALVPHOOK(Reload, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Reload), false);
   608 				break;
   609 			case SDKHook_ReloadPost:
   610 				hookid = SH_ADD_MANUALVPHOOK(Reload, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_ReloadPost), true);
   611 				break;
   612 			case SDKHook_SetTransmit:
   613 				hookid = SH_ADD_MANUALVPHOOK(SetTransmit, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_SetTransmit), false);
   614 				break;
   615 			case SDKHook_Spawn:
   616 				hookid = SH_ADD_MANUALVPHOOK(Spawn, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Spawn), false);
   617 				break;
   618 			case SDKHook_SpawnPost:
   619 				hookid = SH_ADD_MANUALVPHOOK(Spawn, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_SpawnPost), true);
   620 				break;
   621 			case SDKHook_StartTouch:
   622 				hookid = SH_ADD_MANUALVPHOOK(StartTouch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_StartTouch), false);
   623 				break;
   624 			case SDKHook_StartTouchPost:
   625 				hookid = SH_ADD_MANUALVPHOOK(StartTouch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_StartTouchPost), true);
   626 				break;
   627 			case SDKHook_Think:
   628 				hookid = SH_ADD_MANUALVPHOOK(Think, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Think), false);
   629 				break;
   630 			case SDKHook_ThinkPost:
   631 				hookid = SH_ADD_MANUALVPHOOK(Think, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_ThinkPost), true);
   632 				break;
   633 			case SDKHook_Touch:
   634 				hookid = SH_ADD_MANUALVPHOOK(Touch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Touch), false);
   635 				break;
   636 			case SDKHook_TouchPost:
   637 				hookid = SH_ADD_MANUALVPHOOK(Touch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_TouchPost), true);
   638 				break;
   639 			case SDKHook_TraceAttack:
   640 				hookid = SH_ADD_MANUALVPHOOK(TraceAttack, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_TraceAttack), false);
   641 				break;
   642 			case SDKHook_TraceAttackPost:
   643 				hookid = SH_ADD_MANUALVPHOOK(TraceAttack, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_TraceAttackPost), true);
   644 				break;
   645 			case SDKHook_Use:
   646 				hookid = SH_ADD_MANUALVPHOOK(Use, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_Use), false);
   647 				break;
   648 			case SDKHook_UsePost:
   649 				hookid = SH_ADD_MANUALVPHOOK(Use, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_UsePost), true);
   650 				break;
   651 			case SDKHook_VPhysicsUpdate:
   652 				hookid = SH_ADD_MANUALVPHOOK(VPhysicsUpdate, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_VPhysicsUpdate), false);
   653 				break;
   654 			case SDKHook_VPhysicsUpdatePost:
   655 				hookid = SH_ADD_MANUALVPHOOK(VPhysicsUpdate, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_VPhysicsUpdatePost), true);
   656 				break;
   657 			case SDKHook_WeaponCanSwitchTo:
   658 				hookid = SH_ADD_MANUALVPHOOK(Weapon_CanSwitchTo, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponCanSwitchTo), false);
   659 				break;
   660 			case SDKHook_WeaponCanSwitchToPost:
   661 				hookid = SH_ADD_MANUALVPHOOK(Weapon_CanSwitchTo, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponCanSwitchToPost), true);
   662 				break;
   663 			case SDKHook_WeaponCanUse:
   664 				hookid = SH_ADD_MANUALVPHOOK(Weapon_CanUse, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponCanUse), false);
   665 				break;
   666 			case SDKHook_WeaponCanUsePost:
   667 				hookid = SH_ADD_MANUALVPHOOK(Weapon_CanUse, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponCanUsePost), true);
   668 				break;
   669 			case SDKHook_WeaponDrop:
   670 				hookid = SH_ADD_MANUALVPHOOK(Weapon_Drop, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponDrop), false);
   671 				break;
   672 			case SDKHook_WeaponDropPost:
   673 				hookid = SH_ADD_MANUALVPHOOK(Weapon_Drop, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponDropPost), true);
   674 				break;
   675 			case SDKHook_WeaponEquip:
   676 				hookid = SH_ADD_MANUALVPHOOK(Weapon_Equip, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponEquip), false);
   677 				break;
   678 			case SDKHook_WeaponEquipPost:
   679 				hookid = SH_ADD_MANUALVPHOOK(Weapon_Equip, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponEquipPost), true);
   680 				break;
   681 			case SDKHook_WeaponSwitch:
   682 				hookid = SH_ADD_MANUALVPHOOK(Weapon_Switch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponSwitch), false);
   683 				break;
   684 			case SDKHook_WeaponSwitchPost:
   685 				hookid = SH_ADD_MANUALVPHOOK(Weapon_Switch, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_WeaponSwitchPost), true);
   686 				break;
   687 			case SDKHook_ShouldCollide:
   688 				hookid = SH_ADD_MANUALVPHOOK(ShouldCollide, pEnt, SH_MEMBER(&g_Interface, &SDKHooks::Hook_ShouldCollide), false);
   689 				break;
   690 		}
   691 
   692 		vhook.SetHookID(hookid);
   693 
   694 		CVTableList *vtablelist = new CVTableList;
   695 		vtablelist->vtablehook = new CVTableHook(vhook);
   696 		vtablehooklist.append(vtablelist);
   697 	}
   698 	
   699 	// Add hook to hook list
   700 	HookList hook;
   701 	hook.entity = gamehelpers->EntityToBCompatRef(pEnt);
   702 	hook.callback = callback;
   703 	vtablehooklist[entry]->hooks.append(hook);
   704 
   705 	return HookRet_Successful;
   706 }
   707 
   708 void SDKHooks::Unhook(CBaseEntity *pEntity)
   709 {
   710 	if (pEntity == NULL)
   711 	{
   712 		return;
   713 	}
   714 
   715 	int entity = gamehelpers->EntityToBCompatRef(pEntity);
   716 	for (size_t type = 0; type < SDKHook_MAXHOOKS; ++type)
   717 	{
   718 		ke::Vector<CVTableList *> &vtablehooklist = g_HookList[type];
   719 		for (size_t listentry = 0; listentry < vtablehooklist.length(); ++listentry)
   720 		{
   721 			ke::Vector<HookList> &pawnhooks = vtablehooklist[listentry]->hooks;
   722 			for (size_t entry = 0; entry < pawnhooks.length(); ++entry)
   723 			{
   724 				if (entity != pawnhooks[entry].entity)
   725 				{
   726 					continue;
   727 				}
   728 
   729 				pawnhooks.remove(entry--);
   730 			}
   731 
   732 			if (pawnhooks.length() == 0)
   733 			{
   734 				delete vtablehooklist[listentry];
   735 				vtablehooklist.remove(listentry--);
   736 			}
   737 		}
   738 	}
   739 }
   740 
   741 void SDKHooks::Unhook(IPluginContext *pContext)
   742 {
   743 	for (size_t type = 0; type < SDKHook_MAXHOOKS; ++type)
   744 	{
   745 		ke::Vector<CVTableList *> &vtablehooklist = g_HookList[type];
   746 		for (size_t listentry = 0; listentry < vtablehooklist.length(); ++listentry)
   747 		{
   748 			ke::Vector<HookList> &pawnhooks = vtablehooklist[listentry]->hooks;
   749 			for (size_t entry = 0; entry < pawnhooks.length(); ++entry)
   750 			{
   751 				if (pContext != NULL && pContext != pawnhooks[entry].callback->GetParentRuntime()->GetDefaultContext())
   752 				{
   753 					continue;
   754 				}
   755 
   756 				pawnhooks.remove(entry--);
   757 			}
   758 
   759 			if (pawnhooks.length() == 0)
   760 			{
   761 				delete vtablehooklist[listentry];
   762 				vtablehooklist.remove(listentry--);
   763 			}
   764 		}
   765 	}
   766 }
   767 
   768 void SDKHooks::Unhook(int entity, SDKHookType type, IPluginFunction *pCallback)
   769 {
   770 	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(entity);
   771 	if (pEntity == NULL)
   772 	{
   773 		return;
   774 	}
   775 
   776 	CVTableHook vhook(pEntity);
   777 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[type];
   778 	for (size_t listentry = 0; listentry < vtablehooklist.length(); ++listentry)
   779 	{
   780 		if (vhook != vtablehooklist[listentry]->vtablehook)
   781 		{
   782 			continue;
   783 		}
   784 
   785 		entity = gamehelpers->EntityToBCompatRef(pEntity);
   786 
   787 		ke::Vector<HookList> &pawnhooks = vtablehooklist[listentry]->hooks;
   788 		for (size_t entry = 0; entry < pawnhooks.length(); ++entry)
   789 		{
   790 			HookList &hookentry = pawnhooks[entry];
   791 			if (entity != hookentry.entity || pCallback != hookentry.callback)
   792 			{
   793 				continue;
   794 			}
   795 
   796 			pawnhooks.remove(entry--);
   797 		}
   798 
   799 		if (pawnhooks.length() == 0)
   800 		{
   801 			delete vtablehooklist[listentry];
   802 			vtablehooklist.remove(listentry);
   803 		}
   804 
   805 		break;
   806 	}
   807 }
   808 
   809 /**
   810  * IEntityFactoryDictionary, IServerGameDLL & IVEngineServer Hook Handlers
   811  */
   812 void SDKHooks::OnEntityCreated(CBaseEntity *pEntity)
   813 {
   814 	// Call OnEntityCreated forward
   815 	int ref = gamehelpers->EntityToBCompatRef(pEntity);
   816 	int index = gamehelpers->ReferenceToIndex(ref);
   817 
   818 	// This can be -1 for player ents before any players have connected
   819 	if (index == INVALID_EHANDLE_INDEX || m_EntityExists.IsBitSet(index) || (index > 0 && index <= playerhelpers->GetMaxClients()))
   820 	{
   821 		return;
   822 	}
   823 
   824 	HandleEntityCreated(pEntity, ref);
   825 }
   826 
   827 #ifdef GAMEDESC_CAN_CHANGE
   828 const char *SDKHooks::Hook_GetGameDescription()
   829 {
   830 	static char szGameDesc[64];
   831 	cell_t result = Pl_Continue;
   832 
   833 	g_pSM->Format(szGameDesc, sizeof(szGameDesc), "%s",
   834 		SH_CALL(gamedll, &IServerGameDLL::GetGameDescription)());
   835 
   836 	// Call OnGetGameDescription forward
   837 	g_pOnGetGameNameDescription->PushStringEx(szGameDesc, sizeof(szGameDesc), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
   838 	g_pOnGetGameNameDescription->Execute(&result);
   839 
   840 	if(result == Pl_Changed)
   841 		RETURN_META_VALUE(MRES_SUPERCEDE, szGameDesc);
   842 
   843 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   844 }
   845 #endif
   846 
   847 const char *SDKHooks::Hook_GetMapEntitiesString()
   848 {
   849 	if(g_szMapEntities[0])
   850 		RETURN_META_VALUE(MRES_SUPERCEDE, g_szMapEntities);
   851 
   852 	RETURN_META_VALUE(MRES_IGNORED, NULL);
   853 }
   854 
   855 bool SDKHooks::Hook_LevelInit(char const *pMapName, char const *pMapEntities, char const *pOldLevel, char const *pLandmarkName, bool loadGame, bool background)
   856 {
   857 	strcpy(g_szMapEntities, pMapEntities);
   858 	cell_t result = Pl_Continue;
   859 
   860 	// Call OnLevelInit forward
   861 	g_pOnLevelInit->PushString(pMapName);
   862 	g_pOnLevelInit->PushStringEx(g_szMapEntities, sizeof(g_szMapEntities), SM_PARAM_STRING_COPY, SM_PARAM_COPYBACK);
   863 	g_pOnLevelInit->Execute(&result);
   864 
   865 	if(result >= Pl_Handled)
   866 		RETURN_META_VALUE(MRES_SUPERCEDE, false);
   867 
   868 	if(result == Pl_Changed)
   869 		RETURN_META_VALUE_NEWPARAMS(MRES_HANDLED, true, &IServerGameDLL::LevelInit, (pMapName, g_szMapEntities, pOldLevel, pLandmarkName, loadGame, background));
   870 
   871 	RETURN_META_VALUE(MRES_IGNORED, true);
   872 }
   873 
   874 
   875 /**
   876  * CBaseEntity Hook Handlers
   877  */
   878 void SDKHooks::Hook_EndTouch(CBaseEntity *pOther)
   879 {
   880 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_EndTouch, pOther);
   881 	if(result >= Pl_Handled)
   882 		RETURN_META(MRES_SUPERCEDE);
   883 
   884 	RETURN_META(MRES_IGNORED);
   885 }
   886 
   887 void SDKHooks::Hook_EndTouchPost(CBaseEntity *pOther)
   888 {
   889 	Call(META_IFACEPTR(CBaseEntity), SDKHook_EndTouchPost, pOther);
   890 	RETURN_META(MRES_IGNORED);
   891 }
   892 
   893 void SDKHooks::Hook_FireBulletsPost(const FireBulletsInfo_t &info)
   894 {
   895 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
   896 	int entity = gamehelpers->EntityToBCompatRef(pEntity);
   897 
   898 	IGamePlayer *pPlayer = playerhelpers->GetGamePlayer(entity);
   899 	if(!pPlayer)
   900 		RETURN_META(MRES_IGNORED);
   901 
   902 	IPlayerInfo *pInfo = pPlayer->GetPlayerInfo();
   903 	if(!pInfo)
   904 		RETURN_META(MRES_IGNORED);
   905 
   906 	CVTableHook vhook(pEntity);
   907 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_FireBulletsPost];
   908 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
   909 	{
   910 		if (vhook != vtablehooklist[entry]->vtablehook)
   911 		{
   912 			continue;
   913 		}
   914 
   915 		const char *weapon = pInfo->GetWeaponName();
   916 
   917 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
   918 		for (entry = 0; entry < pawnlist.length(); ++entry)
   919 		{
   920 			if (entity != pawnlist[entry].entity)
   921 			{
   922 				continue;
   923 			}
   924 
   925 			IPluginFunction *callback = pawnlist[entry].callback;
   926 			callback->PushCell(entity);
   927 			callback->PushCell(info.m_iShots);
   928 			callback->PushString(weapon?weapon:"");
   929 			callback->Execute(NULL);
   930 		}
   931 
   932 		break;
   933 	}
   934 
   935 	RETURN_META(MRES_IGNORED);
   936 }
   937 
   938 #ifdef GETMAXHEALTH_IS_VIRTUAL
   939 int SDKHooks::Hook_GetMaxHealth()
   940 {
   941 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
   942 	int original_max = SH_MCALL(pEntity, GetMaxHealth)();
   943 
   944 	CVTableHook vhook(pEntity);
   945 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_GetMaxHealth];
   946 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
   947 	{
   948 		if (vhook != vtablehooklist[entry]->vtablehook)
   949 		{
   950 			continue;
   951 		}
   952 
   953 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
   954 
   955 		int new_max = original_max;
   956 
   957 		cell_t res = Pl_Continue;
   958 
   959 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
   960 		for (entry = 0; entry < pawnlist.length(); ++entry)
   961 		{
   962 			if (entity != pawnlist[entry].entity)
   963 			{
   964 				continue;
   965 			}
   966 
   967 			IPluginFunction *callback = pawnlist[entry].callback;
   968 			callback->PushCell(entity);
   969 			callback->PushCellByRef(&new_max);
   970 			callback->Execute(&res);
   971 		}
   972 
   973 		if (res >= Pl_Changed)
   974 			RETURN_META_VALUE(MRES_SUPERCEDE, new_max);
   975 
   976 		break;
   977 	}
   978 
   979 	RETURN_META_VALUE(MRES_IGNORED, original_max);
   980 }
   981 #endif
   982 
   983 void SDKHooks::Hook_GroundEntChangedPost(void *pVar)
   984 {
   985 	Call(META_IFACEPTR(CBaseEntity), SDKHook_GroundEntChangedPost);
   986 }
   987 
   988 int SDKHooks::Hook_OnTakeDamage(CTakeDamageInfoHack &info)
   989 {
   990 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
   991 
   992 	CVTableHook vhook(pEntity);
   993 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_OnTakeDamage];
   994 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
   995 	{
   996 		if (vhook != vtablehooklist[entry]->vtablehook)
   997 		{
   998 			continue;
   999 		}
  1000 
  1001 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1002 		int attacker = info.GetAttacker();
  1003 		int inflictor = info.GetInflictor();
  1004 		float damage = info.GetDamage();
  1005 		int damagetype = info.GetDamageType();
  1006 		int weapon = info.GetWeapon();
  1007 
  1008 		Vector force = info.GetDamageForce();
  1009 		cell_t damageForce[3] = {sp_ftoc(force.x), sp_ftoc(force.y), sp_ftoc(force.z)};
  1010 
  1011 		Vector pos = info.GetDamagePosition();
  1012 		cell_t damagePosition[3] = {sp_ftoc(pos.x), sp_ftoc(pos.y), sp_ftoc(pos.z)};
  1013 
  1014 		cell_t res, ret = Pl_Continue;
  1015 
  1016 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1017 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1018 		{
  1019 			if (entity != pawnlist[entry].entity)
  1020 			{
  1021 				continue;
  1022 			}
  1023 
  1024 			IPluginFunction *callback = pawnlist[entry].callback;
  1025 			callback->PushCell(entity);
  1026 			callback->PushCellByRef(&attacker);
  1027 			callback->PushCellByRef(&inflictor);
  1028 			callback->PushFloatByRef(&damage);
  1029 			callback->PushCellByRef(&damagetype);
  1030 			callback->PushCellByRef(&weapon);
  1031 			callback->PushArray(damageForce, 3, SM_PARAM_COPYBACK);
  1032 			callback->PushArray(damagePosition, 3, SM_PARAM_COPYBACK);	
  1033 			callback->PushCell(info.GetDamageCustom());
  1034 			callback->Execute(&res);
  1035 
  1036 			if(res >= ret)
  1037 			{
  1038 				ret = res;
  1039 				if(ret == Pl_Changed)
  1040 				{
  1041 					CBaseEntity *pEntAttacker = gamehelpers->ReferenceToEntity(attacker);
  1042 					if(!pEntAttacker)
  1043 					{
  1044 						callback->GetParentRuntime()->GetDefaultContext()->ThrowNativeError("Entity %d for attacker is invalid", attacker);
  1045 						RETURN_META_VALUE(MRES_IGNORED, 0);
  1046 					}
  1047 					CBaseEntity *pEntInflictor = gamehelpers->ReferenceToEntity(inflictor);
  1048 					if(!pEntInflictor)
  1049 					{
  1050 						callback->GetParentRuntime()->GetDefaultContext()->ThrowNativeError("Entity %d for inflictor is invalid", inflictor);
  1051 						RETURN_META_VALUE(MRES_IGNORED, 0);
  1052 					}
  1053 
  1054 					info.SetAttacker(pEntAttacker);
  1055 					info.SetInflictor(pEntInflictor);
  1056 					info.SetDamage(damage);
  1057 					info.SetDamageType(damagetype);
  1058 					info.SetWeapon(gamehelpers->ReferenceToEntity(weapon));
  1059 					info.SetDamageForce(
  1060 						sp_ctof(damageForce[0]),
  1061 						sp_ctof(damageForce[1]),
  1062 						sp_ctof(damageForce[2]));
  1063 					info.SetDamagePosition(
  1064 						sp_ctof(damagePosition[0]),
  1065 						sp_ctof(damagePosition[1]),
  1066 						sp_ctof(damagePosition[2]));
  1067 				}
  1068 			}
  1069 		}
  1070 		
  1071 		if(ret >= Pl_Handled)
  1072 			RETURN_META_VALUE(MRES_SUPERCEDE, 1);
  1073 
  1074 		if(ret == Pl_Changed)
  1075 			RETURN_META_VALUE(MRES_HANDLED, 1); 
  1076 
  1077 		break;
  1078 	}
  1079 
  1080 	RETURN_META_VALUE(MRES_IGNORED, 0);
  1081 }
  1082 
  1083 int SDKHooks::Hook_OnTakeDamagePost(CTakeDamageInfoHack &info)
  1084 {
  1085 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1086 
  1087 	CVTableHook vhook(pEntity);
  1088 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_OnTakeDamagePost];
  1089 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1090 	{
  1091 		if (vhook != vtablehooklist[entry]->vtablehook)
  1092 		{
  1093 			continue;
  1094 		}
  1095 
  1096 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1097 
  1098 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1099 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1100 		{
  1101 			if (entity != pawnlist[entry].entity)
  1102 			{
  1103 				continue;
  1104 			}
  1105 
  1106 			IPluginFunction *callback = pawnlist[entry].callback;
  1107 			callback->PushCell(entity);
  1108 			callback->PushCell(info.GetAttacker());
  1109 			callback->PushCell(info.GetInflictor());
  1110 			callback->PushFloat(info.GetDamage());
  1111 			callback->PushCell(info.GetDamageType());
  1112 			callback->PushCell(info.GetWeapon());
  1113 
  1114 			Vector force = info.GetDamageForce();
  1115 			cell_t damageForce[3] = {sp_ftoc(force.x), sp_ftoc(force.y), sp_ftoc(force.z)};
  1116 			callback->PushArray(damageForce, 3);
  1117 
  1118 			Vector pos = info.GetDamagePosition();
  1119 			cell_t damagePosition[3] = {sp_ftoc(pos.x), sp_ftoc(pos.y), sp_ftoc(pos.z)};
  1120 			callback->PushArray(damagePosition, 3);
  1121 
  1122 			callback->PushCell(info.GetDamageCustom());
  1123 
  1124 			callback->Execute(NULL);
  1125 		}
  1126 
  1127 		break;
  1128 	}
  1129 
  1130 	RETURN_META_VALUE(MRES_IGNORED, 0);
  1131 }
  1132 
  1133 void SDKHooks::Hook_PreThink()
  1134 {
  1135 	Call(META_IFACEPTR(CBaseEntity), SDKHook_PreThink);
  1136 	RETURN_META(MRES_IGNORED);
  1137 }
  1138 
  1139 void SDKHooks::Hook_PreThinkPost()
  1140 {
  1141 	Call(META_IFACEPTR(CBaseEntity), SDKHook_PreThinkPost);
  1142 }
  1143 
  1144 void SDKHooks::Hook_PostThink()
  1145 {
  1146 	Call(META_IFACEPTR(CBaseEntity), SDKHook_PostThink);
  1147 	RETURN_META(MRES_IGNORED);
  1148 }
  1149 
  1150 void SDKHooks::Hook_PostThinkPost()
  1151 {
  1152 	Call(META_IFACEPTR(CBaseEntity), SDKHook_PostThinkPost);
  1153 }
  1154 
  1155 bool SDKHooks::Hook_Reload()
  1156 {
  1157 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1158 
  1159 	CVTableHook vhook(pEntity);
  1160 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_Reload];
  1161 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1162 	{
  1163 		if (vhook != vtablehooklist[entry]->vtablehook)
  1164 		{
  1165 			continue;
  1166 		}
  1167 
  1168 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1169 		cell_t res = Pl_Continue;
  1170 
  1171 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1172 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1173 		{
  1174 			if (entity != pawnlist[entry].entity)
  1175 			{
  1176 				continue;
  1177 			}
  1178 
  1179 			IPluginFunction *callback = pawnlist[entry].callback;
  1180 			callback->PushCell(entity);
  1181 			callback->Execute(&res);
  1182 		}
  1183 
  1184 		if (res >= Pl_Handled)
  1185 			RETURN_META_VALUE(MRES_SUPERCEDE, false);
  1186 
  1187 		break;
  1188 	}
  1189 
  1190 	RETURN_META_VALUE(MRES_IGNORED, true);
  1191 }
  1192 
  1193 bool SDKHooks::Hook_ReloadPost()
  1194 {
  1195 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1196 
  1197 	CVTableHook vhook(pEntity);
  1198 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_Reload];
  1199 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1200 	{
  1201 		if (vhook != vtablehooklist[entry]->vtablehook)
  1202 		{
  1203 			continue;
  1204 		}
  1205 
  1206 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1207 		cell_t origreturn = META_RESULT_ORIG_RET(bool) ? 1 : 0;
  1208 
  1209 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1210 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1211 		{
  1212 			if (entity != pawnlist[entry].entity)
  1213 			{
  1214 				continue;
  1215 			}
  1216 
  1217 			IPluginFunction *callback = pawnlist[entry].callback;
  1218 			callback->PushCell(entity);
  1219 			callback->PushCell(origreturn);
  1220 			callback->Execute(NULL);
  1221 		}
  1222 
  1223 		break;
  1224 	}
  1225 
  1226 	return true;
  1227 }
  1228 
  1229 void SDKHooks::Hook_SetTransmit(CCheckTransmitInfo *pInfo, bool bAlways)
  1230 {
  1231 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_SetTransmit, gamehelpers->IndexOfEdict(pInfo->m_pClientEnt));
  1232 
  1233 	if(result >= Pl_Handled)
  1234 		RETURN_META(MRES_SUPERCEDE);
  1235 
  1236 	RETURN_META(MRES_IGNORED);
  1237 }
  1238 
  1239 bool SDKHooks::Hook_ShouldCollide(int collisionGroup, int contentsMask)
  1240 {
  1241 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1242 
  1243 	CVTableHook vhook(pEntity);
  1244 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_ShouldCollide];
  1245 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1246 	{
  1247 		if (vhook != vtablehooklist[entry]->vtablehook)
  1248 		{
  1249 			continue;
  1250 		}
  1251 
  1252 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1253 		cell_t origRet = ((META_RESULT_STATUS >= MRES_OVERRIDE)?(META_RESULT_OVERRIDE_RET(bool)):(META_RESULT_ORIG_RET(bool))) ? 1 : 0;
  1254 		cell_t res = 0;
  1255 
  1256 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1257 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1258 		{
  1259 			if (entity != pawnlist[entry].entity)
  1260 			{
  1261 				continue;
  1262 			}
  1263 
  1264 			IPluginFunction *callback = pawnlist[entry].callback;
  1265 			callback->PushCell(entity);
  1266 			callback->PushCell(collisionGroup);
  1267 			callback->PushCell(contentsMask);
  1268 			callback->PushCell(origRet);
  1269 			callback->Execute(&res);
  1270 		}
  1271 
  1272 		bool ret = false;
  1273 		if (res != 0)
  1274 		{
  1275 			ret = true;
  1276 		}
  1277 
  1278 		RETURN_META_VALUE(MRES_SUPERCEDE, ret);
  1279 	}
  1280 
  1281 	RETURN_META_VALUE(MRES_IGNORED, true);
  1282 }
  1283 
  1284 #if SOURCE_ENGINE == SE_DOTA
  1285 void SDKHooks::Hook_Spawn(CEntityKeyValues *kv)
  1286 #else
  1287 void SDKHooks::Hook_Spawn()
  1288 #endif
  1289 {
  1290 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1291 
  1292 	CVTableHook vhook(pEntity);
  1293 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_Spawn];
  1294 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1295 	{
  1296 		if (vhook != vtablehooklist[entry]->vtablehook)
  1297 		{
  1298 			continue;
  1299 		}
  1300 
  1301 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1302 		cell_t res = Pl_Continue;
  1303 
  1304 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1305 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1306 		{
  1307 			if (entity != pawnlist[entry].entity)
  1308 			{
  1309 				continue;
  1310 			}
  1311 
  1312 			IPluginFunction *callback = pawnlist[entry].callback;
  1313 			callback->PushCell(entity);
  1314 			callback->Execute(&res);
  1315 		}
  1316 
  1317 		if (res >= Pl_Handled)
  1318 			RETURN_META(MRES_SUPERCEDE);
  1319 
  1320 		break;
  1321 	}
  1322 
  1323 	RETURN_META(MRES_IGNORED);
  1324 }
  1325 
  1326 #if SOURCE_ENGINE == SE_DOTA
  1327 void SDKHooks::Hook_SpawnPost(CEntityKeyValues *kv)
  1328 #else
  1329 void SDKHooks::Hook_SpawnPost()
  1330 #endif
  1331 {
  1332 	Call(META_IFACEPTR(CBaseEntity), SDKHook_SpawnPost);
  1333 }
  1334 
  1335 void SDKHooks::Hook_StartTouch(CBaseEntity *pOther)
  1336 {
  1337 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_StartTouch, pOther);
  1338 	if(result >= Pl_Handled)
  1339 		RETURN_META(MRES_SUPERCEDE);
  1340 
  1341 	RETURN_META(MRES_IGNORED);
  1342 }
  1343 
  1344 void SDKHooks::Hook_StartTouchPost(CBaseEntity *pOther)
  1345 {
  1346 	Call(META_IFACEPTR(CBaseEntity), SDKHook_StartTouchPost, pOther);
  1347 	RETURN_META(MRES_IGNORED);
  1348 }
  1349 
  1350 void SDKHooks::Hook_Think()
  1351 {
  1352 	Call(META_IFACEPTR(CBaseEntity), SDKHook_Think);
  1353 	RETURN_META(MRES_IGNORED);
  1354 }
  1355 
  1356 void SDKHooks::Hook_ThinkPost()
  1357 {
  1358 	Call(META_IFACEPTR(CBaseEntity), SDKHook_ThinkPost);
  1359 }
  1360 
  1361 void SDKHooks::Hook_Touch(CBaseEntity *pOther)
  1362 {
  1363 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_Touch, pOther);
  1364 
  1365 	if(result >= Pl_Handled)
  1366 		RETURN_META(MRES_SUPERCEDE);
  1367 
  1368 	RETURN_META(MRES_IGNORED);
  1369 }
  1370 
  1371 void SDKHooks::Hook_TouchPost(CBaseEntity *pOther)
  1372 {
  1373 	Call(META_IFACEPTR(CBaseEntity), SDKHook_TouchPost, pOther);
  1374 	RETURN_META(MRES_IGNORED);
  1375 }
  1376 
  1377 #if SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013
  1378 void SDKHooks::Hook_TraceAttack(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator)
  1379 #else
  1380 void SDKHooks::Hook_TraceAttack(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr)
  1381 #endif
  1382 {
  1383 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1384 
  1385 	CVTableHook vhook(pEntity);
  1386 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_TraceAttack];
  1387 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1388 	{
  1389 		if (vhook != vtablehooklist[entry]->vtablehook)
  1390 		{
  1391 			continue;
  1392 		}
  1393 
  1394 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1395 		int attacker = info.GetAttacker();
  1396 		int inflictor = info.GetInflictor();
  1397 		float damage = info.GetDamage();
  1398 		int damagetype = info.GetDamageType();
  1399 		int ammotype = info.GetAmmoType();
  1400 		cell_t res, ret = Pl_Continue;
  1401 
  1402 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1403 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1404 		{
  1405 			if (entity != pawnlist[entry].entity)
  1406 			{
  1407 				continue;
  1408 			}
  1409 
  1410 			IPluginFunction *callback = pawnlist[entry].callback;
  1411 			callback->PushCell(entity);
  1412 			callback->PushCellByRef(&attacker);
  1413 			callback->PushCellByRef(&inflictor);
  1414 			callback->PushFloatByRef(&damage);
  1415 			callback->PushCellByRef(&damagetype);
  1416 			callback->PushCellByRef(&ammotype);
  1417 			callback->PushCell(ptr->hitbox);
  1418 			callback->PushCell(ptr->hitgroup);
  1419 			callback->Execute(&res);
  1420 
  1421 			if(res > ret)
  1422 			{
  1423 				ret = res;
  1424 
  1425 				if(ret == Pl_Changed)
  1426 				{
  1427 					CBaseEntity *pEntAttacker = gamehelpers->ReferenceToEntity(attacker);
  1428 					if(!pEntAttacker)
  1429 					{
  1430 						callback->GetParentRuntime()->GetDefaultContext()->ThrowNativeError("Entity %d for attacker is invalid", attacker);
  1431 						RETURN_META(MRES_IGNORED);
  1432 					}
  1433 					CBaseEntity *pEntInflictor = gamehelpers->ReferenceToEntity(inflictor);
  1434 					if(!pEntInflictor)
  1435 					{
  1436 						callback->GetParentRuntime()->GetDefaultContext()->ThrowNativeError("Entity %d for inflictor is invalid", inflictor);
  1437 						RETURN_META(MRES_IGNORED);
  1438 					}
  1439 					
  1440 					info.SetAttacker(pEntAttacker);
  1441 					info.SetInflictor(pEntInflictor);
  1442 					info.SetDamage(damage);
  1443 					info.SetDamageType(damagetype);
  1444 					info.SetAmmoType(ammotype);
  1445 				}
  1446 			}
  1447 		}
  1448 
  1449 		if(ret >= Pl_Handled)
  1450 			RETURN_META(MRES_SUPERCEDE);
  1451 
  1452 		if(ret == Pl_Changed)
  1453 			RETURN_META(MRES_HANDLED);
  1454 
  1455 		break;
  1456 	}
  1457 
  1458 	RETURN_META(MRES_IGNORED);
  1459 }
  1460 
  1461 #if SOURCE_ENGINE == SE_HL2DM || SOURCE_ENGINE == SE_DODS || SOURCE_ENGINE == SE_CSS || SOURCE_ENGINE == SE_TF2 || SOURCE_ENGINE == SE_SDK2013
  1462 void SDKHooks::Hook_TraceAttackPost(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator)
  1463 #else
  1464 void SDKHooks::Hook_TraceAttackPost(CTakeDamageInfoHack &info, const Vector &vecDir, trace_t *ptr)
  1465 #endif
  1466 {
  1467 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1468 
  1469 	CVTableHook vhook(pEntity);
  1470 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_TraceAttackPost];
  1471 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1472 	{
  1473 		if (vhook != vtablehooklist[entry]->vtablehook)
  1474 		{
  1475 			continue;
  1476 		}
  1477 
  1478 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1479 
  1480 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1481 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1482 		{
  1483 			if (entity != pawnlist[entry].entity)
  1484 			{
  1485 				continue;
  1486 			}
  1487 
  1488 			IPluginFunction *callback = pawnlist[entry].callback;
  1489 			callback->PushCell(entity);
  1490 			callback->PushCell(info.GetAttacker());
  1491 			callback->PushCell(info.GetInflictor());
  1492 			callback->PushFloat(info.GetDamage());
  1493 			callback->PushCell(info.GetDamageType());
  1494 			callback->PushCell(info.GetAmmoType());
  1495 			callback->PushCell(ptr->hitbox);
  1496 			callback->PushCell(ptr->hitgroup);
  1497 			callback->Execute(NULL);
  1498 		}
  1499 
  1500 		break;
  1501 	}
  1502 
  1503 	RETURN_META(MRES_IGNORED);
  1504 }
  1505 
  1506 void SDKHooks::Hook_Use(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
  1507 {
  1508 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1509 
  1510 	CVTableHook vhook(pEntity);
  1511 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_Use];
  1512 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1513 	{
  1514 		if (vhook != vtablehooklist[entry]->vtablehook)
  1515 		{
  1516 			continue;
  1517 		}
  1518 
  1519 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1520 		int activator = gamehelpers->EntityToBCompatRef(pActivator);
  1521 		int caller = gamehelpers->EntityToBCompatRef(pCaller);
  1522 		cell_t ret = Pl_Continue;
  1523 
  1524 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1525 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1526 		{
  1527 			if (entity != pawnlist[entry].entity)
  1528 			{
  1529 				continue;
  1530 			}
  1531 
  1532 			IPluginFunction *callback = pawnlist[entry].callback;
  1533 			callback->PushCell(entity);
  1534 			callback->PushCell(activator);
  1535 			callback->PushCell(caller);
  1536 			callback->PushCell(useType);
  1537 			callback->PushFloat(value);
  1538 			callback->Execute(&ret);
  1539 		}
  1540 
  1541 		if (ret >= Pl_Handled)
  1542 			RETURN_META(MRES_SUPERCEDE);
  1543 
  1544 		break;
  1545 	}
  1546 
  1547 	RETURN_META(MRES_IGNORED);
  1548 }
  1549 
  1550 void SDKHooks::Hook_UsePost(CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value)
  1551 {
  1552 	CBaseEntity *pEntity = META_IFACEPTR(CBaseEntity);
  1553 
  1554 	CVTableHook vhook(pEntity);
  1555 	ke::Vector<CVTableList *> &vtablehooklist = g_HookList[SDKHook_UsePost];
  1556 	for (size_t entry = 0; entry < vtablehooklist.length(); ++entry)
  1557 	{
  1558 		if (vhook != vtablehooklist[entry]->vtablehook)
  1559 		{
  1560 			continue;
  1561 		}
  1562 
  1563 		int entity = gamehelpers->EntityToBCompatRef(pEntity);
  1564 		int activator = gamehelpers->EntityToBCompatRef(pActivator);
  1565 		int caller = gamehelpers->EntityToBCompatRef(pCaller);
  1566 
  1567 		ke::Vector<HookList> &pawnlist = vtablehooklist[entry]->hooks;
  1568 		for (entry = 0; entry < pawnlist.length(); ++entry)
  1569 		{
  1570 			if (entity != pawnlist[entry].entity)
  1571 			{
  1572 				continue;
  1573 			}
  1574 
  1575 			IPluginFunction *callback = pawnlist[entry].callback;
  1576 			callback->PushCell(entity);
  1577 			callback->PushCell(activator);
  1578 			callback->PushCell(caller);
  1579 			callback->PushCell(useType);
  1580 			callback->PushFloat(value);
  1581 			callback->Execute(NULL);
  1582 		}
  1583 
  1584 		break;
  1585 	}
  1586 
  1587 	RETURN_META(MRES_IGNORED);
  1588 }
  1589 
  1590 void SDKHooks::OnEntityDeleted(CBaseEntity *pEntity)
  1591 {
  1592 	int ref = gamehelpers->EntityToBCompatRef(pEntity);
  1593 	int index = gamehelpers->ReferenceToIndex(ref);
  1594 
  1595 	// This can be -1 for player ents before any players have connected
  1596 	if (index == INVALID_EHANDLE_INDEX || (index > 0 && index <= playerhelpers->GetMaxClients()))
  1597 	{
  1598 		return;
  1599 	}
  1600 
  1601 	HandleEntityDeleted(pEntity, ref);
  1602 }
  1603 
  1604 void SDKHooks::Hook_VPhysicsUpdate(IPhysicsObject *pPhysics)
  1605 {
  1606 	Call(META_IFACEPTR(CBaseEntity), SDKHook_VPhysicsUpdate);
  1607 	RETURN_META(MRES_IGNORED);
  1608 }
  1609 
  1610 void SDKHooks::Hook_VPhysicsUpdatePost(IPhysicsObject *pPhysics)
  1611 {
  1612 	Call(META_IFACEPTR(CBaseEntity), SDKHook_VPhysicsUpdatePost);
  1613 }
  1614 
  1615 bool SDKHooks::Hook_WeaponCanSwitchTo(CBaseCombatWeapon *pWeapon)
  1616 {
  1617 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponCanSwitchTo, pWeapon);
  1618 
  1619 	if(result >= Pl_Handled)
  1620 		RETURN_META_VALUE(MRES_SUPERCEDE, false);
  1621 
  1622 	RETURN_META_VALUE(MRES_IGNORED, true);
  1623 }
  1624 
  1625 bool SDKHooks::Hook_WeaponCanSwitchToPost(CBaseCombatWeapon *pWeapon)
  1626 {
  1627 	Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponCanSwitchToPost, pWeapon);
  1628 	RETURN_META_VALUE(MRES_IGNORED, true);
  1629 }
  1630 
  1631 bool SDKHooks::Hook_WeaponCanUse(CBaseCombatWeapon *pWeapon)
  1632 {
  1633 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponCanUse, pWeapon);
  1634 
  1635 	if(result >= Pl_Handled)
  1636 		RETURN_META_VALUE(MRES_SUPERCEDE, false);
  1637 
  1638 	RETURN_META_VALUE(MRES_IGNORED, true);
  1639 }
  1640 
  1641 bool SDKHooks::Hook_WeaponCanUsePost(CBaseCombatWeapon *pWeapon)
  1642 {
  1643 	Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponCanUsePost, pWeapon);
  1644 	RETURN_META_VALUE(MRES_IGNORED, true);
  1645 }
  1646 
  1647 void SDKHooks::Hook_WeaponDrop(CBaseCombatWeapon *pWeapon, const Vector *pvecTarget, const Vector *pVelocity)
  1648 {
  1649 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponDrop, pWeapon);
  1650 
  1651 	if(result >= Pl_Handled)
  1652 		RETURN_META(MRES_SUPERCEDE);
  1653 
  1654 	RETURN_META(MRES_IGNORED);
  1655 }
  1656 
  1657 void SDKHooks::Hook_WeaponDropPost(CBaseCombatWeapon *pWeapon, const Vector *pvecTarget, const Vector *pVelocity)
  1658 {
  1659 	Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponDropPost, pWeapon);
  1660 	RETURN_META(MRES_IGNORED);
  1661 }
  1662 
  1663 void SDKHooks::Hook_WeaponEquip(CBaseCombatWeapon *pWeapon)
  1664 {
  1665 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponEquip, pWeapon);
  1666 
  1667 	if(result >= Pl_Handled)
  1668 		RETURN_META(MRES_SUPERCEDE);
  1669 
  1670 	RETURN_META(MRES_IGNORED);
  1671 }
  1672 
  1673 void SDKHooks::Hook_WeaponEquipPost(CBaseCombatWeapon *pWeapon)
  1674 {
  1675 	Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponEquipPost, pWeapon);
  1676 	RETURN_META(MRES_IGNORED);
  1677 }
  1678 
  1679 bool SDKHooks::Hook_WeaponSwitch(CBaseCombatWeapon *pWeapon, int viewmodelindex)
  1680 {
  1681 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponSwitch, pWeapon);
  1682 
  1683 	if(result >= Pl_Handled)
  1684 		RETURN_META_VALUE(MRES_SUPERCEDE, false);
  1685 
  1686 	RETURN_META_VALUE(MRES_IGNORED, true);
  1687 }
  1688 
  1689 bool SDKHooks::Hook_WeaponSwitchPost(CBaseCombatWeapon *pWeapon, int viewmodelindex)
  1690 {
  1691 	cell_t result = Call(META_IFACEPTR(CBaseEntity), SDKHook_WeaponSwitchPost, pWeapon);
  1692 	RETURN_META_VALUE(MRES_IGNORED, true);
  1693 }
  1694 
  1695 void SDKHooks::HandleEntityCreated(CBaseEntity *pEntity, int ref)
  1696 {
  1697 	const char *pName = gamehelpers->GetEntityClassname(pEntity);
  1698 
  1699 	// Send OnEntityCreated to SM listeners
  1700 	SourceHook::List<ISMEntityListener *>::iterator iter;
  1701 	ISMEntityListener *pListener = NULL;
  1702 	for (iter = m_EntListeners.begin(); iter != m_EntListeners.end(); iter++)
  1703 	{
  1704 		pListener = (*iter);
  1705 		pListener->OnEntityCreated(pEntity, pName ? pName : "");
  1706 	}
  1707 
  1708 	// Call OnEntityCreated forward
  1709 	g_pOnEntityCreated->PushCell(ref);
  1710 	g_pOnEntityCreated->PushString(pName ? pName : "");
  1711 	g_pOnEntityCreated->Execute(NULL);
  1712 
  1713 	m_EntityExists.Set(gamehelpers->ReferenceToIndex(ref));
  1714 }
  1715 
  1716 void SDKHooks::HandleEntityDeleted(CBaseEntity *pEntity, int ref)
  1717 {
  1718 	// Send OnEntityDestroyed to SM listeners
  1719 	SourceHook::List<ISMEntityListener *>::iterator iter;
  1720 	ISMEntityListener *pListener = NULL;
  1721 	for (iter = m_EntListeners.begin(); iter != m_EntListeners.end(); iter++)
  1722 	{
  1723 		pListener = (*iter);
  1724 		pListener->OnEntityDestroyed(pEntity);
  1725 	}
  1726 
  1727 	// Call OnEntityDestroyed forward
  1728 	g_pOnEntityDestroyed->PushCell(ref);
  1729 	g_pOnEntityDestroyed->Execute(NULL);
  1730 
  1731 	Unhook(pEntity);
  1732 
  1733 	m_EntityExists.Set(gamehelpers->ReferenceToIndex(ref), false);
  1734 }