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