core/smn_console.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Tue Nov 20 20:57:36 2012 -0500 (2012-11-20)
changeset 3689 514c846f5573
parent 3687 d0b1262dbc03
child 3743 a98686a77974
permissions -rw-r--r--
Merge
     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_EYE
    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 	CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
   726 	const char *group = pPlugin->GetFilename();
   727 	if (!g_ConCmds.AddAdminCommand(pFunction, name, group, 0, help, params[4]))
   728 	{
   729 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   730 	}
   731 
   732 	return 1;
   733 }
   734 
   735 static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
   736 {
   737 	char *name,*help;
   738 	const char *group;
   739 	IPluginFunction *pFunction;
   740 	FlagBits flags = params[3];
   741 	int cmdflags = params[6];
   742 
   743 	pContext->LocalToString(params[1], &name);
   744 
   745 	if (strcasecmp(name, "sm") == 0)
   746 	{
   747 		return pContext->ThrowNativeError("Cannot register \"sm\" command");
   748 	}
   749 
   750 	pContext->LocalToString(params[4], &help);
   751 	pContext->LocalToString(params[5], (char **)&group);
   752 	pFunction = pContext->GetFunctionById(params[2]);
   753 
   754 	if (group[0] == '\0')
   755 	{
   756 		CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
   757 		group = pPlugin->GetFilename();
   758 	}
   759 
   760 	if (!pFunction)
   761 	{
   762 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   763 	}
   764 
   765 	if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags))
   766 	{
   767 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   768 	}
   769 
   770 	return 1;
   771 }
   772 
   773 static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
   774 {
   775 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   776 
   777 	if (!pCmd)
   778 	{
   779 		return pContext->ThrowNativeError("No command callback available");
   780 	}
   781 
   782 	return pCmd->ArgC() - 1;
   783 }
   784 
   785 static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params)
   786 {
   787 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   788 
   789 	if (!pCmd)
   790 	{
   791 		return pContext->ThrowNativeError("No command callback available");
   792 	}
   793 
   794 	size_t length;
   795 
   796 	const char *arg = pCmd->Arg(params[1]);
   797 	
   798 	pContext->StringToLocalUTF8(params[2], params[3], arg ? arg : "", &length);
   799 
   800 	return (cell_t)length;
   801 }
   802 
   803 static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params)
   804 {
   805 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   806 
   807 	if (!pCmd)
   808 	{
   809 		return pContext->ThrowNativeError("No command callback available");
   810 	}
   811 
   812 	const char *args = pCmd->ArgS();
   813 	size_t length;
   814 
   815 	if (!args)
   816 	{
   817 		args = "";
   818 	}
   819 
   820 	pContext->StringToLocalUTF8(params[1], params[2], args, &length);
   821 
   822 	return (cell_t)length;
   823 }
   824 
   825 static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params)
   826 {
   827 	char buffer[1024];
   828 	char *fmt;
   829 	int arg = 2;
   830 
   831 	pCtx->LocalToString(params[1], &fmt);
   832 	size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
   833 
   834 	buffer[res++] = '\n';
   835 	buffer[res] = '\0';
   836 
   837 	META_CONPRINT(buffer);
   838 
   839 	return 1;
   840 }
   841 
   842 static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
   843 {
   844 	int index = params[1];
   845 	if ((index < 0) || (index > g_Players.GetMaxClients()))
   846 	{
   847 		return pCtx->ThrowNativeError("Client index %d is invalid", index);
   848 	}
   849 
   850 	CPlayer *pPlayer = NULL;
   851 	if (index != 0)
   852 	{
   853 		pPlayer = g_Players.GetPlayerByIndex(index);
   854 		if (!pPlayer->IsInGame())
   855 		{
   856 			return pCtx->ThrowNativeError("Client %d is not in game", index);
   857 		}
   858 		
   859 		/* Silent fail on bots, engine will crash */
   860 		if (pPlayer->IsFakeClient())
   861 		{
   862 			return 0;
   863 		}
   864 	}
   865 
   866 	char buffer[1024];
   867 	char *fmt;
   868 	int arg = 3;
   869 
   870 	pCtx->LocalToString(params[2], &fmt);
   871 	size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
   872 
   873 	buffer[res++] = '\n';
   874 	buffer[res] = '\0';
   875 
   876 	if (index != 0)
   877 	{
   878 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
   879 	} else {
   880 		META_CONPRINT(buffer);
   881 	}
   882 
   883 	return 1;
   884 }
   885 
   886 static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
   887 {
   888 	g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   889 
   890 	char buffer[1024];
   891 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
   892 
   893 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   894 	{
   895 		return 0;
   896 	}
   897 
   898 	/* One byte for null terminator, one for newline */
   899 	buffer[len++] = '\n';
   900 	buffer[len] = '\0';
   901 
   902 	engine->ServerCommand(buffer);
   903 
   904 	return 1;
   905 }
   906 
   907 char *g_ServerCommandBuffer = NULL;
   908 cell_t g_ServerCommandBufferLength;
   909 
   910 bool g_ShouldCatchSpew = false;
   911 
   912 #if SOURCE_ENGINE < SE_LEFT4DEAD2
   913 SpewOutputFunc_t g_OriginalSpewOutputFunc = NULL;
   914 
   915 SpewRetval_t SourcemodSpewOutputFunc(SpewType_t spewType, tchar const *pMsg)
   916 {
   917 	if (g_ServerCommandBuffer)
   918 	{
   919 		V_strcat(g_ServerCommandBuffer, pMsg, g_ServerCommandBufferLength);
   920 	}
   921 
   922 	if (g_OriginalSpewOutputFunc)
   923 	{
   924 		return g_OriginalSpewOutputFunc(spewType, pMsg);
   925 	} else {
   926 		return SPEW_CONTINUE;
   927 	}
   928 }
   929 #else
   930 class CSourcemodLoggingListener: public ILoggingListener
   931 {
   932 public:
   933 	void Log(const LoggingContext_t *pContext, const tchar *pMessage)
   934 	{
   935 		if (g_ServerCommandBuffer)
   936 		{
   937 			V_strcat(g_ServerCommandBuffer, pMessage, g_ServerCommandBufferLength);
   938 		}
   939 	}
   940 } g_SourcemodLoggingListener;
   941 #endif
   942 
   943 CON_COMMAND(sm_conhook_start, "")
   944 {
   945 	if (!g_ShouldCatchSpew)
   946 	{
   947 		Warning("You shouldn't run this command!\n");
   948 		return;
   949 	}
   950 
   951 #if SOURCE_ENGINE < SE_LEFT4DEAD2
   952 	g_OriginalSpewOutputFunc = GetSpewOutputFunc();
   953 	SpewOutputFunc(SourcemodSpewOutputFunc);
   954 #else
   955 #if SOURCE_ENGINE < SE_ALIENSWARM
   956 	LoggingSystem_PushLoggingState(false);
   957 #else
   958 	LoggingSystem_PushLoggingState(false, false);
   959 #endif
   960 	LoggingSystem_RegisterLoggingListener(&g_SourcemodLoggingListener);
   961 #endif
   962 }
   963 
   964 CON_COMMAND(sm_conhook_stop, "")
   965 {
   966 	if (!g_ShouldCatchSpew)
   967 	{
   968 		Warning("You shouldn't run this command!\n");
   969 		return;
   970 	}
   971 
   972 #if SOURCE_ENGINE < SE_LEFT4DEAD2
   973 	SpewOutputFunc(g_OriginalSpewOutputFunc);
   974 #else
   975 	LoggingSystem_PopLoggingState(false);
   976 #endif
   977 
   978 	g_ShouldCatchSpew = false;
   979 }
   980 
   981 static cell_t sm_ServerCommandEx(IPluginContext *pContext, const cell_t *params)
   982 {
   983 	g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
   984 
   985 	char buffer[1024];
   986 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 3);
   987 
   988 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
   989 	{
   990 		return 0;
   991 	}
   992 
   993 	/* One byte for null terminator, one for newline */
   994 	buffer[len++] = '\n';
   995 	buffer[len] = '\0';
   996 
   997 	pContext->LocalToString(params[1], &g_ServerCommandBuffer);
   998 	g_ServerCommandBufferLength = params[2];
   999 	
  1000 	if (g_ServerCommandBufferLength > 0)
  1001 	{
  1002 		g_ServerCommandBuffer[0] = '\0';
  1003 	}
  1004 
  1005 	engine->ServerExecute();
  1006 
  1007 	g_ShouldCatchSpew = true;
  1008 	engine->ServerCommand("sm_conhook_start\n");
  1009 	engine->ServerCommand(buffer);
  1010 	engine->ServerCommand("sm_conhook_stop\n");
  1011 
  1012 	engine->ServerExecute();
  1013 
  1014 	if (g_ServerCommandBufferLength > 0)
  1015 	{
  1016 		g_ServerCommandBuffer[g_ServerCommandBufferLength-1] = '\0';
  1017 	}
  1018 
  1019 	g_ServerCommandBuffer = NULL;
  1020 	g_ServerCommandBufferLength = 0;
  1021 
  1022 	return 1;
  1023 }
  1024 
  1025 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
  1026 {
  1027 	g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
  1028 
  1029 	char buffer[1024];
  1030 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
  1031 
  1032 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1033 	{
  1034 		return 0;
  1035 	}
  1036 
  1037 	/* One byte for null terminator, one for newline */
  1038 	buffer[len++] = '\n';
  1039 	buffer[len] = '\0';
  1040 
  1041 	InsertServerCommand(buffer);
  1042 
  1043 	return 1;
  1044 }
  1045 
  1046 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
  1047 {
  1048 	engine->ServerExecute();
  1049 
  1050 	return 1;
  1051 }
  1052 
  1053 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
  1054 {
  1055 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1056 
  1057 	if (!pPlayer)
  1058 	{
  1059 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1060 	}
  1061 
  1062 	if (!pPlayer->IsConnected())
  1063 	{
  1064 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1065 	}
  1066 
  1067 	g_SourceMod.SetGlobalTarget(params[1]);
  1068 
  1069 	char buffer[256];
  1070 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
  1071 
  1072 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1073 	{
  1074 		return 0;
  1075 	}
  1076 
  1077 	engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
  1078 
  1079 	return 1;
  1080 }
  1081 
  1082 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
  1083 {
  1084 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1085 
  1086 	if (!pPlayer)
  1087 	{
  1088 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1089 	}
  1090 
  1091 	if (!pPlayer->IsConnected())
  1092 	{
  1093 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1094 	}
  1095 
  1096 	char buffer[256];
  1097 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
  1098 
  1099 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1100 	{
  1101 		return 0;
  1102 	}
  1103 
  1104 	serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
  1105 
  1106 	return 1;
  1107 }
  1108 
  1109 static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params)
  1110 {
  1111 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1112 
  1113 	if (!pPlayer)
  1114 	{
  1115 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1116 	}
  1117 
  1118 	if (!pPlayer->IsConnected())
  1119 	{
  1120 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1121 	}
  1122 
  1123 	char buffer[256];
  1124 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
  1125 
  1126 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1127 	{
  1128 		return 0;
  1129 	}
  1130 
  1131 	g_HL2.AddToFakeCliCmdQueue(params[1], engine->GetPlayerUserId(pPlayer->GetEdict()), buffer);
  1132 
  1133 	return 1;
  1134 }
  1135 
  1136 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
  1137 {
  1138 	g_SourceMod.SetGlobalTarget(params[1]);
  1139 
  1140 	/* Build the format string */
  1141 	char buffer[1024];
  1142 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
  1143 
  1144 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1145 	{
  1146 		return 0;
  1147 	}
  1148 
  1149 	/* If we're printing to the server, shortcut out */
  1150 	if (params[1] == 0)
  1151 	{
  1152 		/* Print */
  1153 		buffer[len++] = '\n';
  1154 		buffer[len] = '\0';
  1155 		META_CONPRINT(buffer);
  1156 		return 1;
  1157 	}
  1158 
  1159 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1160 
  1161 	if (!pPlayer)
  1162 	{
  1163 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1164 	}
  1165 
  1166 	if (!pPlayer->IsConnected())
  1167 	{
  1168 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1169 	}
  1170 
  1171 	g_SourceMod.SetGlobalTarget(params[1]);
  1172 
  1173 	unsigned int replyto = g_ChatTriggers.GetReplyTo();
  1174 	if (replyto == SM_REPLY_CONSOLE)
  1175 	{
  1176 		buffer[len++] = '\n';
  1177 		buffer[len] = '\0';
  1178 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
  1179 	} else if (replyto == SM_REPLY_CHAT) {
  1180 		if (len >= 191)
  1181 		{
  1182 			len = 191;
  1183 		}
  1184 		buffer[len] = '\0';
  1185 		g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
  1186 	}
  1187 
  1188 	return 1;
  1189 }
  1190 
  1191 static cell_t GetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
  1192 {
  1193 	return g_ChatTriggers.GetReplyTo();
  1194 }
  1195 
  1196 static cell_t SetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
  1197 {
  1198 	return g_ChatTriggers.SetReplyTo(params[1]);
  1199 }
  1200 
  1201 static cell_t GetCommandIterator(IPluginContext *pContext, const cell_t *params)
  1202 {
  1203 	GlobCmdIter *iter = new GlobCmdIter;
  1204 	iter->started = false;
  1205 
  1206 	Handle_t hndl = g_HandleSys.CreateHandle(hCmdIterType, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
  1207 	if (hndl == BAD_HANDLE)
  1208 	{
  1209 		delete iter;
  1210 	}
  1211 
  1212 	return hndl;
  1213 }
  1214 
  1215 static cell_t ReadCommandIterator(IPluginContext *pContext, const cell_t *params)
  1216 {
  1217 	GlobCmdIter *iter;
  1218 	HandleError err;
  1219 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
  1220 
  1221 	if ((err = g_HandleSys.ReadHandle(params[1], hCmdIterType, &sec, (void **)&iter))
  1222 		!= HandleError_None)
  1223 	{
  1224 		return pContext->ThrowNativeError("Invalid GlobCmdIter Handle %x", params[1]);
  1225 	}
  1226 
  1227 	const List<ConCmdInfo *> &cmds = g_ConCmds.GetCommandList();
  1228 
  1229 	if (!iter->started)
  1230 	{
  1231 		iter->iter = cmds.begin();
  1232 		iter->started = true;
  1233 	}
  1234 
  1235 	while (iter->iter != cmds.end()
  1236 			&& !(*(iter->iter))->sourceMod)
  1237 	{
  1238 		iter->iter++;
  1239 	}
  1240 
  1241 	if (iter->iter == cmds.end())
  1242 	{
  1243 		return 0;
  1244 	}
  1245 
  1246 	ConCmdInfo *pInfo = (*(iter->iter));
  1247 
  1248 	pContext->StringToLocalUTF8(params[2], params[3], pInfo->pCmd->GetName(), NULL);
  1249 	pContext->StringToLocalUTF8(params[5], params[6], pInfo->pCmd->GetHelpText(), NULL);
  1250 	
  1251 	cell_t *addr;
  1252 	pContext->LocalToPhysAddr(params[4], &addr);
  1253 	*addr = pInfo->admin.eflags;
  1254 
  1255 	iter->iter++;
  1256 
  1257 	return 1;
  1258 }
  1259 
  1260 static cell_t CheckCommandAccess(IPluginContext *pContext, const cell_t *params)
  1261 {
  1262 	if (params[1] == 0)
  1263 	{
  1264 		return 1;
  1265 	}
  1266 
  1267 	char *cmd;
  1268 	pContext->LocalToString(params[2], &cmd);
  1269 
  1270 	/* Match up with an admin command if possible */
  1271 	FlagBits bits = params[3];
  1272 	bool found_command = false;
  1273 	if (params[0] < 4 || !params[4])
  1274 	{
  1275 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1276 	}
  1277 	
  1278 	if (!found_command)
  1279 	{
  1280 		g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
  1281 	}
  1282 
  1283 	return g_ConCmds.CheckClientCommandAccess(params[1], cmd, bits) ? 1 : 0;
  1284 }
  1285 
  1286 static cell_t CheckAccess(IPluginContext *pContext, const cell_t *params)
  1287 {
  1288 	char *cmd;
  1289 	pContext->LocalToString(params[2], &cmd);
  1290 
  1291 	/* Match up with an admin command if possible */
  1292 	FlagBits bits = params[3];
  1293 	bool found_command = false;
  1294 	if (params[0] < 4 || !params[4])
  1295 	{
  1296 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1297 	}
  1298 	
  1299 	if (!found_command)
  1300 	{
  1301 		g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
  1302 	}
  1303 
  1304 	return g_ConCmds.CheckAdminCommandAccess(params[1], cmd, bits) ? 1 : 0;
  1305 }
  1306 
  1307 static cell_t IsChatTrigger(IPluginContext *pContext, const cell_t *params)
  1308 {
  1309 	return g_ChatTriggers.IsChatTrigger() ? 1 : 0;
  1310 }
  1311 
  1312 static cell_t SetCommandFlags(IPluginContext *pContext, const cell_t *params)
  1313 {
  1314 	char *name;
  1315 	pContext->LocalToString(params[1], &name);
  1316 
  1317 	return (s_CommandFlagsHelper.SetFlags(name, params[2])) ? 1 : 0;
  1318 }
  1319 
  1320 static cell_t GetCommandFlags(IPluginContext *pContext, const cell_t *params)
  1321 {
  1322 	char *name;
  1323 	int flags;
  1324 	pContext->LocalToString(params[1], &name);
  1325 
  1326 	return (s_CommandFlagsHelper.GetFlags(name, &flags)) ? flags : -1;
  1327 }
  1328 
  1329 static cell_t FindFirstConCommand(IPluginContext *pContext, const cell_t *params)
  1330 {
  1331 	Handle_t hndl;
  1332 	ConCommandBaseIterator *pIter;
  1333 	cell_t *pIsCmd, *pFlags;
  1334 	const ConCommandBase *pConCmd;
  1335 	const char *desc;
  1336 
  1337 	pContext->LocalToPhysAddr(params[3], &pIsCmd);
  1338 	pContext->LocalToPhysAddr(params[4], &pFlags);
  1339 
  1340 	pIter = new ConCommandBaseIterator();
  1341 
  1342 	if (!pIter->IsValid())
  1343 	{
  1344 		delete pIter;
  1345 		return BAD_HANDLE;
  1346 	}
  1347 
  1348 	pConCmd = pIter->Get();
  1349 
  1350 	pContext->StringToLocalUTF8(params[1], params[2], pConCmd->GetName(), NULL);
  1351 	*pIsCmd = pConCmd->IsCommand() ? 1 : 0;
  1352 	*pFlags = pConCmd->GetFlags();
  1353 
  1354 	if (params[6])
  1355 	{
  1356 		desc = pConCmd->GetHelpText();
  1357 		pContext->StringToLocalUTF8(params[5], params[6], (desc && desc[0]) ? desc : "", NULL);
  1358 	}
  1359 
  1360 	if ((hndl = g_HandleSys.CreateHandle(htConCmdIter, pIter, pContext->GetIdentity(), g_pCoreIdent, NULL))
  1361 		== BAD_HANDLE)
  1362 	{
  1363 		delete pIter;
  1364 		return BAD_HANDLE;
  1365 	}
  1366 
  1367 	return hndl;
  1368 }
  1369 
  1370 static cell_t FindNextConCommand(IPluginContext *pContext, const cell_t *params)
  1371 {
  1372 	HandleError err;
  1373 	ConCommandBaseIterator *pIter;
  1374 	cell_t *pIsCmd, *pFlags;
  1375 	const char *desc;
  1376 	const ConCommandBase *pConCmd;
  1377 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
  1378 
  1379 	if ((err = g_HandleSys.ReadHandle(params[1], htConCmdIter, &sec, (void **)&pIter)) != HandleError_None)
  1380 	{
  1381 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
  1382 	}
  1383 
  1384 	if (!pIter->IsValid())
  1385 	{
  1386 		return 0;
  1387 	}
  1388 
  1389 	pIter->Next();
  1390 	if (!pIter->IsValid())
  1391 	{
  1392 		return 0;
  1393 	}
  1394 	pConCmd = pIter->Get();
  1395 
  1396 	pContext->LocalToPhysAddr(params[4], &pIsCmd);
  1397 	pContext->LocalToPhysAddr(params[5], &pFlags);
  1398 
  1399 	pContext->StringToLocalUTF8(params[2], params[3], pConCmd->GetName(), NULL);
  1400 	*pIsCmd = pConCmd->IsCommand() ? 1 : 0;
  1401 	*pFlags = pConCmd->GetFlags();
  1402 
  1403 	if (params[7])
  1404 	{
  1405 		desc = pConCmd->GetHelpText();
  1406 		pContext->StringToLocalUTF8(params[6], params[7], (desc && desc[0]) ? desc : "", NULL);
  1407 	}
  1408 
  1409 	return 1;
  1410 }
  1411 
  1412 static cell_t SendConVarValue(IPluginContext *pContext, const cell_t *params)
  1413 {
  1414 	Handle_t hndl = static_cast<Handle_t>(params[2]);
  1415 	HandleError err;
  1416 	ConVar *pConVar;
  1417 
  1418 	char *value;
  1419 	pContext->LocalToString(params[3], &value);
  1420 
  1421 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
  1422 		!= HandleError_None)
  1423 	{
  1424 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
  1425 	}
  1426 
  1427 	char data[256];
  1428 	bf_write buffer(data, sizeof(data));
  1429 
  1430 	buffer.WriteUBitLong(NET_SETCONVAR, NETMSG_BITS);
  1431 	buffer.WriteByte(1);
  1432 	buffer.WriteString(pConVar->GetName());
  1433 	buffer.WriteString(value);
  1434 
  1435 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1436 
  1437 	if (!pPlayer)
  1438 	{
  1439 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1440 	}
  1441 
  1442 	if (!pPlayer->IsConnected())
  1443 	{
  1444 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1445 	}
  1446 
  1447 	if (pPlayer->IsFakeClient())
  1448 	{
  1449 		return pContext->ThrowNativeError("Client %d is fake and cannot be targeted", params[1]);
  1450 	}
  1451 
  1452 	INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(params[1]));
  1453 	if (netchan == NULL)
  1454 	{
  1455 		return 0;
  1456 	}
  1457 
  1458 	netchan->SendData(buffer);
  1459 
  1460 	return 1;
  1461 }
  1462 
  1463 static cell_t AddServerTag(IPluginContext *pContext, const cell_t *params)
  1464 {
  1465 	return 0;
  1466 }
  1467 
  1468 static cell_t RemoveServerTag(IPluginContext *pContext, const cell_t *params)
  1469 {
  1470 	return 0;
  1471 }
  1472 
  1473 static cell_t AddCommandListener(IPluginContext *pContext, const cell_t *params)
  1474 {
  1475 	char *name;
  1476 	IPluginFunction *pFunction;
  1477 
  1478 	pContext->LocalToString(params[2], &name);
  1479 
  1480 	if (strcasecmp(name, "sm") == 0)
  1481 	{
  1482 		g_Logger.LogError("Request to register \"sm\" command denied.");
  1483 		return 0;
  1484 	}
  1485 
  1486 	pFunction = pContext->GetFunctionById(params[1]);
  1487 
  1488 	if (!pFunction)
  1489 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
  1490 
  1491 	if (!g_ConsoleDetours.AddListener(pFunction, name[0] == '\0' ? NULL : name))
  1492 		return pContext->ThrowNativeError("This game does not support command listeners");
  1493 
  1494 	return 1;
  1495 }
  1496 
  1497 static cell_t RemoveCommandListener(IPluginContext *pContext, const cell_t *params)
  1498 {
  1499 	char *name;
  1500 	IPluginFunction *pFunction;
  1501 
  1502 	pContext->LocalToString(params[2], &name);
  1503 	pFunction = pContext->GetFunctionById(params[1]);
  1504 	if (!pFunction)
  1505 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
  1506 
  1507 	if (!g_ConsoleDetours.RemoveListener(pFunction, name[0] == '\0' ? NULL : name))
  1508 		return pContext->ThrowNativeError("No matching callback was registered");
  1509 	
  1510 	return 1;
  1511 }
  1512 
  1513 REGISTER_NATIVES(consoleNatives)
  1514 {
  1515 	{"CreateConVar",		sm_CreateConVar},
  1516 	{"FindConVar",			sm_FindConVar},
  1517 	{"HookConVarChange",	sm_HookConVarChange},
  1518 	{"UnhookConVarChange",	sm_UnhookConVarChange},
  1519 	{"GetConVarBool",		sm_GetConVarBool},
  1520 	{"SetConVarBool",		sm_SetConVarNum},
  1521 	{"GetConVarInt",		sm_GetConVarInt},
  1522 	{"SetConVarInt",		sm_SetConVarNum},
  1523 	{"GetConVarFloat",		sm_GetConVarFloat},
  1524 	{"SetConVarFloat",		sm_SetConVarFloat},
  1525 	{"GetConVarString",		sm_GetConVarString},
  1526 	{"SetConVarString",		sm_SetConVarString},
  1527 	{"GetConVarFlags",		sm_GetConVarFlags},
  1528 	{"SetConVarFlags",		sm_SetConVarFlags},
  1529 	{"ResetConVar",			sm_ResetConVar},
  1530 	{"GetConVarName",		sm_GetConVarName},
  1531 	{"GetConVarBounds",		sm_GetConVarBounds},
  1532 	{"SetConVarBounds",		sm_SetConVarBounds},
  1533 	{"QueryClientConVar",	sm_QueryClientConVar},
  1534 	{"GetConVarDefault",	GetConVarDefault},
  1535 	{"RegServerCmd",		sm_RegServerCmd},
  1536 	{"RegConsoleCmd",		sm_RegConsoleCmd},
  1537 	{"GetCmdArgString",		sm_GetCmdArgString},
  1538 	{"GetCmdArgs",			sm_GetCmdArgs},
  1539 	{"GetCmdArg",			sm_GetCmdArg},
  1540 	{"PrintToServer",		sm_PrintToServer},
  1541 	{"PrintToConsole",		sm_PrintToConsole},
  1542 	{"RegAdminCmd",			sm_RegAdminCmd},
  1543 	{"ServerCommand",		sm_ServerCommand},
  1544 	{"ServerCommandEx",		sm_ServerCommandEx},
  1545 	{"InsertServerCommand",	sm_InsertServerCommand},
  1546 	{"ServerExecute",		sm_ServerExecute},
  1547 	{"ClientCommand",		sm_ClientCommand},
  1548 	{"FakeClientCommand",	FakeClientCommand},
  1549 	{"ReplyToCommand",		ReplyToCommand},
  1550 	{"GetCmdReplySource",	GetCmdReplyTarget},
  1551 	{"SetCmdReplySource",	SetCmdReplyTarget},
  1552 	{"GetCommandIterator",	GetCommandIterator},
  1553 	{"ReadCommandIterator",	ReadCommandIterator},
  1554 	{"CheckCommandAccess",	CheckCommandAccess},
  1555 	{"CheckAccess",			CheckAccess},
  1556 	{"FakeClientCommandEx",	FakeClientCommandEx},
  1557 	{"IsChatTrigger",		IsChatTrigger},
  1558 	{"SetCommandFlags",		SetCommandFlags},
  1559 	{"GetCommandFlags",		GetCommandFlags},
  1560 	{"FindFirstConCommand",	FindFirstConCommand},
  1561 	{"FindNextConCommand",	FindNextConCommand},
  1562 	{"SendConVarValue",		SendConVarValue},
  1563 	{"AddServerTag",		AddServerTag},
  1564 	{"RemoveServerTag",		RemoveServerTag},
  1565 	{"AddCommandListener",	AddCommandListener},
  1566 	{"RemoveCommandListener", RemoveCommandListener},
  1567 	{NULL,					NULL}
  1568 };