core/smn_functions.cpp
author Scott Ehlert <ds@alliedmods.net>
Wed Aug 15 06:19:30 2007 +0000 (2007-08-15)
changeset 1330 ab94316bb52b
parent 1231 17badaf66ce4
child 1766 d30c252089be
permissions -rw-r--r--
Updated license headers on virtually all files with extremely minor changes in the name of some sort of strange consistency.
All plugin and include file headers also have been changed to say about GPL3 instead of GPL2.

(This day shall henceforth be known as the Eighty Column Massacre of '07)
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     8  * This program is free software; you can redistribute it and/or modify it under
     9  * the terms of the GNU General Public License, version 3.0, as published by the
    10  * Free Software Foundation.
    11  * 
    12  * This program is distributed in the hope that it will be useful, but WITHOUT
    13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    14  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
    15  * details.
    16  *
    17  * You should have received a copy of the GNU General Public License along with
    18  * this program.  If not, see <http://www.gnu.org/licenses/>.
    19  *
    20  * As a special exception, AlliedModders LLC gives you permission to link the
    21  * code of this program (as well as its derivative works) to "Half-Life 2," the
    22  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
    23  * by the Valve Corporation.  You must obey the GNU General Public License in
    24  * all respects for all other code used.  Additionally, AlliedModders LLC grants
    25  * this exception to all derivative works.  AlliedModders LLC defines further
    26  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
    27  * or <http://www.sourcemod.net/license.php>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include "sm_globals.h"
    33 #include "PluginSys.h"
    34 #include "ForwardSys.h"
    35 #include "HandleSys.h"
    36 
    37 HandleType_t g_GlobalFwdType = 0;
    38 HandleType_t g_PrivateFwdType = 0;
    39 
    40 static bool s_CallStarted = false;
    41 static ICallable *s_pCallable = NULL;
    42 static IPluginFunction *s_pFunction = NULL;
    43 static IForward *s_pForward = NULL;
    44 
    45 class ForwardNativeHelpers : 
    46 	public SMGlobalClass,
    47 	public IHandleTypeDispatch
    48 {
    49 public:
    50 	void OnSourceModAllInitialized()
    51 	{
    52 		HandleAccess sec;
    53 
    54 		/* Set GlobalFwd handle access security */
    55 		g_HandleSys.InitAccessDefaults(NULL, &sec);
    56 		sec.access[HandleAccess_Read] = 0;
    57 		sec.access[HandleAccess_Clone] = HANDLE_RESTRICT_IDENTITY | HANDLE_RESTRICT_OWNER;
    58 
    59 		/* Create 'GlobalFwd' handle type */
    60 		g_GlobalFwdType = g_HandleSys.CreateType("GlobalFwd", this, 0, NULL, &sec, g_pCoreIdent, NULL);
    61 
    62 		/* Private forwards are cloneable */
    63 		sec.access[HandleAccess_Clone] = 0;
    64 
    65 		/* Create 'PrivateFwd' handle type */
    66 		g_PrivateFwdType = g_HandleSys.CreateType("PrivateFwd", this, g_GlobalFwdType, NULL, &sec, g_pCoreIdent, NULL);
    67 	}
    68 
    69 	void OnSourceModShutdown()
    70 	{
    71 		g_HandleSys.RemoveType(g_PrivateFwdType, g_pCoreIdent);
    72 		g_HandleSys.RemoveType(g_GlobalFwdType, g_pCoreIdent);
    73 	}
    74 
    75 	void OnHandleDestroy(HandleType_t type, void *object)
    76 	{
    77 		IForward *pForward = static_cast<IForward *>(object);
    78 
    79 		g_Forwards.ReleaseForward(pForward);
    80 	}
    81 } g_ForwardNativeHelpers;
    82 
    83 
    84 /* Turn a public index into a function ID */
    85 inline funcid_t PublicIndexToFuncId(uint32_t idx)
    86 {
    87 	return (idx << 1) | (1 << 0);
    88 }
    89 
    90 /* Reset global function/forward call variables */
    91 inline void ResetCall()
    92 {
    93 	s_CallStarted = false;
    94 	s_pFunction = NULL;
    95 	s_pForward = NULL;
    96 	s_pCallable = NULL;
    97 }
    98 
    99 static cell_t sm_GetFunctionByName(IPluginContext *pContext, const cell_t *params)
   100 {
   101 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   102 	HandleError err;
   103 	char *name;
   104 	uint32_t idx;
   105 	IPlugin *pPlugin;
   106 
   107 	if (hndl == 0)
   108 	{
   109 		pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
   110 	} else {
   111 		pPlugin = g_PluginSys.PluginFromHandle(hndl, &err);
   112 
   113 		if (!pPlugin)
   114 		{
   115 			return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", hndl, err);
   116 		}
   117 	}
   118 
   119 	pContext->LocalToString(params[2], &name);
   120 
   121 	/* Get public function index */
   122 	if (pPlugin->GetBaseContext()->FindPublicByName(name, &idx) == SP_ERROR_NOT_FOUND)
   123 	{
   124 		/* Return INVALID_FUNCTION if not found */
   125 		return -1;
   126 	}
   127 
   128 	/* Return function ID */
   129 	return PublicIndexToFuncId(idx);
   130 }
   131 
   132 static cell_t sm_CreateGlobalForward(IPluginContext *pContext, const cell_t *params)
   133 {
   134 	cell_t count = params[0];
   135 	char *name;
   136 	ParamType forwardParams[SP_MAX_EXEC_PARAMS];
   137 
   138 	if (count - 2 > SP_MAX_EXEC_PARAMS)
   139 	{
   140 		return pContext->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, NULL);
   141 	}
   142 	
   143 	pContext->LocalToString(params[1], &name);
   144 
   145 	cell_t *addr;
   146 	for (int i = 3; i <= count; i++)
   147 	{
   148 		pContext->LocalToPhysAddr(params[i], &addr);
   149 		forwardParams[i - 3] = static_cast<ParamType>(*addr);
   150 	}
   151 
   152 	IForward *pForward = g_Forwards.CreateForward(name, static_cast<ExecType>(params[2]), count - 2, forwardParams);
   153 
   154 	return g_HandleSys.CreateHandle(g_GlobalFwdType, pForward, pContext->GetIdentity(), g_pCoreIdent, NULL);
   155 }
   156 
   157 static cell_t sm_CreateForward(IPluginContext *pContext, const cell_t *params)
   158 {
   159 	cell_t count = params[0];
   160 	ParamType forwardParams[SP_MAX_EXEC_PARAMS];
   161 
   162 	if (count - 1 > SP_MAX_EXEC_PARAMS)
   163 	{
   164 		return pContext->ThrowNativeErrorEx(SP_ERROR_PARAMS_MAX, NULL);
   165 	}
   166 
   167 	cell_t *addr;
   168 	for (int i = 2; i <= count; i++)
   169 	{
   170 		pContext->LocalToPhysAddr(params[i], &addr);
   171 		forwardParams[i - 2] = static_cast<ParamType>(*addr);
   172 	}
   173 
   174 	IChangeableForward *pForward = g_Forwards.CreateForwardEx(NULL, static_cast<ExecType>(params[1]), count - 1, forwardParams);
   175 
   176 	return g_HandleSys.CreateHandle(g_PrivateFwdType, pForward, pContext->GetIdentity(), g_pCoreIdent, NULL);
   177 }
   178 
   179 static cell_t sm_GetForwardFunctionCount(IPluginContext *pContext, const cell_t *params)
   180 {
   181 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   182 	HandleError err;
   183 	IForward *pForward;
   184 
   185 	if ((err=g_HandleSys.ReadHandle(hndl, g_GlobalFwdType, NULL, (void **)&pForward))
   186 		!= HandleError_None)
   187 	{
   188 		return pContext->ThrowNativeError("Invalid forward handle %x (error %d)", hndl, err);
   189 	}
   190 
   191 	return pForward->GetFunctionCount();
   192 }
   193 
   194 static cell_t sm_AddToForward(IPluginContext *pContext, const cell_t *params)
   195 {
   196 	Handle_t fwdHandle = static_cast<Handle_t>(params[1]);
   197 	Handle_t plHandle = static_cast<Handle_t>(params[2]);
   198 	HandleError err;
   199 	IChangeableForward *pForward;
   200 	IPlugin *pPlugin;
   201 
   202 	if ((err=g_HandleSys.ReadHandle(fwdHandle, g_PrivateFwdType, NULL, (void **)&pForward))
   203 		!= HandleError_None)
   204 	{
   205 		return pContext->ThrowNativeError("Invalid private forward handle %x (error %d)", fwdHandle, err);
   206 	}
   207 
   208 	if (plHandle == 0)
   209 	{
   210 		pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
   211 	} else {
   212 		pPlugin = g_PluginSys.PluginFromHandle(plHandle, &err);
   213 
   214 		if (!pPlugin)
   215 		{
   216 			return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", plHandle, err);
   217 		}
   218 	}
   219 
   220 	IPluginFunction *pFunction = pPlugin->GetBaseContext()->GetFunctionById(params[3]);
   221 
   222 	if (!pFunction)
   223 	{
   224 		return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
   225 	}
   226 
   227 	return pForward->AddFunction(pFunction);
   228 }
   229 
   230 static cell_t sm_RemoveFromForward(IPluginContext *pContext, const cell_t *params)
   231 {
   232 	Handle_t fwdHandle = static_cast<Handle_t>(params[1]);
   233 	Handle_t plHandle = static_cast<Handle_t>(params[2]);
   234 	HandleError err;
   235 	IChangeableForward *pForward;
   236 	IPlugin *pPlugin;
   237 
   238 	if ((err=g_HandleSys.ReadHandle(fwdHandle, g_PrivateFwdType, NULL, (void **)&pForward))
   239 		!= HandleError_None)
   240 	{
   241 		return pContext->ThrowNativeError("Invalid private forward handle %x (error %d)", fwdHandle, err);
   242 	}
   243 
   244 	if (plHandle == 0)
   245 	{
   246 		pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
   247 	} else {
   248 		pPlugin = g_PluginSys.PluginFromHandle(plHandle, &err);
   249 
   250 		if (!pPlugin)
   251 		{
   252 			return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", plHandle, err);
   253 		}
   254 	}
   255 
   256 	IPluginFunction *pFunction = pPlugin->GetBaseContext()->GetFunctionById(params[3]);
   257 
   258 	if (!pFunction)
   259 	{
   260 		return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
   261 	}
   262 
   263 	return pForward->RemoveFunction(pFunction);
   264 }
   265 
   266 static cell_t sm_RemoveAllFromForward(IPluginContext *pContext, const cell_t *params)
   267 {
   268 	Handle_t fwdHandle = static_cast<Handle_t>(params[1]);
   269 	Handle_t plHandle = static_cast<Handle_t>(params[2]);
   270 	HandleError err;
   271 	IChangeableForward *pForward;
   272 	IPlugin *pPlugin;
   273 
   274 	if ((err=g_HandleSys.ReadHandle(fwdHandle, g_PrivateFwdType, NULL, (void **)&pForward))
   275 		!= HandleError_None)
   276 	{
   277 		return pContext->ThrowNativeError("Invalid private forward handle %x (error %d)", fwdHandle, err);
   278 	}
   279 
   280 	if (plHandle == 0)
   281 	{
   282 		pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
   283 	} else {
   284 		pPlugin = g_PluginSys.PluginFromHandle(plHandle, &err);
   285 
   286 		if (!pPlugin)
   287 		{
   288 			return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", plHandle, err);
   289 		}
   290 	}
   291 
   292 	return pForward->RemoveFunctionsOfPlugin(pPlugin);
   293 }
   294 
   295 static cell_t sm_CallStartFunction(IPluginContext *pContext, const cell_t *params)
   296 {
   297 	Handle_t hndl;
   298 	HandleError err;
   299 	IPlugin *pPlugin;
   300 
   301 	if (s_CallStarted)
   302 	{
   303 		return pContext->ThrowNativeError("Cannot start a call while one is already in progress");
   304 	}
   305 
   306 	hndl = static_cast<Handle_t>(params[1]);
   307 
   308 	if (hndl == 0)
   309 	{
   310 		pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
   311 	} else {
   312 		pPlugin = g_PluginSys.PluginFromHandle(hndl, &err);
   313 
   314 		if (!pPlugin)
   315 		{
   316 			return pContext->ThrowNativeError("Plugin handle %x is invalid (error %d)", hndl, err);
   317 		}
   318 	}
   319 
   320 	s_pFunction = pPlugin->GetBaseContext()->GetFunctionById(params[2]);
   321 
   322 	if (!s_pFunction)
   323 	{
   324 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   325 	}
   326 
   327 	s_pCallable = static_cast<ICallable *>(s_pFunction);
   328 
   329 	s_CallStarted = true;
   330 
   331 	return 1;
   332 }
   333 
   334 static cell_t sm_CallStartForward(IPluginContext *pContext, const cell_t *params)
   335 {
   336 	Handle_t hndl;
   337 	HandleError err;
   338 	IForward *pForward;
   339 
   340 	if (s_CallStarted)
   341 	{
   342 		return pContext->ThrowNativeError("Cannot start a call while one is already in progress");
   343 	}
   344 
   345 	hndl = static_cast<Handle_t>(params[1]);
   346 
   347 	if ((err=g_HandleSys.ReadHandle(hndl, g_GlobalFwdType, NULL, (void **)&pForward))
   348 		!= HandleError_None)
   349 	{
   350 		return pContext->ThrowNativeError("Invalid forward handle %x (error %d)", hndl, err);
   351 	}
   352 
   353 	s_pForward = pForward;
   354 
   355 	s_pCallable = static_cast<ICallable *>(pForward);
   356 
   357 	s_CallStarted = true;
   358 
   359 	return 1;
   360 }
   361 
   362 static cell_t sm_CallPushCell(IPluginContext *pContext, const cell_t *params)
   363 {
   364 	int err;
   365 
   366 	if (!s_CallStarted)
   367 	{
   368 		return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress");
   369 	}
   370 
   371 	err = s_pCallable->PushCell(params[1]);
   372 
   373 	if (err)
   374 	{
   375 		s_pCallable->Cancel();
   376 		ResetCall();
   377 		return pContext->ThrowNativeErrorEx(err, NULL);
   378 	}
   379 
   380 	return 1;
   381 }
   382 
   383 static cell_t sm_CallPushCellRef(IPluginContext *pContext, const cell_t *params)
   384 {
   385 	int err;
   386 	cell_t *addr;
   387 
   388 	if (!s_CallStarted)
   389 	{
   390 		return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress");
   391 	}
   392 
   393 	pContext->LocalToPhysAddr(params[1], &addr);
   394 
   395 	err = s_pCallable->PushCellByRef(addr);
   396 
   397 	if (err)
   398 	{
   399 		s_pCallable->Cancel();
   400 		ResetCall();
   401 		return pContext->ThrowNativeErrorEx(err, NULL);
   402 	}
   403 
   404 	return 1;
   405 }
   406 
   407 static cell_t sm_CallPushFloat(IPluginContext *pContext, const cell_t *params)
   408 {
   409 	int err;
   410 
   411 	if (!s_CallStarted)
   412 	{
   413 		return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress");
   414 	}
   415 
   416 	err = s_pCallable->PushFloat(sp_ctof(params[1]));
   417 
   418 	if (err)
   419 	{
   420 		s_pCallable->Cancel();
   421 		ResetCall();
   422 		return pContext->ThrowNativeErrorEx(err, NULL);
   423 	}
   424 
   425 	return 1;
   426 }
   427 
   428 static cell_t sm_CallPushFloatRef(IPluginContext *pContext, const cell_t *params)
   429 {
   430 	int err;
   431 	cell_t *addr;
   432 
   433 	if (!s_CallStarted)
   434 	{
   435 		return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress");
   436 	}
   437 
   438 	pContext->LocalToPhysAddr(params[1], &addr);
   439 
   440 	err = s_pCallable->PushFloatByRef(reinterpret_cast<float *>(addr));
   441 
   442 	if (err)
   443 	{
   444 		s_pCallable->Cancel();
   445 		ResetCall();
   446 		return pContext->ThrowNativeErrorEx(err, NULL);
   447 	}
   448 
   449 	return 1;
   450 }
   451 
   452 static cell_t sm_CallPushArray(IPluginContext *pContext, const cell_t *params)
   453 {
   454 	int err;
   455 	cell_t *addr;
   456 
   457 	if (!s_CallStarted)
   458 	{
   459 		return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress");
   460 	}
   461 
   462 	pContext->LocalToPhysAddr(params[1], &addr);
   463 
   464 	err = s_pCallable->PushArray(addr, params[2]);
   465 
   466 	if (err)
   467 	{
   468 		s_pCallable->Cancel();
   469 		ResetCall();
   470 		return pContext->ThrowNativeErrorEx(err, NULL);
   471 	}
   472 
   473 	return 1;
   474 }
   475 
   476 static cell_t sm_CallPushArrayEx(IPluginContext *pContext, const cell_t *params)
   477 {
   478 	int err;
   479 	cell_t *addr;
   480 
   481 	if (!s_CallStarted)
   482 	{
   483 		return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress");
   484 	}
   485 
   486 	pContext->LocalToPhysAddr(params[1], &addr);
   487 
   488 	err = s_pCallable->PushArray(addr, params[2], params[3]);
   489 
   490 	if (err)
   491 	{
   492 		s_pCallable->Cancel();
   493 		ResetCall();
   494 		return pContext->ThrowNativeErrorEx(err, NULL);
   495 	}
   496 
   497 	return 1;
   498 }
   499 
   500 static cell_t sm_CallPushString(IPluginContext *pContext, const cell_t *params)
   501 {
   502 	int err;
   503 	char *value;
   504 
   505 	if (!s_CallStarted)
   506 	{
   507 		return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress");
   508 	}
   509 
   510 	pContext->LocalToString(params[1], &value);
   511 
   512 	err = s_pCallable->PushString(value);
   513 
   514 	if (err)
   515 	{
   516 		s_pCallable->Cancel();
   517 		ResetCall();
   518 		return pContext->ThrowNativeErrorEx(err, NULL);
   519 	}
   520 
   521 	return 1;
   522 }
   523 
   524 static cell_t sm_CallPushStringEx(IPluginContext *pContext, const cell_t *params)
   525 {
   526 	int err;
   527 	char *value;
   528 
   529 	if (!s_CallStarted)
   530 	{
   531 		return pContext->ThrowNativeError("Cannot push parameters when there is no call in progress");
   532 	}
   533 
   534 	pContext->LocalToString(params[1], &value);
   535 
   536 	err = s_pCallable->PushStringEx(value, params[2], params[3], params[4]);
   537 
   538 	if (err)
   539 	{
   540 		s_pCallable->Cancel();
   541 		ResetCall();
   542 		return pContext->ThrowNativeErrorEx(err, NULL);
   543 	}
   544 
   545 	return 1;
   546 }
   547 
   548 static cell_t sm_CallFinish(IPluginContext *pContext, const cell_t *params)
   549 {
   550 	int err = SP_ERROR_NOT_RUNNABLE;
   551 	cell_t *result;
   552 
   553 	if (!s_CallStarted)
   554 	{
   555 		return pContext->ThrowNativeError("Cannot finish call when there is no call in progress");
   556 	}
   557 
   558 	pContext->LocalToPhysAddr(params[1], &result);
   559 
   560 	if (s_pFunction)
   561 	{
   562 		IPluginFunction *pFunction = s_pFunction;
   563 		ResetCall();
   564 		err = pFunction->Execute(result);
   565 	} else if (s_pForward) {
   566 		IForward *pForward = s_pForward;
   567 		ResetCall();
   568 		err = pForward->Execute(result, NULL);
   569 	}
   570 
   571 	return err;
   572 }
   573 
   574 static cell_t sm_CallCancel(IPluginContext *pContext, const cell_t *params)
   575 {
   576 	if (!s_CallStarted)
   577 	{
   578 		return pContext->ThrowNativeError("Cannot cancel call when there is no call in progress");
   579 	}
   580 
   581 	s_pCallable->Cancel();
   582 	ResetCall();
   583 
   584 	return 1;
   585 }
   586 
   587 REGISTER_NATIVES(functionNatives)
   588 {
   589 	{"GetFunctionByName",		sm_GetFunctionByName},
   590 	{"CreateGlobalForward",		sm_CreateGlobalForward},
   591 	{"CreateForward",			sm_CreateForward},
   592 	{"GetForwardFunctionCount",	sm_GetForwardFunctionCount},
   593 	{"AddToForward",			sm_AddToForward},
   594 	{"RemoveFromForward",		sm_RemoveFromForward},
   595 	{"RemoveAllFromForward",	sm_RemoveAllFromForward},
   596 	{"Call_StartFunction",		sm_CallStartFunction},
   597 	{"Call_StartForward",		sm_CallStartForward},
   598 	{"Call_PushCell",			sm_CallPushCell},
   599 	{"Call_PushCellRef",		sm_CallPushCellRef},
   600 	{"Call_PushFloat",			sm_CallPushFloat},
   601 	{"Call_PushFloatRef",		sm_CallPushFloatRef},
   602 	{"Call_PushArray",			sm_CallPushArray},
   603 	{"Call_PushArrayEx",		sm_CallPushArrayEx},
   604 	{"Call_PushString",			sm_CallPushString},
   605 	{"Call_PushStringEx",		sm_CallPushStringEx},
   606 	{"Call_Finish",				sm_CallFinish},
   607 	{"Call_Cancel",				sm_CallCancel},
   608 	{NULL,						NULL},
   609 };