Added amb1528 - Entity Output Hooking.
authorMatt Woodrow <pred@alliedmods.net>
Wed Apr 02 01:22:02 2008 +0000 (2008-04-02)
changeset 19584ad91ad970ac
parent 1957 045009f474b1
child 1959 ab390ebdcef9
Added amb1528 - Entity Output Hooking.
extensions/sdktools/Makefile
extensions/sdktools/detours.cpp
extensions/sdktools/detours.h
extensions/sdktools/extension.cpp
extensions/sdktools/msvc8/sdktools.vcproj
extensions/sdktools/output.cpp
extensions/sdktools/output.h
extensions/sdktools/outputnatives.cpp
gamedata/sdktools.games.ep2.txt
gamedata/sdktools.games.txt
plugins/include/sdktools.inc
plugins/include/sdktools_entoutput.inc
plugins/testsuite/outputtest.sp
     1.1 --- a/extensions/sdktools/Makefile	Tue Apr 01 04:22:47 2008 +0000
     1.2 +++ b/extensions/sdktools/Makefile	Wed Apr 02 01:22:02 2008 +0000
     1.3 @@ -16,7 +16,8 @@
     1.4  
     1.5  OBJECTS = sdk/smsdk_ext.cpp extension.cpp vdecoder.cpp vcallbuilder.cpp vcaller.cpp \
     1.6  	vnatives.cpp vsound.cpp tenatives.cpp trnatives.cpp tempents.cpp vstringtable.cpp \
     1.7 -	vhelpers.cpp vglobals.cpp voice.cpp inputnatives.cpp teamnatives.cpp
     1.8 +	vhelpers.cpp vglobals.cpp voice.cpp inputnatives.cpp teamnatives.cpp output.cpp \
     1.9 +	outputnatives.cpp detours.cpp
    1.10  
    1.11  ##############################################
    1.12  ### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/extensions/sdktools/detours.cpp	Wed Apr 02 01:22:02 2008 +0000
     2.3 @@ -0,0 +1,39 @@
     2.4 +/**
     2.5 + * vim: set ts=4 :
     2.6 + * =============================================================================
     2.7 + * SourceMod SDKTools Extension
     2.8 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     2.9 + * =============================================================================
    2.10 + *
    2.11 + * This program is free software; you can redistribute it and/or modify it under
    2.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    2.13 + * Free Software Foundation.
    2.14 + * 
    2.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    2.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    2.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    2.18 + * details.
    2.19 + *
    2.20 + * You should have received a copy of the GNU General Public License along with
    2.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    2.22 + *
    2.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    2.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    2.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    2.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    2.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    2.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    2.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    2.30 + * or <http://www.sourcemod.net/license.php>.
    2.31 + *
    2.32 + * Version: $Id: tenatives.cpp 1566 2007-10-14 22:12:46Z faluco $
    2.33 + */
    2.34 +
    2.35 +#include "extension.h"
    2.36 +#include "output.h"
    2.37 +
    2.38 +
    2.39 +
    2.40 +
    2.41 +
    2.42 +
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/extensions/sdktools/detours.h	Wed Apr 02 01:22:02 2008 +0000
     3.3 @@ -0,0 +1,98 @@
     3.4 +/**
     3.5 + * vim: set ts=4 :
     3.6 + * =============================================================================
     3.7 + * SourceMod SDKTools Extension
     3.8 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     3.9 + * =============================================================================
    3.10 + *
    3.11 + * This program is free software; you can redistribute it and/or modify it under
    3.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    3.13 + * Free Software Foundation.
    3.14 + * 
    3.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    3.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    3.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    3.18 + * details.
    3.19 + *
    3.20 + * You should have received a copy of the GNU General Public License along with
    3.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    3.22 + *
    3.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    3.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    3.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    3.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    3.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    3.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    3.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    3.30 + * or <http://www.sourcemod.net/license.php>.
    3.31 + *
    3.32 + * Version: $Id: extension.h 1775 2007-12-06 02:25:35Z faluco $
    3.33 + */
    3.34 +
    3.35 +#ifndef _INCLUDE_SOURCEMOD_DETOURS_H_
    3.36 +#define _INCLUDE_SOURCEMOD_DETOURS_H_
    3.37 +
    3.38 +#if defined PLATFORM_LINUX
    3.39 +#include <sys/mman.h>
    3.40 +#define	PAGE_SIZE	4096
    3.41 +#define ALIGN(ar) ((long)ar & ~(PAGE_SIZE-1))
    3.42 +#define	PAGE_EXECUTE_READWRITE	PROT_READ|PROT_WRITE|PROT_EXEC
    3.43 +#endif
    3.44 +
    3.45 +struct patch_t
    3.46 +{
    3.47 +	patch_t()
    3.48 +	{
    3.49 +		patch[0] = 0;
    3.50 +		bytes = 0;
    3.51 +	}
    3.52 +	unsigned char patch[20];
    3.53 +	size_t bytes;
    3.54 +};
    3.55 +
    3.56 +inline void ProtectMemory(void *addr, int length, int prot)
    3.57 +{
    3.58 +#if defined PLATFORM_LINUX
    3.59 +	void *addr2 = (void *)ALIGN(addr);
    3.60 +	mprotect(addr2, sysconf(_SC_PAGESIZE), prot);
    3.61 +#elif defined PLATFORM_WINDOWS
    3.62 +	DWORD old_prot;
    3.63 +	VirtualProtect(addr, length, prot, &old_prot);
    3.64 +#endif
    3.65 +}
    3.66 +
    3.67 +inline void SetMemPatchable(void *address, size_t size)
    3.68 +{
    3.69 +	ProtectMemory(address, (int)size, PAGE_EXECUTE_READWRITE);
    3.70 +}
    3.71 +
    3.72 +inline void DoGatePatch(unsigned char *target, void *callback)
    3.73 +{
    3.74 +	SetMemPatchable(target, 20);
    3.75 +
    3.76 +	target[0] = 0xFF;	/* JMP */
    3.77 +	target[1] = 0x25;	/* MEM32 */
    3.78 +	*(void **)(&target[2]) = callback;
    3.79 +}
    3.80 +
    3.81 +inline void ApplyPatch(void *address, int offset, const patch_t *patch, patch_t *restore)
    3.82 +{
    3.83 +	ProtectMemory(address, 20, PAGE_EXECUTE_READWRITE);
    3.84 +
    3.85 +	unsigned char *addr = (unsigned char *)address + offset;
    3.86 +	if (restore)
    3.87 +	{
    3.88 +		for (size_t i=0; i<patch->bytes; i++)
    3.89 +		{
    3.90 +			restore->patch[i] = addr[i];
    3.91 +		}
    3.92 +		restore->bytes = patch->bytes;
    3.93 +	}
    3.94 +
    3.95 +	for (size_t i=0; i<patch->bytes; i++)
    3.96 +	{
    3.97 +		addr[i] = patch->patch[i];
    3.98 +	}
    3.99 +}
   3.100 +
   3.101 +#endif //_INCLUDE_SOURCEMOD_DETOURS_H_
     4.1 --- a/extensions/sdktools/extension.cpp	Tue Apr 01 04:22:47 2008 +0000
     4.2 +++ b/extensions/sdktools/extension.cpp	Wed Apr 02 01:22:02 2008 +0000
     4.3 @@ -36,6 +36,7 @@
     4.4  #include "vglobals.h"
     4.5  #include "tempents.h"
     4.6  #include "vsound.h"
     4.7 +#include "output.h"
     4.8  
     4.9  #if defined ORANGEBOX_BUILD
    4.10  	#define SDKTOOLS_GAME_FILE		"sdktools.games.ep2"
    4.11 @@ -92,6 +93,7 @@
    4.12  	sharesys->AddNatives(myself, g_VoiceNatives);
    4.13  	sharesys->AddNatives(myself, g_EntInputNatives);
    4.14  	sharesys->AddNatives(myself, g_TeamNatives);
    4.15 +	sharesys->AddNatives(myself, g_EntOutputNatives);
    4.16  
    4.17  	SM_GET_IFACE(GAMEHELPERS, g_pGameHelpers);
    4.18  
    4.19 @@ -116,6 +118,14 @@
    4.20  
    4.21  	MathLib_Init(2.2f, 2.2f, 0.0f, 2);
    4.22  
    4.23 +	spengine = g_pSM->GetScriptingEngine();
    4.24 +
    4.25 +	//g_OutputManager.VariantHandle = handlesys->CreateType("Variant", &g_OutputManager, 0, NULL, NULL, myself->GetIdentity(), NULL);
    4.26 +
    4.27 +	plsys->AddPluginsListener(&g_OutputManager);
    4.28 +
    4.29 +	g_OutputManager.Init();
    4.30 +
    4.31  	return true;
    4.32  }
    4.33  
     5.1 --- a/extensions/sdktools/msvc8/sdktools.vcproj	Tue Apr 01 04:22:47 2008 +0000
     5.2 +++ b/extensions/sdktools/msvc8/sdktools.vcproj	Wed Apr 02 01:22:02 2008 +0000
     5.3 @@ -40,7 +40,7 @@
     5.4  			<Tool
     5.5  				Name="VCCLCompilerTool"
     5.6  				Optimization="0"
     5.7 -				AdditionalIncludeDirectories="..;..\sdk;..\..\..\public;..\..\..\public\extensions;..\..\..\public\sourcepawn;&quot;$(HL2SDK)\public&quot;;&quot;$(HL2SDK)\public\dlls&quot;;&quot;$(HL2SDK)\public\engine&quot;;&quot;$(HL2SDK)\public\tier0&quot;;&quot;$(HL2SDK)\public\tier1&quot;;&quot;$(SOURCEMM14)&quot;;&quot;$(SOURCEMM14)\sourcemm&quot;;&quot;$(SOURCEMM14)\sourcehook&quot;"
     5.8 +				AdditionalIncludeDirectories="..;..\sdk;..\..\..\public;..\..\..\public\extensions;..\..\..\public\sourcepawn;&quot;$(HL2SDK)\public&quot;;&quot;$(HL2SDK)\public\dlls&quot;;&quot;$(HL2SDK)\public\engine&quot;;&quot;$(HL2SDK)\public\tier0&quot;;&quot;$(HL2SDK)\public\tier1&quot;;&quot;$(SOURCEMM142)&quot;;&quot;$(SOURCEMM142)\sourcemm&quot;;&quot;$(SOURCEMM142)\sourcehook&quot;"
     5.9  				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;SDK_EXPORTS;_CRT_SECURE_NO_DEPRECATE;SOURCEMOD_BUILD"
    5.10  				MinimalRebuild="true"
    5.11  				BasicRuntimeChecks="3"
    5.12 @@ -522,6 +522,14 @@
    5.13  				>
    5.14  			</File>
    5.15  			<File
    5.16 +				RelativePath="..\output.cpp"
    5.17 +				>
    5.18 +			</File>
    5.19 +			<File
    5.20 +				RelativePath="..\outputnatives.cpp"
    5.21 +				>
    5.22 +			</File>
    5.23 +			<File
    5.24  				RelativePath="..\teamnatives.cpp"
    5.25  				>
    5.26  			</File>
    5.27 @@ -584,10 +592,18 @@
    5.28  				>
    5.29  			</File>
    5.30  			<File
    5.31 +				RelativePath="..\detours.h"
    5.32 +				>
    5.33 +			</File>
    5.34 +			<File
    5.35  				RelativePath="..\extension.h"
    5.36  				>
    5.37  			</File>
    5.38  			<File
    5.39 +				RelativePath="..\output.h"
    5.40 +				>
    5.41 +			</File>
    5.42 +			<File
    5.43  				RelativePath="..\tempents.h"
    5.44  				>
    5.45  			</File>
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/extensions/sdktools/output.cpp	Wed Apr 02 01:22:02 2008 +0000
     6.3 @@ -0,0 +1,559 @@
     6.4 +/**
     6.5 + * vim: set ts=4 :
     6.6 + * =============================================================================
     6.7 + * SourceMod SDKTools Extension
     6.8 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6.9 + * =============================================================================
    6.10 + *
    6.11 + * This program is free software; you can redistribute it and/or modify it under
    6.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    6.13 + * Free Software Foundation.
    6.14 + * 
    6.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    6.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    6.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    6.18 + * details.
    6.19 + *
    6.20 + * You should have received a copy of the GNU General Public License along with
    6.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    6.22 + *
    6.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    6.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    6.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    6.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    6.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    6.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    6.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    6.30 + * or <http://www.sourcemod.net/license.php>.
    6.31 + *
    6.32 + * Version: $Id: tenatives.cpp 1566 2007-10-14 22:12:46Z faluco $
    6.33 + */
    6.34 +
    6.35 +#include "extension.h"
    6.36 +#include "output.h"
    6.37 +
    6.38 +ISourcePawnEngine *spengine = NULL;
    6.39 +EntityOutputManager g_OutputManager;
    6.40 +
    6.41 +bool EntityOutputManager::CreateFireEventDetour()
    6.42 +{
    6.43 +	g_pGameConf->GetMemSig("FireOutput", &info_address);
    6.44 +	g_pGameConf->GetOffset("FireOutputBackup", (int *)&(info_restore.bytes));
    6.45 +
    6.46 +	if (!info_address)
    6.47 +	{
    6.48 +		g_pSM->LogError(myself, "Could not locate FireOutput - Disabling Entity Outputs");
    6.49 +		return false;
    6.50 +	}
    6.51 +
    6.52 +	if (!info_restore.bytes)
    6.53 +	{
    6.54 +		g_pSM->LogError(myself, "Could not locate FireOutputBackup - Disabling Entity Outputs");
    6.55 +		return false;
    6.56 +	}
    6.57 +
    6.58 +	/* First, save restore bits */
    6.59 +	for (size_t i=0; i<info_restore.bytes; i++)
    6.60 +	{
    6.61 +		info_restore.patch[i] = ((unsigned char *)info_address)[i];
    6.62 +		g_pSM->LogMessage(myself, "Backing up: %x", info_restore.patch[i]);
    6.63 +	}
    6.64 +
    6.65 +	info_callback = spengine->ExecAlloc(100);
    6.66 +	JitWriter wr;
    6.67 +	JitWriter *jit = &wr;
    6.68 +	wr.outbase = (jitcode_t)info_callback;
    6.69 +	wr.outptr = wr.outbase;
    6.70 +
    6.71 +	g_pSM->LogMessage(myself, "info_callback : %x", info_callback);
    6.72 +	g_pSM->LogMessage(myself, "info_address : %x", info_address);
    6.73 +
    6.74 +	/* Function we are detouring into is
    6.75 +	 *
    6.76 +	 * void FireEventDetour(CBaseEntityOutput(void *) *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay = 0 )
    6.77 +	 */
    6.78 +
    6.79 +	/* push		fDelay [esp+20h]
    6.80 +	 * push		pCaller [esp+1Ch]
    6.81 +	 * push		pActivator [esp+18h]
    6.82 +	 * push		pOutput [ecx]
    6.83 +	 */
    6.84 +
    6.85 +
    6.86 +#if defined PLATFORM_WINDOWS
    6.87 +
    6.88 +	IA32_Push_Rm_Disp8_ESP(jit, 32);
    6.89 +	IA32_Push_Rm_Disp8_ESP(jit, 32);
    6.90 +	IA32_Push_Rm_Disp8_ESP(jit, 32);
    6.91 +
    6.92 +	/* variant_t doesnt do anything so i'll disable this bit */
    6.93 +	//IA32_Push_Rm_Disp8_ESP(jit, 32);
    6.94 +	//IA32_Push_Rm_Disp8_ESP(jit, 32);
    6.95 +	//IA32_Push_Rm_Disp8_ESP(jit, 32);
    6.96 +	//IA32_Push_Rm_Disp8_ESP(jit, 32); 
    6.97 +	//IA32_Push_Rm_Disp8_ESP(jit, 32);
    6.98 +
    6.99 +	IA32_Push_Reg(jit, REG_ECX);
   6.100 +
   6.101 +#elif defined PLATFORM_LINUX
   6.102 +	IA32_Push_Rm_Disp8_ESP(jit, 20);
   6.103 +	IA32_Push_Rm_Disp8_ESP(jit, 20);
   6.104 +	IA32_Push_Rm_Disp8_ESP(jit, 20);
   6.105 +	// We miss the variant_t pointer
   6.106 +	IA32_Push_Rm_Disp8_ESP(jit, 16);
   6.107 +#endif
   6.108 +
   6.109 +	jitoffs_t call = IA32_Call_Imm32(jit, 0); 
   6.110 +	IA32_Write_Jump32_Abs(jit, call, (void *)TempDetour);
   6.111 +
   6.112 +	
   6.113 +#if defined PLATFORM_LINUX
   6.114 +	IA32_Add_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);		//add esp, 4
   6.115 +#elif defined PLATFORM_WINDOWS
   6.116 +	IA32_Pop_Reg(jit, REG_ECX);
   6.117 +#endif
   6.118 +
   6.119 +	IA32_Add_Rm_Imm8(jit, REG_ESP, 12, MOD_REG);	//add esp, 12 (0Ch)
   6.120 +
   6.121 +
   6.122 +	/* Patch old bytes in */
   6.123 +	for (size_t i=0; i<info_restore.bytes; i++)
   6.124 +	{
   6.125 +		jit->write_ubyte(info_restore.patch[i]);
   6.126 +		g_pSM->LogMessage(myself, "Writing: %x", info_restore.patch[i]);
   6.127 +	}
   6.128 +
   6.129 +	/* Return to the original function */
   6.130 +	call = IA32_Jump_Imm32(jit, 0);
   6.131 +	IA32_Write_Jump32_Abs(jit, call, (unsigned char *)info_address + info_restore.bytes);
   6.132 +
   6.133 +	return true;
   6.134 +}
   6.135 +
   6.136 +void EntityOutputManager::InitFireEventDetour()
   6.137 +{
   6.138 +	if (!is_detoured)
   6.139 +	{
   6.140 +		DoGatePatch((unsigned char *)info_address, &info_callback);
   6.141 +		is_detoured = true;
   6.142 +	}
   6.143 +}
   6.144 +
   6.145 +void EntityOutputManager::DeleteFireEventDetour()
   6.146 +{
   6.147 +	if (is_detoured)
   6.148 +	{
   6.149 +		ShutdownFireEventDetour();
   6.150 +	}
   6.151 +
   6.152 +	if (info_callback)
   6.153 +	{
   6.154 +		/* Free the gate */
   6.155 +		spengine->ExecFree(info_callback);
   6.156 +		info_callback = NULL;
   6.157 +	}
   6.158 +}
   6.159 +
   6.160 +void TempDetour(void *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay)
   6.161 +{
   6.162 +	g_OutputManager.FireEventDetour(pOutput, pActivator, pCaller, fDelay);
   6.163 +}
   6.164 +
   6.165 +void EntityOutputManager::ShutdownFireEventDetour()
   6.166 +{
   6.167 +	if (info_callback)
   6.168 +	{
   6.169 +		/* Remove the patch */
   6.170 +		ApplyPatch(info_address, 0, &info_restore, NULL);
   6.171 +		is_detoured = false;
   6.172 +	}
   6.173 +}
   6.174 +
   6.175 +void EntityOutputManager::FireEventDetour(void *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay)
   6.176 +{
   6.177 +	char sOutput[20];
   6.178 +	Q_snprintf(sOutput, sizeof(sOutput), "%x", pOutput);
   6.179 +
   6.180 +	// attempt to directly lookup a hook using the pOutput pointer
   6.181 +	OutputNameStruct *pOutputName = NULL;
   6.182 +
   6.183 +	edict_t *pEdict = gameents->BaseEntityToEdict(pCaller);
   6.184 +
   6.185 +	/* DEBUG START */
   6.186 +
   6.187 +	const char *classname = pEdict->GetClassName();
   6.188 +	const char *outputname = FindOutputName(pOutput, pCaller);
   6.189 +
   6.190 +	g_pSM->LogMessage(myself, "Output \"%s\" fired from entity \"%s\"", outputname, classname);
   6.191 +
   6.192 +	/* DEBUG END */
   6.193 +
   6.194 +
   6.195 +	bool fastLookup = false;
   6.196 +	
   6.197 +	// Fast lookup failed - check the slow way for hooks that havn't fired yet
   6.198 +	if ((fastLookup = EntityOutputs->Retrieve(sOutput, (void **)&pOutputName)) == false)
   6.199 +	{
   6.200 +		const char *classname = pEdict->GetClassName();
   6.201 +		const char *outputname = FindOutputName(pOutput, pCaller);
   6.202 +
   6.203 +		pOutputName = FindOutputPointer(classname, outputname, false);
   6.204 +
   6.205 +		if (!pOutputName)
   6.206 +		{
   6.207 +			return;
   6.208 +		}
   6.209 +	}
   6.210 +
   6.211 +	if (!pOutputName->hooks.empty())
   6.212 +	{
   6.213 +		if (!fastLookup)
   6.214 +		{
   6.215 +			// hook exists on this classname and output - map it into our quick find trie
   6.216 +			EntityOutputs->Insert(sOutput, pOutputName);
   6.217 +		}
   6.218 +
   6.219 +		SourceHook::List<omg_hooks *>::iterator _iter;
   6.220 +
   6.221 +		omg_hooks *hook;
   6.222 +
   6.223 +		_iter = pOutputName->hooks.begin();
   6.224 +
   6.225 +		while (_iter != pOutputName->hooks.end())
   6.226 +		{
   6.227 +			hook = (omg_hooks *)*_iter;
   6.228 +
   6.229 +			hook->in_use = true;
   6.230 +
   6.231 +			int serial = pEdict->m_NetworkSerialNumber;
   6.232 +			
   6.233 +			if (serial != hook->entity_filter && hook->entity_index == engine->IndexOfEdict(pEdict))
   6.234 +			{
   6.235 +				// same entity index but different serial number. Entity has changed, kill the hook.
   6.236 +				_iter = pOutputName->hooks.erase(_iter);
   6.237 +				CleanUpHook(hook);
   6.238 +
   6.239 +				continue;
   6.240 +			}
   6.241 +
   6.242 +			if (hook->entity_filter == -1 || hook->entity_filter == serial) // Global classname hook
   6.243 +			{
   6.244 +				/*
   6.245 +				Handle_t handle;
   6.246 +
   6.247 +				if (Value.FieldType() == FIELD_VOID)
   6.248 +				{
   6.249 +					handle = 0;
   6.250 +				}
   6.251 +				else
   6.252 +				{
   6.253 +					varhandle_t *varhandle = new varhandle_t;
   6.254 +					memmove ((void *)&varhandle->crab, (void *)&Value, sizeof(variant_t));
   6.255 +
   6.256 +					handle = handlesys->CreateHandle(VariantHandle, (void *)varhandle, hook->pf->GetParentContext()->GetIdentity(), myself->GetIdentity(), NULL);
   6.257 +				}
   6.258 +				*/
   6.259 +
   6.260 +				//fire the forward to hook->pf
   6.261 +				hook->pf->PushString(pOutputName->Name);
   6.262 +				hook->pf->PushCell(engine->IndexOfEdict(pEdict));
   6.263 +				hook->pf->PushCell(engine->IndexOfEdict(gameents->BaseEntityToEdict(pActivator)));
   6.264 +				//hook->pf->PushCell(handle);
   6.265 +				hook->pf->PushFloat(fDelay);
   6.266 +				hook->pf->Execute(NULL);
   6.267 +
   6.268 +				if ((hook->entity_filter != -1) && hook->only_once)
   6.269 +				{
   6.270 +					_iter = pOutputName->hooks.erase(_iter);
   6.271 +					CleanUpHook(hook);
   6.272 +
   6.273 +					continue;
   6.274 +				}
   6.275 +
   6.276 +				if (hook->delete_me)
   6.277 +				{
   6.278 +					_iter = pOutputName->hooks.erase(_iter);
   6.279 +					CleanUpHook(hook);
   6.280 +					continue;
   6.281 +				}
   6.282 +
   6.283 +				hook->in_use = false;
   6.284 +				_iter++;
   6.285 +
   6.286 +				continue;
   6.287 +			}
   6.288 +		}
   6.289 +	}
   6.290 +}
   6.291 +
   6.292 +omg_hooks *EntityOutputManager::NewHook()
   6.293 +{
   6.294 +	omg_hooks *hook;
   6.295 +
   6.296 +	if (FreeHooks.empty())
   6.297 +	{
   6.298 +		hook = new omg_hooks;
   6.299 +	}
   6.300 +	else
   6.301 +	{
   6.302 +		hook = g_OutputManager.FreeHooks.front();
   6.303 +		g_OutputManager.FreeHooks.pop();
   6.304 +	}
   6.305 +
   6.306 +	return hook;
   6.307 +}
   6.308 +
   6.309 +void EntityOutputManager::OnHookAdded()
   6.310 +{
   6.311 +	HookCount++;
   6.312 +
   6.313 +	if (HookCount == 1)
   6.314 +	{
   6.315 +		// This is the first hook created
   6.316 +		InitFireEventDetour();
   6.317 +	}
   6.318 +}
   6.319 +
   6.320 +void EntityOutputManager::OnHookRemoved()
   6.321 +{
   6.322 +	HookCount--;
   6.323 +
   6.324 +	if (HookCount == 0)
   6.325 +	{
   6.326 +		//we need to check if we are inside a detour. easy.
   6.327 +		//if we are how do we 
   6.328 +		ShutdownFireEventDetour();
   6.329 +	}
   6.330 +}
   6.331 +
   6.332 +void EntityOutputManager::CleanUpHook(omg_hooks *hook)
   6.333 +{
   6.334 +	FreeHooks.push(hook);
   6.335 +
   6.336 +	OnHookRemoved();
   6.337 +
   6.338 +	IPlugin *pPlugin = plsys->FindPluginByContext(hook->pf->GetParentContext()->GetContext());
   6.339 +	SourceHook::List<omg_hooks *> *pList = NULL;
   6.340 +
   6.341 +	if (!pPlugin->GetProperty("OutputHookList", (void **)&pList, false) || !pList)
   6.342 +	{
   6.343 +		return;
   6.344 +	}
   6.345 +
   6.346 +	SourceHook::List<omg_hooks *>::iterator p_iter = pList->begin();
   6.347 +
   6.348 +	omg_hooks *pluginHook;
   6.349 +
   6.350 +	while (p_iter != pList->end())
   6.351 +	{
   6.352 +		pluginHook = (omg_hooks *)*p_iter;
   6.353 +		if (pluginHook == hook)
   6.354 +		{
   6.355 +			p_iter = pList->erase(p_iter);
   6.356 +		}
   6.357 +		else
   6.358 +		{
   6.359 +			p_iter++;
   6.360 +		}
   6.361 +	}
   6.362 +}
   6.363 +
   6.364 +void EntityOutputManager::OnPluginDestroyed(IPlugin *plugin)
   6.365 +{
   6.366 +	SourceHook::List<omg_hooks *> *pList = NULL;
   6.367 +
   6.368 +	if (plugin->GetProperty("OutputHookList", (void **)&pList, true))
   6.369 +	{
   6.370 +		SourceHook::List<omg_hooks *>::iterator p_iter = pList->begin();
   6.371 +		omg_hooks *hook;
   6.372 +
   6.373 +		while (p_iter != pList->end())
   6.374 +		{
   6.375 +			hook = (omg_hooks *)*p_iter;
   6.376 +		
   6.377 +			p_iter = pList->erase(p_iter); //remove from this plugins list
   6.378 +			hook->m_parent->hooks.remove(hook); // remove from the y's list
   6.379 +
   6.380 +			FreeHooks.push(hook); //save the omg_hook
   6.381 +
   6.382 +			OnHookRemoved();
   6.383 +		}		
   6.384 +	}
   6.385 +}
   6.386 +
   6.387 +OutputNameStruct *EntityOutputManager::FindOutputPointer(const char *classname, const char *outputname, bool create)
   6.388 +{
   6.389 +	ClassNameStruct *pClassname;
   6.390 +	
   6.391 +	if (!ClassNames->Retrieve(classname, (void **)&pClassname))
   6.392 +	{
   6.393 +		if (create)
   6.394 +		{
   6.395 +			pClassname = new ClassNameStruct;
   6.396 +			ClassNames->Insert(classname, pClassname);
   6.397 +		}
   6.398 +		else
   6.399 +		{
   6.400 +			return NULL;
   6.401 +		}
   6.402 +	}
   6.403 +
   6.404 +	OutputNameStruct *pOutputName;
   6.405 +
   6.406 +	if (!pClassname->OutputList->Retrieve(outputname, (void **)&pOutputName))
   6.407 +	{
   6.408 +		if (create)
   6.409 +		{
   6.410 +			pOutputName = new OutputNameStruct;
   6.411 +			pClassname->OutputList->Insert(outputname, pOutputName);
   6.412 +			strncpy(pOutputName->Name, outputname, sizeof(pOutputName->Name));
   6.413 +			pOutputName->Name[49] = 0;
   6.414 +		}
   6.415 +		else
   6.416 +		{
   6.417 +			return NULL;
   6.418 +		}
   6.419 +	}
   6.420 +
   6.421 +	return pOutputName;
   6.422 +}
   6.423 +
   6.424 +// Iterate the datamap of pCaller and look for output pointers with the same address as pOutput
   6.425 +const char *EntityOutputManager::FindOutputName(void *pOutput, CBaseEntity *pCaller)
   6.426 +{
   6.427 +	datamap_t *pMap = gamehelpers->GetDataMap(pCaller);
   6.428 +
   6.429 +	while (pMap)
   6.430 +	{
   6.431 +		for (int i=0; i<pMap->dataNumFields; i++)
   6.432 +		{
   6.433 +			if (pMap->dataDesc[i].flags & FTYPEDESC_OUTPUT)
   6.434 +			{
   6.435 +				if ((char *)pCaller + pMap->dataDesc[i].fieldOffset[0] == pOutput)
   6.436 +				{
   6.437 +					return pMap->dataDesc[i].externalName;
   6.438 +				}
   6.439 +			}
   6.440 +		}
   6.441 +		pMap = pMap->baseMap;
   6.442 +	}
   6.443 +
   6.444 +	return NULL;
   6.445 +}
   6.446 +#if 0
   6.447 +// Almost identical copy of this function from cbase.cpp - FIELD_EHANDLE changed to remove dependencies
   6.448 +const char *variant_t::ToString( void ) const
   6.449 +{
   6.450 +	COMPILE_TIME_ASSERT( sizeof(string_t) == sizeof(int) );
   6.451 +
   6.452 +	static char szBuf[512];
   6.453 +
   6.454 +	switch (fieldType)
   6.455 +	{
   6.456 +	case FIELD_STRING:
   6.457 +		{
   6.458 +			return(STRING(iszVal));
   6.459 +		}
   6.460 +
   6.461 +	case FIELD_BOOLEAN:
   6.462 +		{
   6.463 +			if (bVal == 0)
   6.464 +			{
   6.465 +				Q_strncpy(szBuf, "false",sizeof(szBuf));
   6.466 +			}
   6.467 +			else
   6.468 +			{
   6.469 +				Q_strncpy(szBuf, "true",sizeof(szBuf));
   6.470 +			}
   6.471 +			return(szBuf);
   6.472 +		}
   6.473 +
   6.474 +	case FIELD_INTEGER:
   6.475 +		{
   6.476 +			Q_snprintf( szBuf, sizeof( szBuf ), "%i", iVal );
   6.477 +			return(szBuf);
   6.478 +		}
   6.479 +
   6.480 +	case FIELD_FLOAT:
   6.481 +		{
   6.482 +			Q_snprintf(szBuf,sizeof(szBuf), "%g", flVal);
   6.483 +			return(szBuf);
   6.484 +		}
   6.485 +
   6.486 +	case FIELD_COLOR32:
   6.487 +		{
   6.488 +			Q_snprintf(szBuf,sizeof(szBuf), "%d %d %d %d", (int)rgbaVal.r, (int)rgbaVal.g, (int)rgbaVal.b, (int)rgbaVal.a);
   6.489 +			return(szBuf);
   6.490 +		}
   6.491 +
   6.492 +	case FIELD_VECTOR:
   6.493 +		{
   6.494 +			Q_snprintf(szBuf,sizeof(szBuf), "[%g %g %g]", (double)vecVal[0], (double)vecVal[1], (double)vecVal[2]);
   6.495 +			return(szBuf);
   6.496 +		}
   6.497 +
   6.498 +	case FIELD_VOID:
   6.499 +		{
   6.500 +			szBuf[0] = '\0';
   6.501 +			return(szBuf);
   6.502 +		}
   6.503 +
   6.504 +	case FIELD_EHANDLE:
   6.505 +		{
   6.506 +			CBaseHandle temp = eVal;
   6.507 +
   6.508 +			const char *pszName = g_OutputManager.BaseHandleToEdict(temp)->GetClassName();
   6.509 +
   6.510 +			if (pszName == NULL)
   6.511 +			{
   6.512 +				Q_strncpy( szBuf, "<<null entity>>", 512 );
   6.513 +				return (szBuf);
   6.514 +			}
   6.515 +
   6.516 +			Q_strncpy( szBuf, pszName, 512 );
   6.517 +			return (szBuf);
   6.518 +		}
   6.519 +
   6.520 +	default:
   6.521 +		break;
   6.522 +	}
   6.523 +
   6.524 +	return("No conversion to string");
   6.525 +}
   6.526 +#endif
   6.527 +// Thanks SM core
   6.528 +edict_t *EntityOutputManager::BaseHandleToEdict(CBaseHandle &hndl)
   6.529 +{
   6.530 +	if (!hndl.IsValid())
   6.531 +	{
   6.532 +		return NULL;
   6.533 +	}
   6.534 +
   6.535 +	int index = hndl.GetEntryIndex();
   6.536 +
   6.537 +	edict_t *pStoredEdict;
   6.538 +
   6.539 +	pStoredEdict = engine->PEntityOfEntIndex(index);
   6.540 +
   6.541 +	if (pStoredEdict == NULL)
   6.542 +	{
   6.543 +		return NULL;
   6.544 +	}
   6.545 +
   6.546 +	IServerEntity *pSE = pStoredEdict->GetIServerEntity();
   6.547 +
   6.548 +	if (pSE == NULL)
   6.549 +	{
   6.550 +		return NULL;
   6.551 +	}
   6.552 +
   6.553 +	if (pSE->GetRefEHandle() != hndl)
   6.554 +	{
   6.555 +		return NULL;
   6.556 +	}
   6.557 +
   6.558 +	return pStoredEdict;
   6.559 +}
   6.560 +
   6.561 +
   6.562 +
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/extensions/sdktools/output.h	Wed Apr 02 01:22:02 2008 +0000
     7.3 @@ -0,0 +1,176 @@
     7.4 +/**
     7.5 + * vim: set ts=4 :
     7.6 + * =============================================================================
     7.7 + * SourceMod SDKTools Extension
     7.8 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     7.9 + * =============================================================================
    7.10 + *
    7.11 + * This program is free software; you can redistribute it and/or modify it under
    7.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    7.13 + * Free Software Foundation.
    7.14 + * 
    7.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    7.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    7.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    7.18 + * details.
    7.19 + *
    7.20 + * You should have received a copy of the GNU General Public License along with
    7.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    7.22 + *
    7.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    7.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    7.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    7.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    7.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    7.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    7.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    7.30 + * or <http://www.sourcemod.net/license.php>.
    7.31 + *
    7.32 + * Version: $Id: output.h 1775 2007-12-06 02:25:35Z faluco $
    7.33 + */
    7.34 +
    7.35 +#ifndef _INCLUDE_SOURCEMOD_OUTPUT_H_
    7.36 +#define _INCLUDE_SOURCEMOD_OUTPUT_H_
    7.37 +
    7.38 +#include <jit/jit_helpers.h>
    7.39 +#include <jit/x86/x86_macros.h>
    7.40 +//#include "variant_t.h"
    7.41 +#include "sh_list.h"
    7.42 +#include "sh_stack.h"
    7.43 +#include "sm_trie_tpl.h"
    7.44 +#include "detours.h"
    7.45 +
    7.46 +extern ISourcePawnEngine *spengine;
    7.47 +
    7.48 +struct OutputNameStruct;
    7.49 +
    7.50 +/**
    7.51 + * This is a function specific hook that corresponds to an entity classname
    7.52 + * and outputname. There can be many of these for each classname/output combo
    7.53 + */
    7.54 +struct omg_hooks 
    7.55 +{
    7.56 +	int entity_filter; // if not -1 is an entity signature
    7.57 +	int entity_index;
    7.58 +	bool only_once;
    7.59 +	IPluginFunction *pf;
    7.60 +	OutputNameStruct *m_parent;
    7.61 +	bool in_use;
    7.62 +	bool delete_me;
    7.63 +};
    7.64 +
    7.65 +/**
    7.66 + * This represents an output belonging to a specific classname
    7.67 + */
    7.68 +struct OutputNameStruct
    7.69 +{
    7.70 +	SourceHook::List<omg_hooks *> hooks;
    7.71 +	char Name[50];
    7.72 +};
    7.73 +
    7.74 +/** 
    7.75 + * This represents an entity classname
    7.76 + */
    7.77 +struct ClassNameStruct
    7.78 +{
    7.79 +	//Trie mapping outputname to a OutputNameStruct
    7.80 +	//KTrie<OutputNameStruct *> OutputList;
    7.81 +	IBasicTrie *OutputList;
    7.82 +
    7.83 +	ClassNameStruct()
    7.84 +	{
    7.85 +		OutputList = adtfactory->CreateBasicTrie();
    7.86 +	}
    7.87 +
    7.88 +	~ClassNameStruct()
    7.89 +	{
    7.90 +		OutputList->Destroy();
    7.91 +	}
    7.92 +};
    7.93 +
    7.94 +class EntityOutputManager : public IPluginsListener
    7.95 +{
    7.96 +public:
    7.97 +	EntityOutputManager()
    7.98 +	{
    7.99 +		info_address = NULL;
   7.100 +		info_callback = NULL;
   7.101 +		HookCount = 0;
   7.102 +		is_detoured = false;
   7.103 +		enabled = false;
   7.104 +	}
   7.105 +
   7.106 +	~EntityOutputManager()
   7.107 +	{
   7.108 +		EntityOutputs->Destroy();
   7.109 +		ClassNames->Destroy();
   7.110 +		ShutdownFireEventDetour();
   7.111 +	}
   7.112 +
   7.113 +	void Init()
   7.114 +	{
   7.115 +		enabled = CreateFireEventDetour();
   7.116 +
   7.117 +		if (!enabled)
   7.118 +		{
   7.119 +			return;
   7.120 +		}
   7.121 +
   7.122 +		EntityOutputs = adtfactory->CreateBasicTrie();
   7.123 +		ClassNames = adtfactory->CreateBasicTrie();
   7.124 +	}
   7.125 +
   7.126 +	bool IsEnabled()
   7.127 +	{
   7.128 +		return enabled;
   7.129 +	}
   7.130 +
   7.131 +	void FireEventDetour(void *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay);
   7.132 +
   7.133 +	void OnPluginDestroyed(IPlugin *plugin);
   7.134 +
   7.135 +	OutputNameStruct *FindOutputPointer(const char *classname, const char *outputname, bool create);
   7.136 +
   7.137 +	void CleanUpHook(omg_hooks *hook);
   7.138 +
   7.139 +	omg_hooks *NewHook();
   7.140 +
   7.141 +	void OnHookAdded();
   7.142 +	void OnHookRemoved();
   7.143 +
   7.144 +private:
   7.145 +	bool enabled;
   7.146 +
   7.147 +	// Patch/unpatch the server dll
   7.148 +	void InitFireEventDetour();
   7.149 +	void ShutdownFireEventDetour();
   7.150 +	bool is_detoured;
   7.151 +
   7.152 +	//These create/delete the allocated memory and write into it
   7.153 +	bool CreateFireEventDetour();
   7.154 +	void DeleteFireEventDetour();
   7.155 +
   7.156 +	const char *FindOutputName(void *pOutput, CBaseEntity *pCaller);
   7.157 +	edict_t *BaseHandleToEdict(CBaseHandle &hndl);
   7.158 +
   7.159 +	//Maps CEntityOutput * to a OutputNameStruct
   7.160 +	IBasicTrie *EntityOutputs;
   7.161 +	// Maps classname to a ClassNameStruct
   7.162 +	IBasicTrie *ClassNames;
   7.163 +
   7.164 +	SourceHook::CStack<omg_hooks *> FreeHooks; //Stores hook pointers to avoid calls to new
   7.165 +
   7.166 +	int HookCount;
   7.167 +
   7.168 +	patch_t info_restore;
   7.169 +	void *info_address;
   7.170 +	void *info_callback;
   7.171 +};
   7.172 +
   7.173 +void TempDetour(void *pOutput, CBaseEntity *pActivator, CBaseEntity *pCaller, float fDelay);
   7.174 +
   7.175 +extern EntityOutputManager g_OutputManager;
   7.176 +
   7.177 +extern sp_nativeinfo_t g_EntOutputNatives[];
   7.178 +
   7.179 +#endif //_INCLUDE_SOURCEMOD_OUTPUT_H_
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/extensions/sdktools/outputnatives.cpp	Wed Apr 02 01:22:02 2008 +0000
     8.3 @@ -0,0 +1,611 @@
     8.4 +/**
     8.5 + * vim: set ts=4 :
     8.6 + * =============================================================================
     8.7 + * SourceMod SDKTools Extension
     8.8 + * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     8.9 + * =============================================================================
    8.10 + *
    8.11 + * This program is free software; you can redistribute it and/or modify it under
    8.12 + * the terms of the GNU General Public License, version 3.0, as published by the
    8.13 + * Free Software Foundation.
    8.14 + * 
    8.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
    8.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    8.17 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    8.18 + * details.
    8.19 + *
    8.20 + * You should have received a copy of the GNU General Public License along with
    8.21 + * this program.  If not, see <http://www.gnu.org/licenses/>.
    8.22 + *
    8.23 + * As a special exception, AlliedModders LLC gives you permission to link the
    8.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
    8.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    8.26 + * by the Valve Corporation.  You must obey the GNU General Public License in
    8.27 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
    8.28 + * this exception to all derivative works.  AlliedModders LLC defines further
    8.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    8.30 + * or <http://www.sourcemod.net/license.php>.
    8.31 + *
    8.32 + * Version: $Id: outputnatives.cpp 1521 2007-10-01 21:20:30Z faluco $
    8.33 + */
    8.34 +
    8.35 +#include "extension.h"
    8.36 +#include "output.h"
    8.37 +
    8.38 +
    8.39 +// HookSingleEntityOutput(ent, const String:output[], function, bool:once);
    8.40 +cell_t HookSingleEntityOutput(IPluginContext *pContext, const cell_t *params)
    8.41 +{
    8.42 +	if (!g_OutputManager.IsEnabled())
    8.43 +	{
    8.44 +		return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
    8.45 +	}
    8.46 +
    8.47 +	edict_t *pEdict = engine->PEntityOfEntIndex(params[1]);
    8.48 +	if (!pEdict)
    8.49 +	{
    8.50 +		return pContext->ThrowNativeError("Invalid Entity index %i", params[1]);
    8.51 +	}
    8.52 +	const char *classname = pEdict->GetClassName();
    8.53 +
    8.54 +	char *outputname;
    8.55 +	pContext->LocalToString(params[2], &outputname);
    8.56 +
    8.57 +	OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, true);
    8.58 +
    8.59 +	//Check for an existing identical hook
    8.60 +	SourceHook::List<omg_hooks *>::iterator _iter;
    8.61 +
    8.62 +	omg_hooks *hook;
    8.63 +
    8.64 +	IPluginFunction *pFunction;
    8.65 +	pFunction = pContext->GetFunctionById(params[3]);
    8.66 +
    8.67 +	for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
    8.68 +	{
    8.69 +		hook = (omg_hooks *)*_iter;
    8.70 +		if (hook->pf == pFunction && hook->entity_filter == pEdict->m_NetworkSerialNumber)
    8.71 +		{
    8.72 +			return 0;
    8.73 +		}
    8.74 +	}
    8.75 +
    8.76 +	hook = g_OutputManager.NewHook();
    8.77 +
    8.78 +	hook->entity_filter = pEdict->m_NetworkSerialNumber;
    8.79 +	hook->entity_index = engine->IndexOfEdict(pEdict);
    8.80 +	hook->only_once= !!params[4];
    8.81 +	hook->pf = pFunction;
    8.82 +	hook->m_parent = pOutputName;
    8.83 +	hook->in_use = false;
    8.84 +	hook->delete_me = false;
    8.85 +
    8.86 +	pOutputName->hooks.push_back(hook);
    8.87 +
    8.88 +	g_OutputManager.OnHookAdded();
    8.89 +
    8.90 +	IPlugin *pPlugin = plsys->FindPluginByContext(pContext->GetContext());
    8.91 +	SourceHook::List<omg_hooks *> *pList = NULL;
    8.92 +
    8.93 +	if (!pPlugin->GetProperty("OutputHookList", (void **)&pList, false) || !pList)
    8.94 +	{
    8.95 +		pList = new SourceHook::List<omg_hooks *>;
    8.96 +		pPlugin->SetProperty("OutputHookList", pList);
    8.97 +	}
    8.98 +
    8.99 +	pList->push_back(hook);
   8.100 +
   8.101 +	return 1;
   8.102 +}
   8.103 +
   8.104 +
   8.105 +// HookEntityOutput(const String:classname[], const String:output[], function);
   8.106 +cell_t HookEntityOutput(IPluginContext *pContext, const cell_t *params)
   8.107 +{
   8.108 +	if (!g_OutputManager.IsEnabled())
   8.109 +	{
   8.110 +		return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
   8.111 +	}
   8.112 +
   8.113 +	//Find or create the base structures for this classname and the output
   8.114 +	char *classname;
   8.115 +	pContext->LocalToString(params[1], &classname);
   8.116 +
   8.117 +	char *outputname;
   8.118 +	pContext->LocalToString(params[2], &outputname);
   8.119 +
   8.120 +	OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, true);
   8.121 +
   8.122 +	//Check for an existing identical hook
   8.123 +	SourceHook::List<omg_hooks *>::iterator _iter;
   8.124 +
   8.125 +	omg_hooks *hook;
   8.126 +
   8.127 +	IPluginFunction *pFunction;
   8.128 +	pFunction = pContext->GetFunctionById(params[3]);
   8.129 +
   8.130 +	for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
   8.131 +	{
   8.132 +		hook = (omg_hooks *)*_iter;
   8.133 +		if (hook->pf == pFunction && hook->entity_filter == -1)
   8.134 +		{
   8.135 +			//already hooked to this function...
   8.136 +			//throw an error or just let them get away with stupidity?
   8.137 +			// seems like poor coding if they dont know if something is hooked or not
   8.138 +			return 0;
   8.139 +		}
   8.140 +	}
   8.141 +
   8.142 +	hook = g_OutputManager.NewHook();
   8.143 +
   8.144 +	hook->entity_filter = -1;
   8.145 +	hook->pf = pFunction;
   8.146 +	hook->m_parent = pOutputName;
   8.147 +	hook->in_use = false;
   8.148 +	hook->delete_me = false;
   8.149 +
   8.150 +	pOutputName->hooks.push_back(hook);
   8.151 +
   8.152 +	g_OutputManager.OnHookAdded();
   8.153 +
   8.154 +	IPlugin *pPlugin = plsys->FindPluginByContext(pContext->GetContext());
   8.155 +	SourceHook::List<omg_hooks *> *pList = NULL;
   8.156 +
   8.157 +	if (!pPlugin->GetProperty("OutputHookList", (void **)&pList, false) || !pList)
   8.158 +	{
   8.159 +		pList = new SourceHook::List<omg_hooks *>;
   8.160 +		pPlugin->SetProperty("OutputHookList", pList);
   8.161 +	}
   8.162 +
   8.163 +	pList->push_back(hook);
   8.164 +
   8.165 +	return 1;
   8.166 +}
   8.167 +
   8.168 +// UnHookEntityOutput(const String:classname[], const String:output[], EntityOutput:callback);
   8.169 +cell_t UnHookEntityOutput(IPluginContext *pContext, const cell_t *params)
   8.170 +{
   8.171 +	if (!g_OutputManager.IsEnabled())
   8.172 +	{
   8.173 +		return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
   8.174 +	}
   8.175 +
   8.176 +	char *classname;
   8.177 +	pContext->LocalToString(params[1], &classname);
   8.178 +
   8.179 +	char *outputname;
   8.180 +	pContext->LocalToString(params[2], &outputname);
   8.181 +
   8.182 +	OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, false);
   8.183 +
   8.184 +	if (!pOutputName)
   8.185 +	{
   8.186 +		return 0;
   8.187 +	}
   8.188 +
   8.189 +	//Check for an existing identical hook
   8.190 +	SourceHook::List<omg_hooks *>::iterator _iter;
   8.191 +
   8.192 +	omg_hooks *hook;
   8.193 +
   8.194 +	IPluginFunction *pFunction;
   8.195 +	pFunction = pContext->GetFunctionById(params[3]);
   8.196 +
   8.197 +	for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
   8.198 +	{
   8.199 +		hook = (omg_hooks *)*_iter;
   8.200 +		if (hook->pf == pFunction && hook->entity_filter == -1)
   8.201 +		{
   8.202 +			// remove this hook.
   8.203 +			if (hook->in_use)
   8.204 +			{
   8.205 +				hook->delete_me = true;
   8.206 +				return 1;
   8.207 +			}
   8.208 +
   8.209 +			pOutputName->hooks.erase(_iter);
   8.210 +			g_OutputManager.CleanUpHook(hook);
   8.211 +
   8.212 +			return 1;
   8.213 +		}
   8.214 +	}
   8.215 +
   8.216 +	return 0;
   8.217 +}
   8.218 +
   8.219 +// UnHookSingleEntityOutput(entity, const String:output[], EntityOutput:callback);
   8.220 +cell_t UnHookSingleEntityOutput(IPluginContext *pContext, const cell_t *params)
   8.221 +{
   8.222 +	if (!g_OutputManager.IsEnabled())
   8.223 +	{
   8.224 +		return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
   8.225 +	}
   8.226 +
   8.227 +	// Find the classname of the entity and lookup the classname and output structures
   8.228 +	edict_t *pEdict = engine->PEntityOfEntIndex(params[1]);
   8.229 +	if (!pEdict)
   8.230 +	{
   8.231 +		return pContext->ThrowNativeError("Invalid Entity index %i", params[1]);
   8.232 +	}
   8.233 +
   8.234 +	const char *classname = pEdict->GetClassName();
   8.235 +
   8.236 +	char *outputname;
   8.237 +	pContext->LocalToString(params[2], &outputname);
   8.238 +
   8.239 +	OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, false);
   8.240 +
   8.241 +	if (!pOutputName)
   8.242 +	{
   8.243 +		return 0;
   8.244 +	}
   8.245 +
   8.246 +	//Check for an existing identical hook
   8.247 +	SourceHook::List<omg_hooks *>::iterator _iter;
   8.248 +
   8.249 +	omg_hooks *hook;
   8.250 +
   8.251 +	IPluginFunction *pFunction;
   8.252 +	pFunction = pContext->GetFunctionById(params[3]);
   8.253 +
   8.254 +	for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
   8.255 +	{
   8.256 +		hook = (omg_hooks *)*_iter;
   8.257 +		if (hook->pf == pFunction && hook->entity_index == engine->IndexOfEdict(pEdict))
   8.258 +		{
   8.259 +			// remove this hook.
   8.260 +			if (hook->in_use)
   8.261 +			{
   8.262 +				hook->delete_me = true;
   8.263 +				return 1;
   8.264 +			}
   8.265 +
   8.266 +			pOutputName->hooks.erase(_iter);
   8.267 +			g_OutputManager.CleanUpHook(hook);
   8.268 +
   8.269 +			return 1;
   8.270 +		}
   8.271 +	}
   8.272 +
   8.273 +	return 0;
   8.274 +}
   8.275 +
   8.276 +#if 0
   8.277 +static cell_t GetVariantType(IPluginContext *pContext, const cell_t *params)
   8.278 +{
   8.279 +	Handle_t hndl = static_cast<Handle_t>(params[1]);
   8.280 +	HandleError err;
   8.281 +	varhandle_t *pInfo;
   8.282 +	HandleSecurity sec;
   8.283 + 
   8.284 +	sec.pOwner = NULL;
   8.285 +	sec.pIdentity = myself->GetIdentity();
   8.286 +
   8.287 +	if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
   8.288 +		!= HandleError_None)
   8.289 +	{
   8.290 +		return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
   8.291 +	}
   8.292 +
   8.293 +	return pInfo->crab.FieldType();
   8.294 +}
   8.295 +
   8.296 +static cell_t GetVariantInt(IPluginContext *pContext, const cell_t *params)
   8.297 +{
   8.298 +	Handle_t hndl = static_cast<Handle_t>(params[1]);
   8.299 +	HandleError err;
   8.300 +	varhandle_t *pInfo;
   8.301 +	HandleSecurity sec;
   8.302 + 
   8.303 +	sec.pOwner = NULL;
   8.304 +	sec.pIdentity = myself->GetIdentity();
   8.305 +
   8.306 +
   8.307 +	if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
   8.308 +		!= HandleError_None)
   8.309 +	{
   8.310 +		return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
   8.311 +	}
   8.312 +
   8.313 +	if (pInfo->crab.FieldType() != FIELD_INTEGER)
   8.314 +	{
   8.315 +		return pContext->ThrowNativeError("Variant is not an integer");
   8.316 +	}
   8.317 +
   8.318 +	return pInfo->crab.Int();
   8.319 +}
   8.320 +
   8.321 +static cell_t GetVariantBool(IPluginContext *pContext, const cell_t *params)
   8.322 +{
   8.323 +	Handle_t hndl = static_cast<Handle_t>(params[1]);
   8.324 +	HandleError err;
   8.325 +	varhandle_t *pInfo;
   8.326 +	HandleSecurity sec;
   8.327 + 
   8.328 +	sec.pOwner = NULL;
   8.329 +	sec.pIdentity = myself->GetIdentity();
   8.330 +
   8.331 +	if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
   8.332 +		!= HandleError_None)
   8.333 +	{
   8.334 +		return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
   8.335 +	}
   8.336 +
   8.337 +	if (pInfo->crab.FieldType() != FIELD_BOOLEAN)
   8.338 +	{
   8.339 +		return pContext->ThrowNativeError("Variant is not a boolean");
   8.340 +	}
   8.341 +
   8.342 +	return pInfo->crab.Bool();
   8.343 +}
   8.344 +
   8.345 +static cell_t GetVariantEntity(IPluginContext *pContext, const cell_t *params)
   8.346 +{
   8.347 +	Handle_t hndl = static_cast<Handle_t>(params[1]);
   8.348 +	HandleError err;
   8.349 +	varhandle_t *pInfo;
   8.350 +	HandleSecurity sec;
   8.351 + 
   8.352 +	sec.pOwner = NULL;
   8.353 +	sec.pIdentity = myself->GetIdentity();
   8.354 +
   8.355 +	if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
   8.356 +		!= HandleError_None)
   8.357 +	{
   8.358 +		return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
   8.359 +	}
   8.360 +
   8.361 +	if (pInfo->crab.FieldType() != FIELD_EHANDLE)
   8.362 +	{
   8.363 +		return pContext->ThrowNativeError("Variant is not an entity");
   8.364 +	}
   8.365 +
   8.366 +	edict_t *pEdict = g_OutputManager.BaseHandleToEdict((CBaseHandle)pInfo->crab.Entity());
   8.367 +
   8.368 +	return engine->IndexOfEdict(pEdict);
   8.369 +}
   8.370 +
   8.371 +static cell_t GetVariantFloat(IPluginContext *pContext, const cell_t *params)
   8.372 +{
   8.373 +	Handle_t hndl = static_cast<Handle_t>(params[1]);
   8.374 +	HandleError err;
   8.375 +	varhandle_t *pInfo;
   8.376 +	HandleSecurity sec;
   8.377 + 
   8.378 +	sec.pOwner = NULL;
   8.379 +	sec.pIdentity = myself->GetIdentity();
   8.380 +
   8.381 +	if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
   8.382 +		!= HandleError_None)
   8.383 +	{
   8.384 +		return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
   8.385 +	}
   8.386 +
   8.387 +	if (pInfo->crab.FieldType() != FIELD_FLOAT)
   8.388 +	{
   8.389 +		return pContext->ThrowNativeError("Variant is not a float");
   8.390 +	}
   8.391 +
   8.392 +	return sp_ftoc(pInfo->crab.Float());
   8.393 +}
   8.394 +
   8.395 +static cell_t GetVariantVector(IPluginContext *pContext, const cell_t *params)
   8.396 +{
   8.397 +	Handle_t hndl = static_cast<Handle_t>(params[1]);
   8.398 +	HandleError err;
   8.399 +	varhandle_t *pInfo;
   8.400 +	HandleSecurity sec;
   8.401 + 
   8.402 +	sec.pOwner = NULL;
   8.403 +	sec.pIdentity = myself->GetIdentity();
   8.404 +
   8.405 +	if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
   8.406 +		!= HandleError_None)
   8.407 +	{
   8.408 +		return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
   8.409 +	}
   8.410 +
   8.411 +	if (pInfo->crab.FieldType() != FIELD_VECTOR)
   8.412 +	{
   8.413 +		return pContext->ThrowNativeError("Variant is not a vector");
   8.414 +	}
   8.415 +		
   8.416 +	cell_t *r;
   8.417 +	pContext->LocalToPhysAddr(params[2], &r);
   8.418 +
   8.419 +	Vector temp;
   8.420 +	pInfo->crab.Vector3D(temp);
   8.421 +
   8.422 +	r[0] = sp_ftoc(temp[0]);
   8.423 +	r[1] = sp_ftoc(temp[1]);
   8.424 +	r[2] = sp_ftoc(temp[2]);
   8.425 +
   8.426 +	return 0;
   8.427 +}
   8.428 +
   8.429 +static cell_t GetVariantColour(IPluginContext *pContext, const cell_t *params)
   8.430 +{
   8.431 +	Handle_t hndl = static_cast<Handle_t>(params[1]);
   8.432 +	HandleError err;
   8.433 +	varhandle_t *pInfo;
   8.434 +	HandleSecurity sec;
   8.435 + 
   8.436 +	sec.pOwner = NULL;
   8.437 +	sec.pIdentity = myself->GetIdentity();
   8.438 +
   8.439 +	if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
   8.440 +		!= HandleError_None)
   8.441 +	{
   8.442 +		return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
   8.443 +	}
   8.444 +
   8.445 +	if (pInfo->crab.FieldType() != FIELD_COLOR32)
   8.446 +	{
   8.447 +		return pContext->ThrowNativeError("Variant is not a colour");
   8.448 +	}
   8.449 +		
   8.450 +	cell_t *r;
   8.451 +	pContext->LocalToPhysAddr(params[2], &r);
   8.452 +
   8.453 +	color32 temp = pInfo->crab.Color32();
   8.454 +
   8.455 +	r[0] = temp.r;
   8.456 +	r[1] = temp.g;
   8.457 +	r[2] = temp.b;
   8.458 +	r[4] = temp.a;
   8.459 +
   8.460 +	return 0;
   8.461 +}
   8.462 +
   8.463 +static cell_t GetVariantString(IPluginContext *pContext, const cell_t *params)
   8.464 +{
   8.465 +	Handle_t hndl = static_cast<Handle_t>(params[1]);
   8.466 +	HandleError err;
   8.467 +	varhandle_t *pInfo;
   8.468 +	HandleSecurity sec;
   8.469 + 
   8.470 +	sec.pOwner = NULL;
   8.471 +	sec.pIdentity = myself->GetIdentity();
   8.472 +
   8.473 +	if ((err=handlesys->ReadHandle(hndl, g_OutputManager.VariantHandle, &sec, (void **)&pInfo))
   8.474 +		!= HandleError_None)
   8.475 +	{
   8.476 +		return pContext->ThrowNativeError("Invalid variant handle %x (error %d)", hndl, err);
   8.477 +	}
   8.478 +		
   8.479 +	char *dest;
   8.480 +	const char *src = pInfo->crab.String();
   8.481 +	size_t len;
   8.482 + 
   8.483 +	pContext->LocalToString(params[2], &dest);
   8.484 + 
   8.485 +	/* Perform bounds checking */
   8.486 +	len = strlen(src);
   8.487 +	if (len >= (unsigned)params[3])
   8.488 +	{
   8.489 +		len = params[3] - 1;
   8.490 +	} else {
   8.491 +		len = params[3];
   8.492 +	}
   8.493 + 
   8.494 +	/* Copy */
   8.495 +	memmove(dest, src, len);
   8.496 + 
   8.497 +	dest[len] = '\0';
   8.498 + 
   8.499 +	return 0;
   8.500 +}
   8.501 +#endif
   8.502 +
   8.503 +sp_nativeinfo_t g_EntOutputNatives[] =
   8.504 +{
   8.505 +	{"HookEntityOutput",			HookEntityOutput},
   8.506 +	{"UnHookEntityOutput",			UnHookEntityOutput},
   8.507 +	{"HookSingleEntityOutput",		HookSingleEntityOutput},
   8.508 +	{"UnHookSingleEntityOutput",	UnHookSingleEntityOutput},
   8.509 +#if 0 //Removed because we don't need them
   8.510 +	{"GetVariantType",				GetVariantType},
   8.511 +	{"GetVariantInt",				GetVariantInt},
   8.512 +	{"GetVariantFloat",				GetVariantFloat},
   8.513 +	{"GetVariantBool",				GetVariantBool},
   8.514 +	{"GetVariantString",			GetVariantString},
   8.515 +	{"GetVariantEntity",			GetVariantEntity},
   8.516 +	{"GetVariantVector",			GetVariantVector},
   8.517 +	{"GetVariantColour",			GetVariantColour},
   8.518 +#endif
   8.519 +	{NULL,							NULL},
   8.520 +};
   8.521 +
   8.522 +#if 0
   8.523 +//Include file stuff that wasn't needed:
   8.524 +
   8.525 +enum FieldType
   8.526 +{
   8.527 +	FieldType_Float = 1,
   8.528 +	FieldType_String = 2,
   8.529 +	FieldType_Vector = 3,
   8.530 +	FieldType_Int = 5,
   8.531 +	FieldType_Bool = 6,
   8.532 +	FieldType_Colour = 9,
   8.533 +	FieldType_Entity = 13
   8.534 +}
   8.535 +
   8.536 +/**
   8.537 + * Gets the current type of a variant handle
   8.538 + *
   8.539 + * @param variant		A variant handle.
   8.540 + * @return				Current type of the variant.
   8.541 + */
   8.542 +native FieldType:GetVariantType(Handle:variant);
   8.543 +
   8.544 +/**
   8.545 + * Retreives a Float from a variant handle.
   8.546 + *
   8.547 + * @param variant		A variant handle.
   8.548 + * @return				Float value.
   8.549 + * @error				Variant handle is not type FieldType_Float
   8.550 + */
   8.551 +native Float:GetVariantFloat(Handle:variant);
   8.552 +
   8.553 +/**
   8.554 + * Retreives an Integer from a variant handle.
   8.555 + *
   8.556 + * @param variant		A variant handle.
   8.557 + * @return				Int value.
   8.558 + * @error				Variant handle is not type FieldType_Int
   8.559 + */
   8.560 +native GetVariantInt(Handle:variant);
   8.561 +
   8.562 +/**
   8.563 + * Retreives a bool from a variant handle.
   8.564 + *
   8.565 + * @param variant		A variant handle.
   8.566 + * @return				bool value.
   8.567 + * @error				Variant handle is not type FieldType_Bool
   8.568 + */
   8.569 +native bool:GetVariantBool(Handle:variant);
   8.570 +
   8.571 +/**
   8.572 + * Retreives an entity from a variant handle.
   8.573 + *
   8.574 + * @param variant		A variant handle.
   8.575 + * @return				Entity Index.
   8.576 + * @error				Variant handle is not type FieldType_Entity
   8.577 + */
   8.578 +native GetVariantEntity(Handle:variant);
   8.579 +
   8.580 +/**
   8.581 + * Retreives a Vector from a variant handle.
   8.582 + *
   8.583 + * @param variant		A variant handle.
   8.584 + * @param vec			buffer to store the Vector.
   8.585 + * @noreturn
   8.586 + * @error				Variant handle is not type FieldType_Vector
   8.587 + */
   8.588 +native GetVariantVector(Handle:variant, Float:vec[3]);
   8.589 +
   8.590 +/**
   8.591 + * Retreives a String from a variant handle.
   8.592 + *
   8.593 + * @note 	This native does not require the variant to be FieldType_String,
   8.594 + *			It can convert any type into a string representation.
   8.595 + *
   8.596 + * @param variant		A variant handle.
   8.597 + * @param buffer		buffer to store the string in
   8.598 + * @param maxlen		Maximum length of buffer.
   8.599 + * @noreturn
   8.600 + */
   8.601 +native GetVariantString(Handle:variant, String:buffer[], maxlen);
   8.602 +
   8.603 +/**
   8.604 + * Retreives a Colour from a variant handle.
   8.605 + *
   8.606 + * @note Colour is in format r,g,b,a
   8.607 + *
   8.608 + * @param variant		A variant handle.
   8.609 + * @param colour		buffer array to store the colour in.
   8.610 + * @noreturn
   8.611 + * @error				Variant handle is not type FieldType_Colour
   8.612 + */
   8.613 +native GetVariantColour(Handle:variant, colour[4]);
   8.614 +#endif
     9.1 --- a/gamedata/sdktools.games.ep2.txt	Tue Apr 01 04:22:47 2008 +0000
     9.2 +++ b/gamedata/sdktools.games.ep2.txt	Wed Apr 02 01:22:02 2008 +0000
     9.3 @@ -305,12 +305,20 @@
     9.4  		}
     9.5  		"Signatures"
     9.6  		{
     9.7 -			"FireEvent"
     9.8 +			"FireOutput"
     9.9  			{
    9.10  				"library"	"server"
    9.11  				"windows"	"\x81\xEC\x1C\x01\x00\x00\x53\x55\x56\x8B\x71\x14\x85\xF6"
    9.12  				"linux"		"@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f"
    9.13  			}
    9.14  		}
    9.15 +		"Offsets"
    9.16 +		{
    9.17 +			"FireOutputBackup"
    9.18 +			{
    9.19 +				"windows"	"6"
    9.20 +				"linux"		"10"
    9.21 +			}
    9.22 +		}
    9.23  	}
    9.24  }
    10.1 --- a/gamedata/sdktools.games.txt	Tue Apr 01 04:22:47 2008 +0000
    10.2 +++ b/gamedata/sdktools.games.txt	Wed Apr 02 01:22:02 2008 +0000
    10.3 @@ -1707,13 +1707,21 @@
    10.4  		}
    10.5  		"Signatures"
    10.6  		{
    10.7 -			"FireEvent"
    10.8 +			"FireOutput"
    10.9  			{
   10.10  				"library"	"server"
   10.11  				"windows"	"\x81\xEC\x1C\x03\x00\x00\x53\x55\x56\x8B\x71\x14"
   10.12  				"linux"		"@_ZN17CBaseEntityOutput10FireOutputE9variant_tP11CBaseEntityS2_f"
   10.13  			}
   10.14  		}
   10.15 +		"Offsets"
   10.16 +		{
   10.17 +			"FireOutputBackup"
   10.18 +			{
   10.19 +				"windows"	"6"
   10.20 +				"linux"		"6"
   10.21 +			}
   10.22 +		}
   10.23  	}
   10.24  }
   10.25  
    11.1 --- a/plugins/include/sdktools.inc	Tue Apr 01 04:22:47 2008 +0000
    11.2 +++ b/plugins/include/sdktools.inc	Wed Apr 02 01:22:02 2008 +0000
    11.3 @@ -47,6 +47,7 @@
    11.4  #include <sdktools_tempents_stocks>
    11.5  #include <sdktools_voice>
    11.6  #include <sdktools_entinput>
    11.7 +#include <sdktools_entoutput>
    11.8  
    11.9  enum SDKCallType
   11.10  {
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/plugins/include/sdktools_entoutput.inc	Wed Apr 02 01:22:02 2008 +0000
    12.3 @@ -0,0 +1,91 @@
    12.4 +/**
    12.5 + * vim: set ts=4 :
    12.6 + * =============================================================================
    12.7 + * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
    12.8 + * =============================================================================
    12.9 + *
   12.10 + * This file is part of the SourceMod/SourcePawn SDK.
   12.11 + *
   12.12 + * This program is free software; you can redistribute it and/or modify it under
   12.13 + * the terms of the GNU General Public License, version 3.0, as published by the
   12.14 + * Free Software Foundation.
   12.15 + * 
   12.16 + * This program is distributed in the hope that it will be useful, but WITHOUT
   12.17 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   12.18 + * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
   12.19 + * details.
   12.20 + *
   12.21 + * You should have received a copy of the GNU General Public License along with
   12.22 + * this program.  If not, see <http://www.gnu.org/licenses/>.
   12.23 + *
   12.24 + * As a special exception, AlliedModders LLC gives you permission to link the
   12.25 + * code of this program (as well as its derivative works) to "Half-Life 2," the
   12.26 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
   12.27 + * by the Valve Corporation.  You must obey the GNU General Public License in
   12.28 + * all respects for all other code used.  Additionally, AlliedModders LLC grants
   12.29 + * this exception to all derivative works.  AlliedModders LLC defines further
   12.30 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
   12.31 + * or <http://www.sourcemod.net/license.php>.
   12.32 + *
   12.33 + * Version: $Id: sdktools_entoutput.inc 1504 2007-09-28 19:56:19Z faluco $
   12.34 + */
   12.35 +
   12.36 +#if defined _sdktools_entoutput_included
   12.37 +  #endinput
   12.38 +#endif
   12.39 +#define _sdktools_entoutput_included
   12.40 +
   12.41 +/**
   12.42 + * Called when an entity output is fired.
   12.43 + *
   12.44 + * @param output		Name of the output that fired.
   12.45 + * @param caller		Entity index of the caller.
   12.46 + * @param activator		Entity index of the activator.
   12.47 + * @param delay			Delay in seconds? before the event gets fired.
   12.48 + */
   12.49 +functag EntityOutput public(const String:output[], caller, activator, Float:delay);
   12.50 +
   12.51 +/**
   12.52 + * Add an entity output hook on a entity classname
   12.53 + *
   12.54 + * @param classname		The classname to hook.
   12.55 + * @param output		The output name to hook.
   12.56 + * @param callback		An EntityOutput function pointer.
   12.57 + * @noreturn
   12.58 + * @error				Entity Outputs disabled.
   12.59 + */
   12.60 +native HookEntityOutput(const String:classname[], const String:output[], EntityOutput:callback);
   12.61 +
   12.62 +/**
   12.63 + * Remove an entity output hook.
   12.64 + * @param classname		The classname to hook.
   12.65 + * @param output		The output name to hook.
   12.66 + * @param callback		An EntityOutput function pointer.
   12.67 + * @return				True on success, false if no valid hook was found.
   12.68 + * @error				Entity Outputs disabled.
   12.69 + */
   12.70 +native bool:UnHookEntityOutput(const String:classname[], const String:output[], EntityOutput:callback);
   12.71 +
   12.72 +/**
   12.73 + * Add an entity output hook on a single entity instance	
   12.74 + *
   12.75 + * @param entity		The entity on which to add a hook.
   12.76 + * @param output		The output name to hook.
   12.77 + * @param callback		An EntityOutput function pointer.
   12.78 + * @param once			Only fire this hook once and then remove itself.
   12.79 + * @noreturn			
   12.80 + * @error				Entity Outputs disabled	or Invalid Entity index.
   12.81 + */
   12.82 +native HookSingleEntityOutput(entity, const String:output[], EntityOutput:callback , bool:once=false);
   12.83 +
   12.84 +/**
   12.85 + * Remove a single entity output hook.	
   12.86 + *
   12.87 + * @param entity		The entity on which to remove the hook.
   12.88 + * @param output		The output name to hook.
   12.89 + * @param callback		An EntityOutput function pointer.
   12.90 + * @return				True on success, false if no valid hook was found.
   12.91 + * @error				Entity Outputs disabled	or Invalid Entity index.	
   12.92 + */
   12.93 +native bool:UnHookSingleEntityOutput(entity, const String:output[], EntityOutput:callback);
   12.94 +
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/plugins/testsuite/outputtest.sp	Wed Apr 02 01:22:02 2008 +0000
    13.3 @@ -0,0 +1,59 @@
    13.4 +#include <sourcemod>
    13.5 +#include <sdktools>
    13.6 +
    13.7 +public Plugin:myinfo = 
    13.8 +{
    13.9 +	name = "Entity Output Hook Testing",
   13.10 +	author = "AlliedModders LLC",
   13.11 +	description = "Test suite for Entity Output Hooks",
   13.12 +	version = "1.0.0.0",
   13.13 +	url = "http://www.sourcemod.net/"
   13.14 +};
   13.15 +
   13.16 +public OnPluginStart()
   13.17 +{
   13.18 +	HookEntityOutput("point_spotlight", "OnLightOn", OutputHook);
   13.19 +	
   13.20 +	HookEntityOutput("func_door", "OnOpen", OutputHook);
   13.21 +	HookEntityOutput("func_door_rotating", "OnOpen", OutputHook);
   13.22 +	HookEntityOutput("func_door", "OnClose", OutputHook);
   13.23 +	HookEntityOutput("func_door_rotating", "OnClose", OutputHook);
   13.24 +}
   13.25 +
   13.26 +public OutputHook(const String:name[], caller, activator, Float:delay)
   13.27 +{
   13.28 +	LogMessage("[ENTOUTPUT] %s", name);
   13.29 +}
   13.30 +
   13.31 +public OnMapStart()
   13.32 +{
   13.33 +	new ent = FindEntityByClassname(-1, "point_spotlight");
   13.34 +	
   13.35 +	if (ent == -1)
   13.36 +	{
   13.37 +		LogError("Could not find a point_spotlight");
   13.38 +		ent = CreateEntityByName("point_spotlight");
   13.39 +		DispatchSpawn(ent);
   13.40 +	}
   13.41 +
   13.42 +	HookSingleEntityOutput(ent, "OnLightOn", OutputHook, true);
   13.43 +	HookSingleEntityOutput(ent, "OnLightOff", OutputHook, true);
   13.44 +	
   13.45 +	AcceptEntityInput(ent, "LightOff", ent, ent);
   13.46 +	AcceptEntityInput(ent, "LightOn", ent, ent);
   13.47 +	
   13.48 +	AcceptEntityInput(ent, "LightOff", ent, ent);
   13.49 +	AcceptEntityInput(ent, "LightOn", ent, ent);
   13.50 +	
   13.51 +	HookSingleEntityOutput(ent, "OnLightOn", OutputHook, false);
   13.52 +	HookSingleEntityOutput(ent, "OnLightOff", OutputHook, false);
   13.53 +	
   13.54 +	AcceptEntityInput(ent, "LightOff", ent, ent);
   13.55 +	AcceptEntityInput(ent, "LightOn", ent, ent);
   13.56 +	AcceptEntityInput(ent, "LightOff", ent, ent);
   13.57 +	AcceptEntityInput(ent, "LightOn", ent, ent);
   13.58 +	
   13.59 +	//Comment these out (and reload the plugin heaps) to test for leaks on plugin unload
   13.60 +	UnHookSingleEntityOutput(ent, "OnLightOn", OutputHook);
   13.61 +	UnHookSingleEntityOutput(ent, "OnLightOff", OutputHook);
   13.62 +}
   13.63 \ No newline at end of file