extensions/sdktools/outputnatives.cpp
author Matt Woodrow <pred@alliedmods.net>
Fri Jul 24 12:34:31 2009 +1200 (2009-07-24)
changeset 2746 8d68569b9648
parent 2400 76b7b4575e21
permissions -rw-r--r--
Added support for logical (non-networked) entities using entity references - See http://wiki.alliedmods.net/Entity_References_%28SourceMod%29 (bug 2459, r=dvander)
pred@1958
     1
/**
pred@1958
     2
 * vim: set ts=4 :
pred@1958
     3
 * =============================================================================
pred@1958
     4
 * SourceMod SDKTools Extension
ds@1990
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
pred@1958
     6
 * =============================================================================
pred@1958
     7
 *
pred@1958
     8
 * This program is free software; you can redistribute it and/or modify it under
pred@1958
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
pred@1958
    10
 * Free Software Foundation.
pred@1958
    11
 * 
pred@1958
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
pred@1958
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
pred@1958
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
pred@1958
    15
 * details.
pred@1958
    16
 *
pred@1958
    17
 * You should have received a copy of the GNU General Public License along with
pred@1958
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
pred@1958
    19
 *
pred@1958
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
pred@1958
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
pred@1958
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
pred@1958
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
pred@1958
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
pred@1958
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
pred@1958
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
pred@1958
    27
 * or <http://www.sourcemod.net/license.php>.
pred@1958
    28
 *
ds@1987
    29
 * Version: $Id$
pred@1958
    30
 */
pred@1958
    31
pred@1958
    32
#include "extension.h"
pred@1958
    33
#include "output.h"
pred@1958
    34
pred@1958
    35
// HookSingleEntityOutput(ent, const String:output[], function, bool:once);
pred@1958
    36
cell_t HookSingleEntityOutput(IPluginContext *pContext, const cell_t *params)
pred@1958
    37
{
pred@1958
    38
	if (!g_OutputManager.IsEnabled())
pred@1958
    39
	{
pred@1958
    40
		return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
pred@1958
    41
	}
pred@1958
    42
pred@2746
    43
	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(params[1]);
pred@2746
    44
	if (!pEntity)
pred@1958
    45
	{
pred@2746
    46
		return pContext->ThrowNativeError("Invalid Entity index %i (%i)", gamehelpers->ReferenceToIndex(params[1]), params[1]);
pred@1958
    47
	}
pred@2746
    48
pred@2746
    49
	const char *classname = g_OutputManager.GetEntityClassname(pEntity);
pred@1958
    50
pred@1958
    51
	char *outputname;
pred@1958
    52
	pContext->LocalToString(params[2], &outputname);
pred@1958
    53
pred@1958
    54
	OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, true);
pred@1958
    55
pred@1958
    56
	//Check for an existing identical hook
pred@1958
    57
	SourceHook::List<omg_hooks *>::iterator _iter;
pred@1958
    58
pred@1958
    59
	omg_hooks *hook;
pred@1958
    60
pred@1958
    61
	IPluginFunction *pFunction;
pred@1958
    62
	pFunction = pContext->GetFunctionById(params[3]);
pred@1958
    63
pred@1958
    64
	for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
pred@1958
    65
	{
pred@1958
    66
		hook = (omg_hooks *)*_iter;
pred@2746
    67
		if (hook->pf == pFunction && hook->entity_ref == gamehelpers->EntityToReference(pEntity))
pred@1958
    68
		{
pred@1958
    69
			return 0;
pred@1958
    70
		}
pred@1958
    71
	}
pred@1958
    72
pred@1958
    73
	hook = g_OutputManager.NewHook();
pred@1958
    74
pred@2746
    75
	hook->entity_ref = gamehelpers->EntityToReference(pEntity);
pred@1958
    76
	hook->only_once= !!params[4];
pred@1958
    77
	hook->pf = pFunction;
pred@1958
    78
	hook->m_parent = pOutputName;
pred@1958
    79
	hook->in_use = false;
pred@1958
    80
	hook->delete_me = false;
pred@1958
    81
pred@1958
    82
	pOutputName->hooks.push_back(hook);
pred@1958
    83
pred@1958
    84
	g_OutputManager.OnHookAdded();
pred@1958
    85
pred@1958
    86
	IPlugin *pPlugin = plsys->FindPluginByContext(pContext->GetContext());
pred@1958
    87
	SourceHook::List<omg_hooks *> *pList = NULL;
pred@1958
    88
pred@1958
    89
	if (!pPlugin->GetProperty("OutputHookList", (void **)&pList, false) || !pList)
pred@1958
    90
	{
pred@1958
    91
		pList = new SourceHook::List<omg_hooks *>;
pred@1958
    92
		pPlugin->SetProperty("OutputHookList", pList);
pred@1958
    93
	}
