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