core/smn_console.cpp
author David Anderson <dvander@alliedmods.net>
Thu Mar 05 02:06:59 2009 -0500 (2009-03-05)
changeset 2653 0ae7d8ab1de7
parent 2648 dbcc58361ada
child 2684 a802f5cebe28
permissions -rw-r--r--
Removed -dev tag for release.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2008 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 "HalfLife2.h"
    34 #include "sourcemm_api.h"
    35 #include "HandleSys.h"
    36 #include "ConVarManager.h"
    37 #include "ConCmdManager.h"
    38 #include "PluginSys.h"
    39 #include "sm_stringutil.h"
    40 #include "PlayerManager.h"
    41 #include "ChatTriggers.h"
    42 #include "AdminCache.h"
    43 #include <inetchannel.h>
    44 #include <bitbuf.h>
    45 #include <sm_trie_tpl.h>
    46 #include "TagsSystem.h"
    47 
    48 #if SOURCE_ENGINE == SE_LEFT4DEAD
    49 #define NET_SETCONVAR	6
    50 #else
    51 #define NET_SETCONVAR	5
    52 #endif
    53 
    54 enum ConVarBounds
    55 {
    56 	ConVarBound_Upper = 0,
    57 	ConVarBound_Lower
    58 };
    59 
    60 HandleType_t hCmdIterType = 0;
    61 HandleType_t htConCmdIter = 0;
    62 
    63 struct GlobCmdIter
    64 {
    65 	bool started;
    66 	List<ConCmdInfo *>::iterator iter;
    67 };
    68 
    69 struct ConCmdIter
    70 {
    71 #if SOURCE_ENGINE == SE_LEFT4DEAD
    72 	ICvarIteratorInternal *pLast;
    73 #else
    74 	const ConCommandBase *pLast;
    75 #endif
    76 };
    77 
    78 class ConsoleHelpers : 
    79 	public SMGlobalClass,
    80 	public IHandleTypeDispatch
    81 {
    82 public:
    83 	virtual void OnSourceModAllInitialized()
    84 	{
    85 		HandleAccess access;
    86 
    87 		g_HandleSys.InitAccessDefaults(NULL, &access);
    88 
    89 		htConCmdIter = g_HandleSys.CreateType("ConCmdIter", this, 0, NULL, &access, g_pCoreIdent, NULL);
    90 
    91 		access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER | HANDLE_RESTRICT_IDENTITY;
    92 
    93 		hCmdIterType = g_HandleSys.CreateType("CmdIter", this, 0, NULL, &access, g_pCoreIdent, NULL);
    94 	}
    95 	virtual void OnHandleDestroy(HandleType_t type, void *object)
    96 	{
    97 		if (type == hCmdIterType)
    98 		{
    99 			GlobCmdIter *iter = (GlobCmdIter *)object;
   100 			delete iter;
   101 		}
   102 		else if (type == htConCmdIter)
   103 		{
   104 			ConCmdIter *iter = (ConCmdIter * )object;
   105 #if SOURCE_ENGINE == SE_LEFT4DEAD
   106 			// ICvarIteratorInternal has no virtual destructor
   107 			g_pMemAlloc->Free(iter->pLast);
   108 #endif
   109 			delete iter;
   110 		}
   111 	}
   112 	virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
   113 	{
   114 		if (type == htConCmdIter)
   115 		{
   116 			*pSize = sizeof(ConCmdIter);
   117 			return true;
   118 		}
   119 		else if (type == hCmdIterType)
   120 		{
   121 			*pSize = sizeof(GlobCmdIter);
   122 			return true;
   123 		}
   124 		return false;
   125 	}
   126 } s_ConsoleHelpers;
   127 
   128 class CommandFlagsHelper : public IConCommandTracker
   129 {
   130 public:
   131 	void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
   132 	{
   133 		m_CmdFlags.remove(name);
   134 	}
   135 	bool GetFlags(const char *name, int *flags)
   136 	{
   137 		ConCommandBase **ppCmd;
   138 		ConCommandBase *pCmd;
   139 		if ((ppCmd=m_CmdFlags.retrieve(name)))
   140 		{
   141 			TrackConCommandBase((*ppCmd), this);
   142 			*flags = (*ppCmd)->GetFlags();
   143 			return true;
   144 		}
   145 		else if ((pCmd=FindCommandBase(name)))
   146 		{
   147 			m_CmdFlags.insert(name, pCmd);
   148 			TrackConCommandBase(pCmd, this);
   149 			*flags = pCmd->GetFlags();
   150 			return true;
   151 		}
   152 		else
   153 		{
   154 			return false;
   155 		}
   156 	}
   157 	bool SetFlags(const char *name, int flags)
   158 	{
   159 		ConCommandBase **ppCmd;
   160 		ConCommandBase *pCmd;
   161 		if ((ppCmd=m_CmdFlags.retrieve(name)))
   162 		{
   163 			(*ppCmd)->SetFlags(flags);
   164 			TrackConCommandBase((*ppCmd), this);
   165 			return true;
   166 		}
   167 		else if ((pCmd=FindCommandBase(name)))
   168 		{
   169 			m_CmdFlags.insert(name, pCmd);
   170 			pCmd->SetFlags(flags);
   171 			TrackConCommandBase(pCmd, this);
   172 			return true;
   173 		}
   174 		else
   175 		{
   176 			return false;
   177 		}
   178 	}
   179 private:
   180 	KTrie<ConCommandBase *> m_CmdFlags;
   181 } s_CommandFlagsHelper;
   182 
   183 #if SOURCE_ENGINE >= SE_ORANGEBOX
   184 static void ReplicateConVar(ConVar *pConVar)
   185 {
   186 	int maxClients = g_Players.GetMaxClients();
   187 
   188 	char data[256];
   189 	bf_write buffer(data, sizeof(data));
   190 
   191 	buffer.WriteUBitLong(NET_SETCONVAR, 5);
   192 	buffer.WriteByte(1);
   193 	buffer.WriteString(pConVar->GetName());
   194 	buffer.WriteString(pConVar->GetString());
   195 
   196 	for (int i = 1; i <= maxClients; i++)
   197 	{
   198 		CPlayer *pPlayer = g_Players.GetPlayerByIndex(i);
   199 
   200 		if (pPlayer && pPlayer->IsInGame() && !pPlayer->IsFakeClient())
   201 		{
   202 			INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(i));
   203 			netchan->SendData(buffer);
   204 		}
   205 	}
   206 }
   207 
   208 static void NotifyConVar(ConVar *pConVar)
   209 {
   210 	IGameEvent *pEvent = gameevents->CreateEvent("server_cvar");
   211 	pEvent->SetString("cvarname", pConVar->GetName());
   212 	if (IsFlagSet(pConVar, FCVAR_PROTECTED))
   213 	{
   214 		pEvent->SetString("cvarvalue", "***PROTECTED***");
   215 	}
   216 	else
   217 	{
   218 		pEvent->SetString("cvarvalue", pConVar->GetString());
   219 	}
   220 
   221 	gameevents->FireEvent(pEvent);
   222 }
   223 #endif
   224 
   225 static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
   226 {
   227 	char *name, *defaultVal, *helpText;
   228 
   229 	pContext->LocalToString(params[1], &name);
   230 
   231 	// While the engine seems to accept a blank convar name, it causes a crash upon server quit
   232 	if (name == NULL || strcmp(name, "") == 0)
   233 	{
   234 		return pContext->ThrowNativeError("Convar with blank name is not permitted");
   235 	}
   236 
   237 	pContext->LocalToString(params[2], &defaultVal);
   238 	pContext->LocalToString(params[3], &helpText);
   239 
   240 	bool hasMin = params[5] ? true : false;
   241 	bool hasMax = params[7] ? true : false;
   242 	float min = sp_ctof(params[6]);
   243 	float max = sp_ctof(params[8]);
   244 
   245 	Handle_t hndl = g_ConVarManager.CreateConVar(pContext, name, defaultVal, helpText, params[4], hasMin, min, hasMax, max);
   246 
   247 	if (hndl == BAD_HANDLE)
   248 	{
   249 		return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same might already exist.", name);
   250 	}
   251 
   252 	return hndl;
   253 }
   254 
   255 static cell_t sm_FindConVar(IPluginContext *pContext, const cell_t *params)
   256 {
   257 	char *name;
   258 
   259 	pContext->LocalToString(params[1], &name);
   260 
   261 	return g_ConVarManager.FindConVar(name);
   262 }
   263 
   264 static cell_t sm_HookConVarChange(IPluginContext *pContext, const cell_t *params)
   265 {
   266 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   267 	HandleError err;
   268 	ConVar *pConVar;
   269 
   270 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   271 		!= HandleError_None)
   272 	{
   273 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   274 	}
   275 
   276 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
   277 
   278 	if (!pFunction)
   279 	{
   280 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   281 	}
   282 
   283 	g_ConVarManager.HookConVarChange(pConVar, pFunction);
   284 
   285 	return 1;
   286 }
   287 
   288 static cell_t sm_UnhookConVarChange(IPluginContext *pContext, const cell_t *params)
   289 {
   290 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   291 	HandleError err;
   292 	ConVar *pConVar;
   293 
   294 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   295 		!= HandleError_None)
   296 	{
   297 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   298 	}
   299 
   300 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
   301 
   302 	if (!pFunction)
   303 	{
   304 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   305 	}
   306 
   307 	g_ConVarManager.UnhookConVarChange(pConVar, pFunction);
   308 
   309 	return 1;
   310 }
   311 
   312 static cell_t sm_GetConVarBool(IPluginContext *pContext, const cell_t *params)
   313 {
   314 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   315 	HandleError err;
   316 	ConVar *pConVar;
   317 
   318 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   319 		!= HandleError_None)
   320 	{
   321 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   322 	}
   323 
   324 	return pConVar->GetBool();
   325 }
   326 
   327 static cell_t sm_GetConVarInt(IPluginContext *pContext, const cell_t *params)
   328 {
   329 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   330 	HandleError err;
   331 	ConVar *pConVar;
   332 
   333 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   334 		!= HandleError_None)
   335 	{
   336 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   337 	}
   338 
   339 	return pConVar->GetInt();
   340 }
   341 
   342 /* This handles both SetConVarBool() and SetConVarInt() */
   343 static cell_t sm_SetConVarNum(IPluginContext *pContext, const cell_t *params)
   344 {
   345 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   346 	HandleError err;
   347 	ConVar *pConVar;
   348 
   349 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   350 		!= HandleError_None)
   351 	{
   352 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   353 	}
   354 
   355 	pConVar->SetValue(params[2]);
   356 
   357 #if SOURCE_ENGINE >= SE_ORANGEBOX
   358 	/* Should we replicate it? */
   359 	if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
   360 	{
   361 		ReplicateConVar(pConVar);
   362 	}
   363 
   364 	/* Should we notify clients? */
   365 	if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
   366 	{
   367 		NotifyConVar(pConVar);
   368 	}
   369 #endif
   370 
   371 	return 1;
   372 }
   373 
   374 static cell_t sm_GetConVarFloat(IPluginContext *pContext, const cell_t *params)
   375 {
   376 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   377 	HandleError err;
   378 	ConVar *pConVar;
   379 
   380 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   381 		!= HandleError_None)
   382 	{
   383 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   384 	}
   385 	
   386 	float value = pConVar->GetFloat();
   387 
   388 	return sp_ftoc(value);
   389 }
   390 
   391 static cell_t sm_SetConVarFloat(IPluginContext *pContext, const cell_t *params)
   392 {
   393 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   394 	HandleError err;
   395 	ConVar *pConVar;
   396 
   397 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   398 		!= HandleError_None)
   399 	{
   400 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   401 	}
   402 
   403 	float value = sp_ctof(params[2]);
   404 	pConVar->SetValue(value);
   405 
   406 #if SOURCE_ENGINE >= SE_ORANGEBOX
   407 	/* Should we replicate it? */
   408 	if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
   409 	{
   410 		ReplicateConVar(pConVar);
   411 	}
   412 
   413 	/* Should we notify clients? */
   414 	if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
   415 	{
   416 		NotifyConVar(pConVar);
   417 	}
   418 #endif
   419 
   420 	return 1;
   421 }
   422 
   423 static cell_t sm_GetConVarString(IPluginContext *pContext, const cell_t *params)
   424 {
   425 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   426 	HandleError err;
   427 	ConVar *pConVar;
   428 
   429 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   430 		!= HandleError_None)
   431 	{
   432 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   433 	}
   434 
   435 	pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetString(), NULL);
   436 
   437 	return 1;
   438 }
   439 
   440 static cell_t sm_SetConVarString(IPluginContext *pContext, const cell_t *params)
   441 {
   442 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   443 	HandleError err;
   444 	ConVar *pConVar;
   445 
   446 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   447 		!= HandleError_None)
   448 	{
   449 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   450 	}
   451 
   452 	char *value;
   453 	pContext->LocalToString(params[2], &value);
   454 
   455 	pConVar->SetValue(value);
   456 
   457 #if SOURCE_ENGINE >= SE_ORANGEBOX
   458 	/* Should we replicate it? */
   459 	if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
   460 	{
   461 		ReplicateConVar(pConVar);
   462 	}
   463 
   464 	/* Should we notify clients? */
   465 	if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
   466 	{
   467 		NotifyConVar(pConVar);
   468 	}
   469 #endif
   470 
   471 	return 1;
   472 }
   473 
   474 static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
   475 {
   476 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   477 	HandleError err;
   478 	ConVar *pConVar;
   479 
   480 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   481 		!= HandleError_None)
   482 	{
   483 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   484 	}
   485 
   486 	pConVar->Revert();
   487 	
   488 #if SOURCE_ENGINE >= SE_ORANGEBOX
   489 	/* Should we replicate it? */
   490 	if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
   491 	{
   492 		ReplicateConVar(pConVar);
   493 	}
   494 
   495 	/* Should we notify clients? */
   496 	if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
   497 	{
   498 		NotifyConVar(pConVar);
   499 	}
   500 #endif
   501 
   502 	return 1;
   503 }
   504 
   505 static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params)
   506 {
   507 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   508 	HandleError err;
   509 	ConVar *pConVar;
   510 
   511 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   512 		!= HandleError_None)
   513 	{
   514 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   515 	}
   516 
   517 	return pConVar->GetFlags();
   518 }
   519 
   520 static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params)
   521 {
   522 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   523 	HandleError err;
   524 	ConVar *pConVar;
   525 
   526 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   527 		!= HandleError_None)
   528 	{
   529 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   530 	}
   531 
   532 	pConVar->SetFlags(params[2]);
   533 
   534 	return 1;
   535 }
   536 
   537 static cell_t sm_GetConVarBounds(IPluginContext *pContext, const cell_t *params)
   538 {
   539 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   540 	HandleError err;
   541 	ConVar *pConVar;
   542 
   543 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   544 		!= HandleError_None)
   545 	{
   546 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   547 	}
   548 
   549 	cell_t *addr;
   550 	bool hasBound;
   551 	float bound;
   552 
   553 	switch (params[2])
   554 	{
   555 	case ConVarBound_Upper:
   556 		hasBound = pConVar->GetMax(bound);
   557 		break;
   558 	case ConVarBound_Lower:
   559 		hasBound = pConVar->GetMin(bound);
   560 		break;
   561 	default:
   562 		return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
   563 	}
   564 	
   565 	pContext->LocalToPhysAddr(params[3], &addr);
   566 	*addr = sp_ftoc(bound);
   567 
   568 	return hasBound;
   569 }
   570 
   571 static cell_t sm_SetConVarBounds(IPluginContext *pContext, const cell_t *params)
   572 {
   573 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   574 	HandleError err;
   575 	ConVar *pConVar;
   576 
   577 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   578 		!= HandleError_None)
   579 	{
   580 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   581 	}
   582 
   583 	switch (params[2])
   584 	{
   585 	case ConVarBound_Upper:
   586 		pConVar->SetMax(params[3] ? true : false, sp_ctof(params[4]));
   587 		break;
   588 	case ConVarBound_Lower:
   589 		pConVar->SetMin(params[3] ? true : false, sp_ctof(params[4]));
   590 		break;
   591 	default:
   592 		return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
   593 	}
   594 
   595 	return 1;
   596 }
   597 
   598 static cell_t sm_GetConVarName(IPluginContext *pContext, const cell_t *params)
   599 {
   600 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   601 	HandleError err;
   602 	ConVar *pConVar;
   603 
   604 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   605 		!= HandleError_None)
   606 	{
   607 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   608 	}
   609 
   610 	pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetName(), NULL);
   611 
   612 	return 1;
   613 }
   614 
   615 static bool s_QueryAlreadyWarned = false;
   616 
   617 static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params)
   618 {
   619 	CPlayer *pPlayer;
   620 	char *name;
   621 	IPluginFunction *pCallback;
   622 
   623 	if (!g_ConVarManager.IsQueryingSupported())
   624 	{
   625 		if (!s_QueryAlreadyWarned)
   626 		{
   627 			s_QueryAlreadyWarned = true;
   628 			return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)");
   629 		}
   630 
   631 		return 0;
   632 	}
   633 
   634 	pPlayer = g_Players.GetPlayerByIndex(params[1]);
   635 
   636 	if (!pPlayer)
   637 	{
   638 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   639 	}
   640 
   641 	if (!pPlayer->IsConnected())
   642 	{
   643 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   644 	}
   645 
   646 	/* Trying a query on a bot results in callback not be fired, so don't bother */
   647 	if (pPlayer->IsFakeClient())
   648 	{
   649 		return 0;
   650 	}
   651 
   652 	pContext->LocalToString(params[2], &name);
   653 	pCallback = pContext->GetFunctionById(params[3]);
   654 
   655 	if (!pCallback)
   656 	{
   657 		return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
   658 	}
   659 
   660 	return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]);
   661 }
   662 
   663 static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
   664 {
   665 	char *name,*help;
   666 	IPluginFunction *pFunction;
   667 
   668 	pContext->LocalToString(params[1], &name);
   669 	pContext->LocalToString(params[3], &help);
   670 	pFunction = pContext->GetFunctionById(params[2]);
   671 
   672 	if (!pFunction)
   673 	{
   674 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   675 	}
   676 
   677 	if (!g_ConCmds.AddServerCommand(pFunction, name, help, params[4]))
   678 	{
   679 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   680 	}
   681 
   682 	return 1;
   683 }
   684 
   685 static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params)
   686 {
   687 	char *name,*help;
   688 	IPluginFunction *pFunction;
   689 
   690 	pContext->LocalToString(params[1], &name);
   691 	pContext->LocalToString(params[3], &help);
   692 	pFunction = pContext->GetFunctionById(params[2]);
   693 
   694 	if (!pFunction)
   695 	{
   696 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   697 	}
   698 
   699 	if (!g_ConCmds.AddConsoleCommand(pFunction, name, help, params[4]))
   700 	{
   701 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   702 	}
   703 
   704 	return 1;
   705 }
   706 
   707 static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
   708 {
   709 	char *name,*help;
   710 	const char *group;
   711 	IPluginFunction *pFunction;
   712 	FlagBits flags = params[3];
   713 	int cmdflags = params[6];
   714 
   715 	pContext->LocalToString(params[1], &name);
   716 	pContext->LocalToString(params[4], &help);
   717 	pContext->LocalToString(params[5], (char **)&group);
   718 	pFunction = pContext->GetFunctionById(params[2]);
   719 
   720 	if (group[0] == '\0')
   721 	{
   722 		CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
   723 		group = pPlugin->GetFilename();
   724 	}
   725 
   726 	if (!pFunction)
   727 	{
   728 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   729 	}
   730 
   731 	if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags))
   732 	{
   733 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   734 	}
   735 
   736 	return 1;
   737 }
   738 
   739 static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
   740 {
   741 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   742 
   743 	if (!pCmd)
   744 	{
   745 		return pContext->ThrowNativeError("No command callback available");
   746 	}
   747 
   748 	return pCmd->ArgC() - 1;
   749 }
   750 
   751 static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params)
   752 {
   753 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   754 
   755 	if (!pCmd)
   756 	{
   757 		return pContext->ThrowNativeError("No command callback available");
   758 	}
   759 
   760 	size_t length;
   761 
   762 	const char *arg = pCmd->Arg(params[1]);
   763 	
   764 	pContext->StringToLocalUTF8(params[2], params[3], arg ? arg : "", &length);
   765 
   766 	return (cell_t)length;
   767 }
   768 
   769 static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params)
   770 {
   771 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   772 
   773 	if (!pCmd)
   774 	{
   775 		return pContext->ThrowNativeError("No command callback available");
   776 	}
   777 
   778 	const char *args = pCmd->ArgS();
   779 	size_t length;
   780 
   781 	if (!args)
   782 	{
   783 		args = "";
   784 	}
   785 
   786 	pContext->StringToLocalUTF8(params[1], params[2], args, &length);
   787 
   788 	return (cell_t)length;
   789 }
   790 
   791 static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params)
   792 {
   793 	char buffer[1024];
   794 	char *fmt;
   795 	int arg = 2;
   796 
   797 	pCtx->LocalToString(params[1], &fmt);
   798 	size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
   799 
   800 	buffer[res++] = '\n';
   801 	buffer[res] = '\0';
   802 
   803 	META_CONPRINT(buffer);
   804 
   805 	return 1;
   806 }
   807 
   808 static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
   809 {
   810 	int index = params[1];
   811 	if ((index < 0) || (index > g_Players.GetMaxClients()))
   812 	{
   813 		return pCtx->ThrowNativeError("Client index %d is invalid", index);
   814 	}
   815 
   816 	CPlayer *pPlayer = NULL;
   817 	if (index != 0)
   818 	{
   819 		pPlayer = g_Players.GetPlayerByIndex(index);
   820 		if (!pPlayer->IsInGame())
   821 		{
   822 			return pCtx->ThrowNativeError("Client %d is not in game", index);
   823 		}
   824 		
   825 		/* Silent fail on bots, engine will crash */
   826 		if (pPlayer->IsFakeClient())
   827 		{
   828 			return 0;
   829 		}
   830 	}
   831 
   832 	char buffer[1024];
   833 	char *fmt;
   834 	int arg = 3;
   835 
   836 	pCtx->LocalToString(params[2], &fmt);
   837 	size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
   838 
   839 	buffer[res++] = '\n';
   840 	buffer[res] = '\0';
   841 
   842 	if (index != 0)
   843 	{
   844 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
   845 	} else {
   846 		META_CONPRINT(buffer);
   847 	}
   848 
   849 	return 1;
   850 }
   851 
   852 static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
   853 {
   854 	g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   855 
   856 	char buffer[1024];
   857 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
   858 
   859 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   860 	{
   861 		return 0;
   862 	}
   863 
   864 	/* One byte for null terminator, one for newline */
   865 	buffer[len++] = '\n';
   866 	buffer[len] = '\0';
   867 
   868 	engine->ServerCommand(buffer);
   869 
   870 	return 1;
   871 }
   872 
   873 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
   874 {
   875 	g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   876 
   877 	char buffer[1024];
   878 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
   879 
   880 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   881 	{
   882 		return 0;
   883 	}
   884 
   885 	/* One byte for null terminator, one for newline */
   886 	buffer[len++] = '\n';
   887 	buffer[len] = '\0';
   888 
   889 	InsertServerCommand(buffer);
   890 
   891 	return 1;
   892 }
   893 
   894 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
   895 {
   896 	engine->ServerExecute();
   897 
   898 	return 1;
   899 }
   900 
   901 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
   902 {
   903 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   904 
   905 	if (!pPlayer)
   906 	{
   907 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   908 	}
   909 
   910 	if (!pPlayer->IsConnected())
   911 	{
   912 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   913 	}
   914 
   915 	g_SourceMod.SetGlobalTarget(params[1]);
   916 
   917 	char buffer[256];
   918 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   919 
   920 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   921 	{
   922 		return 0;
   923 	}
   924 
   925 	engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
   926 
   927 	return 1;
   928 }
   929 
   930 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
   931 {
   932 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   933 
   934 	if (!pPlayer)
   935 	{
   936 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   937 	}
   938 
   939 	if (!pPlayer->IsConnected())
   940 	{
   941 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   942 	}
   943 
   944 	char buffer[256];
   945 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   946 
   947 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   948 	{
   949 		return 0;
   950 	}
   951 
   952 	serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
   953 
   954 	return 1;
   955 }
   956 
   957 static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params)
   958 {
   959 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   960 
   961 	if (!pPlayer)
   962 	{
   963 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   964 	}
   965 
   966 	if (!pPlayer->IsConnected())
   967 	{
   968 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   969 	}
   970 
   971 	char buffer[256];
   972 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   973 
   974 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   975 	{
   976 		return 0;
   977 	}
   978 
   979 	g_HL2.AddToFakeCliCmdQueue(params[1], engine->GetPlayerUserId(pPlayer->GetEdict()), buffer);
   980 
   981 	return 1;
   982 }
   983 
   984 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
   985 {
   986 	g_SourceMod.SetGlobalTarget(params[1]);
   987 
   988 	/* Build the format string */
   989 	char buffer[1024];
   990 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
   991 
   992 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   993 	{
   994 		return 0;
   995 	}
   996 
   997 	/* If we're printing to the server, shortcut out */
   998 	if (params[1] == 0)
   999 	{
  1000 		/* Print */
  1001 		buffer[len++] = '\n';
  1002 		buffer[len] = '\0';
  1003 		META_CONPRINT(buffer);
  1004 		return 1;
  1005 	}
  1006 
  1007 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1008 
  1009 	if (!pPlayer)
  1010 	{
  1011 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1012 	}
  1013 
  1014 	if (!pPlayer->IsConnected())
  1015 	{
  1016 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1017 	}
  1018 
  1019 	g_SourceMod.SetGlobalTarget(params[1]);
  1020 
  1021 	unsigned int replyto = g_ChatTriggers.GetReplyTo();
  1022 	if (replyto == SM_REPLY_CONSOLE)
  1023 	{
  1024 		buffer[len++] = '\n';
  1025 		buffer[len] = '\0';
  1026 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
  1027 	} else if (replyto == SM_REPLY_CHAT) {
  1028 		if (len >= 191)
  1029 		{
  1030 			len = 191;
  1031 		}
  1032 		buffer[len] = '\0';
  1033 		g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
  1034 	}
  1035 
  1036 	return 1;
  1037 }
  1038 
  1039 static cell_t GetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
  1040 {
  1041 	return g_ChatTriggers.GetReplyTo();
  1042 }
  1043 
  1044 static cell_t SetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
  1045 {
  1046 	return g_ChatTriggers.SetReplyTo(params[1]);
  1047 }
  1048 
  1049 static cell_t GetCommandIterator(IPluginContext *pContext, const cell_t *params)
  1050 {
  1051 	GlobCmdIter *iter = new GlobCmdIter;
  1052 	iter->started = false;
  1053 
  1054 	Handle_t hndl = g_HandleSys.CreateHandle(hCmdIterType, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
  1055 	if (hndl == BAD_HANDLE)
  1056 	{
  1057 		delete iter;
  1058 	}
  1059 
  1060 	return hndl;
  1061 }
  1062 
  1063 static cell_t ReadCommandIterator(IPluginContext *pContext, const cell_t *params)
  1064 {
  1065 	GlobCmdIter *iter;
  1066 	HandleError err;
  1067 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
  1068 
  1069 	if ((err = g_HandleSys.ReadHandle(params[1], hCmdIterType, &sec, (void **)&iter))
  1070 		!= HandleError_None)
  1071 	{
  1072 		return pContext->ThrowNativeError("Invalid GlobCmdIter Handle %x", params[1]);
  1073 	}
  1074 
  1075 	const List<ConCmdInfo *> &cmds = g_ConCmds.GetCommandList();
  1076 
  1077 	if (!iter->started)
  1078 	{
  1079 		iter->iter = cmds.begin();
  1080 		iter->started = true;
  1081 	}
  1082 
  1083 	while (iter->iter != cmds.end()
  1084 			&& !(*(iter->iter))->sourceMod)
  1085 	{
  1086 		iter->iter++;
  1087 	}
  1088 
  1089 	if (iter->iter == cmds.end())
  1090 	{
  1091 		return 0;
  1092 	}
  1093 
  1094 	ConCmdInfo *pInfo = (*(iter->iter));
  1095 
  1096 	pContext->StringToLocalUTF8(params[2], params[3], pInfo->pCmd->GetName(), NULL);
  1097 	pContext->StringToLocalUTF8(params[5], params[6], pInfo->pCmd->GetHelpText(), NULL);
  1098 	
  1099 	cell_t *addr;
  1100 	pContext->LocalToPhysAddr(params[4], &addr);
  1101 	*addr = pInfo->admin.eflags;
  1102 
  1103 	iter->iter++;
  1104 
  1105 	return 1;
  1106 }
  1107 
  1108 static cell_t CheckCommandAccess(IPluginContext *pContext, const cell_t *params)
  1109 {
  1110 	if (params[1] == 0)
  1111 	{
  1112 		return 1;
  1113 	}
  1114 
  1115 	char *cmd;
  1116 	pContext->LocalToString(params[2], &cmd);
  1117 
  1118 	/* Auto-detect a command if we can */
  1119 	FlagBits bits = params[3];
  1120 	bool found_command = false;
  1121 	if (params[0] < 4 || !params[4])
  1122 	{
  1123 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1124 	}
  1125 	
  1126 	if (!found_command)
  1127 	{
  1128 		g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
  1129 	}
  1130 
  1131 	return g_ConCmds.CheckCommandAccess(params[1], cmd, bits) ? 1 : 0;
  1132 }
  1133 
  1134 static cell_t IsChatTrigger(IPluginContext *pContext, const cell_t *params)
  1135 {
  1136 	return g_ChatTriggers.IsChatTrigger() ? 1 : 0;
  1137 }
  1138 
  1139 static cell_t SetCommandFlags(IPluginContext *pContext, const cell_t *params)
  1140 {
  1141 	char *name;
  1142 	pContext->LocalToString(params[1], &name);
  1143 
  1144 	return (s_CommandFlagsHelper.SetFlags(name, params[2])) ? 1 : 0;
  1145 }
  1146 
  1147 static cell_t GetCommandFlags(IPluginContext *pContext, const cell_t *params)
  1148 {
  1149 	char *name;
  1150 	int flags;
  1151 	pContext->LocalToString(params[1], &name);
  1152 
  1153 	return (s_CommandFlagsHelper.GetFlags(name, &flags)) ? flags : -1;
  1154 }
  1155 
  1156 static cell_t FindFirstConCommand(IPluginContext *pContext, const cell_t *params)
  1157 {
  1158 	Handle_t hndl;
  1159 	ConCmdIter *pIter;
  1160 	cell_t *pIsCmd, *pFlags;
  1161 	const ConCommandBase *pConCmd;
  1162 	const char *desc;
  1163 
  1164 	pContext->LocalToPhysAddr(params[3], &pIsCmd);
  1165 	pContext->LocalToPhysAddr(params[4], &pFlags);
  1166 
  1167 #if SOURCE_ENGINE == SE_LEFT4DEAD
  1168 	ICvarIteratorInternal *cvarIter = icvar->FactoryInternalIterator();
  1169 	cvarIter->SetFirst();
  1170 	if (!cvarIter->IsValid())
  1171 	{
  1172 		return BAD_HANDLE;
  1173 	}
  1174 	pConCmd = cvarIter->Get();
  1175 #else
  1176 	pConCmd = icvar->GetCommands();
  1177 	if (pConCmd == NULL)
  1178 	{
  1179 		return BAD_HANDLE;
  1180 	}
  1181 #endif
  1182 
  1183 	pContext->StringToLocalUTF8(params[1], params[2], pConCmd->GetName(), NULL);
  1184 	*pIsCmd = pConCmd->IsCommand() ? 1 : 0;
  1185 	*pFlags = pConCmd->GetFlags();
  1186 
  1187 	if (params[6])
  1188 	{
  1189 		desc = pConCmd->GetHelpText();
  1190 		pContext->StringToLocalUTF8(params[5], params[6], (desc && desc[0]) ? desc : "", NULL);
  1191 	}
  1192 
  1193 	pIter = new ConCmdIter;
  1194 #if SOURCE_ENGINE == SE_LEFT4DEAD
  1195 	pIter->pLast = cvarIter;
  1196 #else
  1197 	pIter->pLast = pConCmd;
  1198 #endif
  1199 
  1200 	if ((hndl = g_HandleSys.CreateHandle(htConCmdIter, pIter, pContext->GetIdentity(), g_pCoreIdent, NULL))
  1201 		== BAD_HANDLE)
  1202 	{
  1203 		delete pIter;
  1204 		return BAD_HANDLE;
  1205 	}
  1206 
  1207 	return hndl;
  1208 }
  1209 
  1210 static cell_t FindNextConCommand(IPluginContext *pContext, const cell_t *params)
  1211 {
  1212 	HandleError err;
  1213 	ConCmdIter *pIter;
  1214 	cell_t *pIsCmd, *pFlags;
  1215 	const char *desc;
  1216 	const ConCommandBase *pConCmd;
  1217 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
  1218 
  1219 	if ((err = g_HandleSys.ReadHandle(params[1], htConCmdIter, &sec, (void **)&pIter)) != HandleError_None)
  1220 	{
  1221 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
  1222 	}
  1223 
  1224 	if (pIter->pLast == NULL)
  1225 	{
  1226 		return 0;
  1227 	}
  1228 
  1229 #if SOURCE_ENGINE == SE_LEFT4DEAD
  1230 	ICvarIteratorInternal *cvarIter = pIter->pLast;
  1231 	cvarIter->Next();
  1232 	if (!cvarIter->IsValid())
  1233 	{
  1234 		return 0;
  1235 	}
  1236 	pConCmd = cvarIter->Get();
  1237 #else
  1238 	pConCmd = pIter->pLast->GetNext();
  1239 	if (pConCmd == NULL)
  1240 	{
  1241 		return 0;
  1242 	}
  1243 	pIter->pLast = pConCmd;
  1244 #endif
  1245 
  1246 	pContext->LocalToPhysAddr(params[4], &pIsCmd);
  1247 	pContext->LocalToPhysAddr(params[5], &pFlags);
  1248 
  1249 	pContext->StringToLocalUTF8(params[2], params[3], pConCmd->GetName(), NULL);
  1250 	*pIsCmd = pConCmd->IsCommand() ? 1 : 0;
  1251 	*pFlags = pConCmd->GetFlags();
  1252 
  1253 	if (params[7])
  1254 	{
  1255 		desc = pConCmd->GetHelpText();
  1256 		pContext->StringToLocalUTF8(params[6], params[7], (desc && desc[0]) ? desc : "", NULL);
  1257 	}
  1258 
  1259 	return 1;
  1260 }
  1261 
  1262 static cell_t SendConVarValue(IPluginContext *pContext, const cell_t *params)
  1263 {
  1264 	Handle_t hndl = static_cast<Handle_t>(params[2]);
  1265 	HandleError err;
  1266 	ConVar *pConVar;
  1267 
  1268 	char *value;
  1269 	pContext->LocalToString(params[3], &value);
  1270 
  1271 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
  1272 		!= HandleError_None)
  1273 	{
  1274 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
  1275 	}
  1276 
  1277 	char data[256];
  1278 	bf_write buffer(data, sizeof(data));
  1279 
  1280 	buffer.WriteUBitLong(NET_SETCONVAR, 5);
  1281 	buffer.WriteByte(1);
  1282 	buffer.WriteString(pConVar->GetName());
  1283 	buffer.WriteString(value);
  1284 
  1285 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1286 
  1287 	if (!pPlayer)
  1288 	{
  1289 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1290 	}
  1291 
  1292 	if (!pPlayer->IsConnected())
  1293 	{
  1294 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1295 	}
  1296 
  1297 	if (pPlayer->IsFakeClient())
  1298 	{
  1299 		return pContext->ThrowNativeError("Client %d is fake and cannot be targeted", params[1]);
  1300 	}
  1301 
  1302 	INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(params[1]));
  1303 	netchan->SendData(buffer);
  1304 
  1305 	return 1;
  1306 }
  1307 
  1308 static cell_t AddServerTag(IPluginContext *pContext, const cell_t *params)
  1309 {
  1310 	char *value;
  1311 	pContext->LocalToString(params[1], &value);
  1312 
  1313 	if (!g_Tags.AddTag(value))
  1314 	{
  1315 		return 0;
  1316 	}
  1317 
  1318 	IPlugin *pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
  1319 	SourceHook::List<char *> *pList = NULL;
  1320 
  1321 	if (!pPlugin->GetProperty("ServerTags", (void **)&pList, false) || !pList)
  1322 	{
  1323 		pList = new SourceHook::List<char *>;
  1324 		pPlugin->SetProperty("ServerTags", pList);
  1325 	}
  1326 
  1327 	pList->push_back(strdup(value));
  1328 
  1329 	return 1;
  1330 }
  1331 
  1332 static cell_t RemoveServerTag(IPluginContext *pContext, const cell_t *params)
  1333 {
  1334 	char *value;
  1335 	pContext->LocalToString(params[1], &value);
  1336 
  1337 	IPlugin *pPlugin = g_PluginSys.FindPluginByContext(pContext->GetContext());
  1338 	SourceHook::List<char *> *pList = NULL;
  1339 
  1340 	if (!pPlugin->GetProperty("ServerTags", (void **)&pList, false) || !pList)
  1341 	{
  1342 		pList = new SourceHook::List<char *>;
  1343 		pPlugin->SetProperty("ServerTags", pList);
  1344 	}
  1345 
  1346 	SourceHook::List<char *>::iterator iter = pList->begin();
  1347 
  1348 	while (iter != pList->end())
  1349 	{
  1350 		if (strcmp(*iter, value) == 0)
  1351 		{
  1352 			g_pMemAlloc->Free(*iter);
  1353 			pList->erase(iter);
  1354 
  1355 			return g_Tags.RemoveTag(value);
  1356 		}
  1357 	}
  1358 
  1359 	return 0;
  1360 }
  1361 
  1362 REGISTER_NATIVES(consoleNatives)
  1363 {
  1364 	{"CreateConVar",		sm_CreateConVar},
  1365 	{"FindConVar",			sm_FindConVar},
  1366 	{"HookConVarChange",	sm_HookConVarChange},
  1367 	{"UnhookConVarChange",	sm_UnhookConVarChange},
  1368 	{"GetConVarBool",		sm_GetConVarBool},
  1369 	{"SetConVarBool",		sm_SetConVarNum},
  1370 	{"GetConVarInt",		sm_GetConVarInt},
  1371 	{"SetConVarInt",		sm_SetConVarNum},
  1372 	{"GetConVarFloat",		sm_GetConVarFloat},
  1373 	{"SetConVarFloat",		sm_SetConVarFloat},
  1374 	{"GetConVarString",		sm_GetConVarString},
  1375 	{"SetConVarString",		sm_SetConVarString},
  1376 	{"GetConVarFlags",		sm_GetConVarFlags},
  1377 	{"SetConVarFlags",		sm_SetConVarFlags},
  1378 	{"ResetConVar",			sm_ResetConVar},
  1379 	{"GetConVarName",		sm_GetConVarName},
  1380 	{"GetConVarBounds",		sm_GetConVarBounds},
  1381 	{"SetConVarBounds",		sm_SetConVarBounds},
  1382 	{"QueryClientConVar",	sm_QueryClientConVar},
  1383 	{"RegServerCmd",		sm_RegServerCmd},
  1384 	{"RegConsoleCmd",		sm_RegConsoleCmd},
  1385 	{"GetCmdArgString",		sm_GetCmdArgString},
  1386 	{"GetCmdArgs",			sm_GetCmdArgs},
  1387 	{"GetCmdArg",			sm_GetCmdArg},
  1388 	{"PrintToServer",		sm_PrintToServer},
  1389 	{"PrintToConsole",		sm_PrintToConsole},
  1390 	{"RegAdminCmd",			sm_RegAdminCmd},
  1391 	{"ServerCommand",		sm_ServerCommand},
  1392 	{"InsertServerCommand",	sm_InsertServerCommand},
  1393 	{"ServerExecute",		sm_ServerExecute},
  1394 	{"ClientCommand",		sm_ClientCommand},
  1395 	{"FakeClientCommand",	FakeClientCommand},
  1396 	{"ReplyToCommand",		ReplyToCommand},
  1397 	{"GetCmdReplySource",	GetCmdReplyTarget},
  1398 	{"SetCmdReplySource",	SetCmdReplyTarget},
  1399 	{"GetCommandIterator",	GetCommandIterator},
  1400 	{"ReadCommandIterator",	ReadCommandIterator},
  1401 	{"CheckCommandAccess",	CheckCommandAccess},
  1402 	{"FakeClientCommandEx",	FakeClientCommandEx},
  1403 	{"IsChatTrigger",		IsChatTrigger},
  1404 	{"SetCommandFlags",		SetCommandFlags},
  1405 	{"GetCommandFlags",		GetCommandFlags},
  1406 	{"FindFirstConCommand",	FindFirstConCommand},
  1407 	{"FindNextConCommand",	FindNextConCommand},
  1408 	{"SendConVarValue",		SendConVarValue},
  1409 	{"AddServerTag",		AddServerTag},
  1410 	{"RemoveServerTag",		RemoveServerTag},
  1411 	{NULL,					NULL}
  1412 };