pred@1958
    94
pred@1958
    95
	pList->push_back(hook);
pred@1958
    96
pred@1958
    97
	return 1;
pred@1958
    98
}
pred@1958
    99
pred@1958
   100
// HookEntityOutput(const String:classname[], const String:output[], function);
pred@1958
   101
cell_t HookEntityOutput(IPluginContext *pContext, const cell_t *params)
pred@1958
   102
{
pred@1958
   103
	if (!g_OutputManager.IsEnabled())
pred@1958
   104
	{
pred@1958
   105
		return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
pred@1958
   106
	}
pred@1958
   107
pred@1958
   108
	//Find or create the base structures for this classname and the output
pred@1958
   109
	char *classname;
pred@1958
   110
	pContext->LocalToString(params[1], &classname);
pred@1958
   111
pred@1958
   112
	char *outputname;
pred@1958
   113
	pContext->LocalToString(params[2], &outputname);
pred@1958
   114
pred@1958
   115
	OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, true);
pred@1958
   116
pred@1958
   117
	//Check for an existing identical hook
pred@1958
   118
	SourceHook::List<omg_hooks *>::iterator _iter;
pred@1958
   119
pred@1958
   120
	omg_hooks *hook;
pred@1958
   121
pred@1958
   122
	IPluginFunction *pFunction;
pred@1958
   123
	pFunction = pContext->GetFunctionById(params[3]);
pred@1958
   124
pred@1958
   125
	for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
pred@1958
   126
	{
pred@1958
   127
		hook = (omg_hooks *)*_iter;
pred@2746
   128
		if (hook->pf == pFunction && hook->entity_ref == -1)
pred@1958
   129
		{
pred@1958
   130
			//already hooked to this function...
pred@1958
   131
			//throw an error or just let them get away with stupidity?
pred@1958
   132
			// seems like poor coding if they dont know if something is hooked or not
pred@1958
   133
			return 0;
pred@1958
   134
		}
pred@1958
   135
	}
pred@1958
   136
pred@1958
   137
	hook = g_OutputManager.NewHook();
pred@1958
   138
pred@2746
   139
	hook->entity_ref = -1;
pred@1958
   140
	hook->pf = pFunction;
pred@1958
   141
	hook->m_parent = pOutputName;
pred@1958
   142
	hook->in_use = false;
pred@1958
   143
	hook->delete_me = false;
pred@1958
   144
pred@1958
   145
	pOutputName->hooks.push_back(hook);
pred@1958
   146
pred@1958
   147
	g_OutputManager.OnHookAdded();
pred@1958
   148
pred@1958
   149
	IPlugin *pPlugin = plsys->FindPluginByContext(pContext->GetContext());
pred@1958
   150
	SourceHook::List<omg_hooks *> *pList = NULL;
pred@1958
   151
pred@1958
   152
	if (!pPlugin->GetProperty("OutputHookList", (void **)&pList, false) || !pList)
pred@1958
   153
	{
pred@1958
   154
		pList = new SourceHook::List<omg_hooks *>;
pred@1958
   155
		pPlugin->SetProperty("OutputHookList", pList);
pred@1958
   156
	}
pred@1958
   157
pred@1958
   158
	pList->push_back(hook);
pred@1958
   159
pred@1958
   160
	return 1;
pred@1958
   161
}
pred@1958
   162
pred@1958
   163
// UnHookEntityOutput(const String:classname[], const String:output[], EntityOutput:callback);
pred@1958
   164
cell_t UnHookEntityOutput(IPluginContext *pContext, const cell_t *params)
pred@1958
   165
{
pred@1958
   166
	if (!g_OutputManager.IsEnabled())
pred@1958
   167
	{
pred@1958
   168
		return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
pred@1958
   169
	}
pred@1958
   170
pred@1958
   171
	char *classname;
pred@1958
   172
	pContext->LocalToString(params[1], &classname);
pred@1958
   173
pred@1958
   174
	char *outputname;
pred@1958
   175
	pContext->LocalToString(params[2], &outputname);
pred@1958
   176
pred@1958
   177
	OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, false);
pred@1958
   178
pred@1958
   179
	if (!pOutputName)
pred@1958
   180
	{
pred@1958
   181
		return 0;
pred@1958
   182
	}
pred@1958
   183
pred@1958
   184
	//Check for an existing identical hook
pred@1958
   185
	SourceHook::List<omg_hooks *>::iterator _iter;
pred@1958
   186
pred@1958
   187
	omg_hooks *hook;
pred@1958
   188
pred@1958
   189
	IPluginFunction *pFunction;
pred@1958
   190
	pFunction = pContext->GetFunctionById(params[3]);
