Added amb1528 - Entity Output Hooking.
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;"$(HL2SDK)\public";"$(HL2SDK)\public\dlls";"$(HL2SDK)\public\engine";"$(HL2SDK)\public\tier0";"$(HL2SDK)\public\tier1";"$(SOURCEMM14)";"$(SOURCEMM14)\sourcemm";"$(SOURCEMM14)\sourcehook""
5.8 + AdditionalIncludeDirectories="..;..\sdk;..\..\..\public;..\..\..\public\extensions;..\..\..\public\sourcepawn;"$(HL2SDK)\public";"$(HL2SDK)\public\dlls";"$(HL2SDK)\public\engine";"$(HL2SDK)\public\tier0";"$(HL2SDK)\public\tier1";"$(SOURCEMM142)";"$(SOURCEMM142)\sourcemm";"$(SOURCEMM142)\sourcehook""
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 = ≀
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