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