pred@1958
   191
pred@1958
   192
	for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
pred@1958
   193
	{
pred@1958
   194
		hook = (omg_hooks *)*_iter;
pred@2746
   195
		if (hook->pf == pFunction && hook->entity_ref == -1)
pred@1958
   196
		{
pred@1958
   197
			// remove this hook.
pred@1958
   198
			if (hook->in_use)
pred@1958
   199
			{
pred@1958
   200
				hook->delete_me = true;
pred@1958
   201
				return 1;
pred@1958
   202
			}
pred@1958
   203
pred@1958
   204
			pOutputName->hooks.erase(_iter);
pred@1958
   205
			g_OutputManager.CleanUpHook(hook);
pred@1958
   206
pred@1958
   207
			return 1;
pred@1958
   208
		}
pred@1958
   209
	}
pred@1958
   210
pred@1958
   211
	return 0;
pred@1958
   212
}
pred@1958
   213
pred@1958
   214
// UnHookSingleEntityOutput(entity, const String:output[], EntityOutput:callback);
pred@1958
   215
cell_t UnHookSingleEntityOutput(IPluginContext *pContext, const cell_t *params)
pred@1958
   216
{
pred@1958
   217
	if (!g_OutputManager.IsEnabled())
pred@1958
   218
	{
pred@1958
   219
		return pContext->ThrowNativeError("Entity Outputs are disabled - See error logs for details");
pred@1958
   220
	}
pred@1958
   221
pred@1958
   222
	// Find the classname of the entity and lookup the classname and output structures
pred@2746
   223
	CBaseEntity *pEntity = gamehelpers->ReferenceToEntity(params[1]);
pred@2746
   224
	if (!pEntity)
pred@1958
   225
	{
pred@2746
   226
		return pContext->ThrowNativeError("Invalid Entity index %i (%i)", gamehelpers->ReferenceToIndex(params[1]), params[1]);
pred@1958
   227
	}
pred@1958
   228
pred@2746
   229
	const char *classname = g_OutputManager.GetEntityClassname(pEntity);
pred@1958
   230
pred@1958
   231
	char *outputname;
pred@1958
   232
	pContext->LocalToString(params[2], &outputname);
pred@1958
   233
pred@1958
   234
	OutputNameStruct *pOutputName = g_OutputManager.FindOutputPointer((const char *)classname, outputname, false);
pred@1958
   235
pred@1958
   236
	if (!pOutputName)
pred@1958
   237
	{
pred@1958
   238
		return 0;
pred@1958
   239
	}
pred@1958
   240
pred@1958
   241
	//Check for an existing identical hook
pred@1958
   242
	SourceHook::List<omg_hooks *>::iterator _iter;
pred@1958
   243
pred@1958
   244
	omg_hooks *hook;
pred@1958
   245
pred@1958
   246
	IPluginFunction *pFunction;
pred@1958
   247
	pFunction = pContext->GetFunctionById(params[3]);
pred@1958
   248
pred@1958
   249
	for (_iter=pOutputName->hooks.begin(); _iter!=pOutputName->hooks.end(); _iter++)
pred@1958
   250
	{
pred@1958
   251
		hook = (omg_hooks *)*_iter;
pred@2746
   252
		/* We're not serial checking and just removing by index here - This was always allowed so is left for bcompat */
pred@2746
   253
		if (hook->pf == pFunction && gamehelpers->ReferenceToIndex(hook->entity_ref) == gamehelpers->ReferenceToIndex(params[1]))
pred@1958
   254
		{
pred@1958
   255
			// remove this hook.
pred@1958
   256
			if (hook->in_use)
pred@1958
   257
			{
pred@1958
   258
				hook->delete_me = true;
pred@1958
   259
				return 1;
pred@1958
   260
			}
pred@1958
   261
pred@1958
   262
			pOutputName->hooks.erase(_iter);
pred@1958
   263
			g_OutputManager.CleanUpHook(hook);
pred@1958
   264
pred@1958
   265
			return 1;
pred@1958
   266
		}
pred@1958
   267
	}
pred@1958
   268
pred@1958
   269
	return 0;
pred@1958
   270
}
pred@1958
   271
pred@1958
   272
sp_nativeinfo_t g_EntOutputNatives[] =
pred@1958
   273
{
pred@1958
   274
	{"HookEntityOutput",			HookEntityOutput},
pred@1960
   275
	{"UnhookEntityOutput",			UnHookEntityOutput},
pred@1958
   276
	{"HookSingleEntityOutput",		HookSingleEntityOutput},
pred@1960
   277
	{"UnhookSingleEntityOutput",	UnHookSingleEntityOutput},
pred@1958
   278
	{NULL,							NULL},
pred@2028
   279
};