core/smn_console.cpp
author Nicholas Hastings <skamonkey@gmail.com>
Sat Jun 02 10:07:25 2012 -0400 (2012-06-02)
changeset 3549 657ea0e26e7a
parent 3416 7cb415ecd933
child 3687 d0b1262dbc03
permissions -rw-r--r--
Added missing CS:GO GameRulesProxy gamedata.
     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 	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 	if (g_ServerCommandBufferLength > 0)
   999 	{
  1000 		g_ServerCommandBuffer[0] = '\0';
  1001 	}
  1002 
  1003 	engine->ServerExecute();
  1004 
  1005 	g_ShouldCatchSpew = true;
  1006 	engine->ServerCommand("sm_conhook_start\n");
  1007 	engine->ServerCommand(buffer);
  1008 	engine->ServerCommand("sm_conhook_stop\n");
  1009 
  1010 	engine->ServerExecute();
  1011 
  1012 	if (g_ServerCommandBufferLength > 0)
  1013 	{
  1014 		g_ServerCommandBuffer[g_ServerCommandBufferLength-1] = '\0';
  1015 	}
  1016 
  1017 	g_ServerCommandBuffer = NULL;
  1018 	g_ServerCommandBufferLength = 0;
  1019 
  1020 	return 1;
  1021 }
  1022 
  1023 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
  1024 {
  1025 	g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
  1026 
  1027 	char buffer[1024];
  1028 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
  1029 
  1030 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1031 	{
  1032 		return 0;
  1033 	}
  1034 
  1035 	/* One byte for null terminator, one for newline */
  1036 	buffer[len++] = '\n';
  1037 	buffer[len] = '\0';
  1038 
  1039 	InsertServerCommand(buffer);
  1040 
  1041 	return 1;
  1042 }
  1043 
  1044 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
  1045 {
  1046 	engine->ServerExecute();
  1047 
  1048 	return 1;
  1049 }
  1050 
  1051 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
  1052 {
  1053 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1054 
  1055 	if (!pPlayer)
  1056 	{
  1057 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1058 	}
  1059 
  1060 	if (!pPlayer->IsConnected())
  1061 	{
  1062 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1063 	}
  1064 
  1065 	g_SourceMod.SetGlobalTarget(params[1]);
  1066 
  1067 	char buffer[256];
  1068 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
  1069 
  1070 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1071 	{
  1072 		return 0;
  1073 	}
  1074 
  1075 	engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
  1076 
  1077 	return 1;
  1078 }
  1079 
  1080 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
  1081 {
  1082 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1083 
  1084 	if (!pPlayer)
  1085 	{
  1086 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1087 	}
  1088 
  1089 	if (!pPlayer->IsConnected())
  1090 	{
  1091 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1092 	}
  1093 
  1094 	char buffer[256];
  1095 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
  1096 
  1097 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1098 	{
  1099 		return 0;
  1100 	}
  1101 
  1102 	serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
  1103 
  1104 	return 1;
  1105 }
  1106 
  1107 static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params)
  1108 {
  1109 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1110 
  1111 	if (!pPlayer)
  1112 	{
  1113 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1114 	}
  1115 
  1116 	if (!pPlayer->IsConnected())
  1117 	{
  1118 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1119 	}
  1120 
  1121 	char buffer[256];
  1122 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
  1123 
  1124 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1125 	{
  1126 		return 0;
  1127 	}
  1128 
  1129 	g_HL2.AddToFakeCliCmdQueue(params[1], engine->GetPlayerUserId(pPlayer->GetEdict()), buffer);
  1130 
  1131 	return 1;
  1132 }
  1133 
  1134 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
  1135 {
  1136 	g_SourceMod.SetGlobalTarget(params[1]);
  1137 
  1138 	/* Build the format string */
  1139 	char buffer[1024];
  1140 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
  1141 
  1142 	if (pContext->GetLastNativeError() != SP_ERROR_NONE)
  1143 	{
  1144 		return 0;
  1145 	}
  1146 
  1147 	/* If we're printing to the server, shortcut out */
  1148 	if (params[1] == 0)
  1149 	{
  1150 		/* Print */
  1151 		buffer[len++] = '\n';
  1152 		buffer[len] = '\0';
  1153 		META_CONPRINT(buffer);
  1154 		return 1;
  1155 	}
  1156 
  1157 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1158 
  1159 	if (!pPlayer)
  1160 	{
  1161 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1162 	}
  1163 
  1164 	if (!pPlayer->IsConnected())
  1165 	{
  1166 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1167 	}
  1168 
  1169 	g_SourceMod.SetGlobalTarget(params[1]);
  1170 
  1171 	unsigned int replyto = g_ChatTriggers.GetReplyTo();
  1172 	if (replyto == SM_REPLY_CONSOLE)
  1173 	{
  1174 		buffer[len++] = '\n';
  1175 		buffer[len] = '\0';
  1176 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
  1177 	} else if (replyto == SM_REPLY_CHAT) {
  1178 		if (len >= 191)
  1179 		{
  1180 			len = 191;
  1181 		}
  1182 		buffer[len] = '\0';
  1183 		g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
  1184 	}
  1185 
  1186 	return 1;
  1187 }
  1188 
  1189 static cell_t GetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
  1190 {
  1191 	return g_ChatTriggers.GetReplyTo();
  1192 }
  1193 
  1194 static cell_t SetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
  1195 {
  1196 	return g_ChatTriggers.SetReplyTo(params[1]);
  1197 }
  1198 
  1199 static cell_t GetCommandIterator(IPluginContext *pContext, const cell_t *params)
  1200 {
  1201 	GlobCmdIter *iter = new GlobCmdIter;
  1202 	iter->started = false;
  1203 
  1204 	Handle_t hndl = g_HandleSys.CreateHandle(hCmdIterType, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
  1205 	if (hndl == BAD_HANDLE)
  1206 	{
  1207 		delete iter;
  1208 	}
  1209 
  1210 	return hndl;
  1211 }
  1212 
  1213 static cell_t ReadCommandIterator(IPluginContext *pContext, const cell_t *params)
  1214 {
  1215 	GlobCmdIter *iter;
  1216 	HandleError err;
  1217 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
  1218 
  1219 	if ((err = g_HandleSys.ReadHandle(params[1], hCmdIterType, &sec, (void **)&iter))
  1220 		!= HandleError_None)
  1221 	{
  1222 		return pContext->ThrowNativeError("Invalid GlobCmdIter Handle %x", params[1]);
  1223 	}
  1224 
  1225 	const List<ConCmdInfo *> &cmds = g_ConCmds.GetCommandList();
  1226 
  1227 	if (!iter->started)
  1228 	{
  1229 		iter->iter = cmds.begin();
  1230 		iter->started = true;
  1231 	}
  1232 
  1233 	while (iter->iter != cmds.end()
  1234 			&& !(*(iter->iter))->sourceMod)
  1235 	{
  1236 		iter->iter++;
  1237 	}
  1238 
  1239 	if (iter->iter == cmds.end())
  1240 	{
  1241 		return 0;
  1242 	}
  1243 
  1244 	ConCmdInfo *pInfo = (*(iter->iter));
  1245 
  1246 	pContext->StringToLocalUTF8(params[2], params[3], pInfo->pCmd->GetName(), NULL);
  1247 	pContext->StringToLocalUTF8(params[5], params[6], pInfo->pCmd->GetHelpText(), NULL);
  1248 	
  1249 	cell_t *addr;
  1250 	pContext->LocalToPhysAddr(params[4], &addr);
  1251 	*addr = pInfo->admin.eflags;
  1252 
  1253 	iter->iter++;
  1254 
  1255 	return 1;
  1256 }
  1257 
  1258 static cell_t CheckCommandAccess(IPluginContext *pContext, const cell_t *params)
  1259 {
  1260 	if (params[1] == 0)
  1261 	{
  1262 		return 1;
  1263 	}
  1264 
  1265 	char *cmd;
  1266 	pContext->LocalToString(params[2], &cmd);
  1267 
  1268 	/* Match up with an admin command if possible */
  1269 	FlagBits bits = params[3];
  1270 	bool found_command = false;
  1271 	if (params[0] < 4 || !params[4])
  1272 	{
  1273 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1274 	}
  1275 	
  1276 	if (!found_command)
  1277 	{
  1278 		g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
  1279 	}
  1280 
  1281 	return g_ConCmds.CheckClientCommandAccess(params[1], cmd, bits) ? 1 : 0;
  1282 }
  1283 
  1284 static cell_t CheckAccess(IPluginContext *pContext, const cell_t *params)
  1285 {
  1286 	char *cmd;
  1287 	pContext->LocalToString(params[2], &cmd);
  1288 
  1289 	/* Match up with an admin command if possible */
  1290 	FlagBits bits = params[3];
  1291 	bool found_command = false;
  1292 	if (params[0] < 4 || !params[4])
  1293 	{
  1294 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1295 	}
  1296 	
  1297 	if (!found_command)
  1298 	{
  1299 		g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
  1300 	}
  1301 
  1302 	return g_ConCmds.CheckAdminCommandAccess(params[1], cmd, bits) ? 1 : 0;
  1303 }
  1304 
  1305 static cell_t IsChatTrigger(IPluginContext *pContext, const cell_t *params)
  1306 {
  1307 	return g_ChatTriggers.IsChatTrigger() ? 1 : 0;
  1308 }
  1309 
  1310 static cell_t SetCommandFlags(IPluginContext *pContext, const cell_t *params)
  1311 {
  1312 	char *name;
  1313 	pContext->LocalToString(params[1], &name);
  1314 
  1315 	return (s_CommandFlagsHelper.SetFlags(name, params[2])) ? 1 : 0;
  1316 }
  1317 
  1318 static cell_t GetCommandFlags(IPluginContext *pContext, const cell_t *params)
  1319 {
  1320 	char *name;
  1321 	int flags;
  1322 	pContext->LocalToString(params[1], &name);
  1323 
  1324 	return (s_CommandFlagsHelper.GetFlags(name, &flags)) ? flags : -1;
  1325 }
  1326 
  1327 static cell_t FindFirstConCommand(IPluginContext *pContext, const cell_t *params)
  1328 {
  1329 	Handle_t hndl;
  1330 	ConCommandBaseIterator *pIter;
  1331 	cell_t *pIsCmd, *pFlags;
  1332 	const ConCommandBase *pConCmd;
  1333 	const char *desc;
  1334 
  1335 	pContext->LocalToPhysAddr(params[3], &pIsCmd);
  1336 	pContext->LocalToPhysAddr(params[4], &pFlags);
  1337 
  1338 	pIter = new ConCommandBaseIterator();
  1339 
  1340 	if (!pIter->IsValid())
  1341 	{
  1342 		delete pIter;
  1343 		return BAD_HANDLE;
  1344 	}
  1345 
  1346 	pConCmd = pIter->Get();
  1347 
  1348 	pContext->StringToLocalUTF8(params[1], params[2], pConCmd->GetName(), NULL);
  1349 	*pIsCmd = pConCmd->IsCommand() ? 1 : 0;
  1350 	*pFlags = pConCmd->GetFlags();
  1351 
  1352 	if (params[6])
  1353 	{
  1354 		desc = pConCmd->GetHelpText();
  1355 		pContext->StringToLocalUTF8(params[5], params[6], (desc && desc[0]) ? desc : "", NULL);
  1356 	}
  1357 
  1358 	if ((hndl = g_HandleSys.CreateHandle(htConCmdIter, pIter, pContext->GetIdentity(), g_pCoreIdent, NULL))
  1359 		== BAD_HANDLE)
  1360 	{
  1361 		delete pIter;
  1362 		return BAD_HANDLE;
  1363 	}
  1364 
  1365 	return hndl;
  1366 }
  1367 
  1368 static cell_t FindNextConCommand(IPluginContext *pContext, const cell_t *params)
  1369 {
  1370 	HandleError err;
  1371 	ConCommandBaseIterator *pIter;
  1372 	cell_t *pIsCmd, *pFlags;
  1373 	const char *desc;
  1374 	const ConCommandBase *pConCmd;
  1375 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
  1376 
  1377 	if ((err = g_HandleSys.ReadHandle(params[1], htConCmdIter, &sec, (void **)&pIter)) != HandleError_None)
  1378 	{
  1379 		return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
  1380 	}
  1381 
  1382 	if (!pIter->IsValid())
  1383 	{
  1384 		return 0;
  1385 	}
  1386 
  1387 	pIter->Next();
  1388 	if (!pIter->IsValid())
  1389 	{
  1390 		return 0;
  1391 	}
  1392 	pConCmd = pIter->Get();
  1393 
  1394 	pContext->LocalToPhysAddr(params[4], &pIsCmd);
  1395 	pContext->LocalToPhysAddr(params[5], &pFlags);
  1396 
  1397 	pContext->StringToLocalUTF8(params[2], params[3], pConCmd->GetName(), NULL);
  1398 	*pIsCmd = pConCmd->IsCommand() ? 1 : 0;
  1399 	*pFlags = pConCmd->GetFlags();
  1400 
  1401 	if (params[7])
  1402 	{
  1403 		desc = pConCmd->GetHelpText();
  1404 		pContext->StringToLocalUTF8(params[6], params[7], (desc && desc[0]) ? desc : "", NULL);
  1405 	}
  1406 
  1407 	return 1;
  1408 }
  1409 
  1410 static cell_t SendConVarValue(IPluginContext *pContext, const cell_t *params)
  1411 {
  1412 	Handle_t hndl = static_cast<Handle_t>(params[2]);
  1413 	HandleError err;
  1414 	ConVar *pConVar;
  1415 
  1416 	char *value;
  1417 	pContext->LocalToString(params[3], &value);
  1418 
  1419 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
  1420 		!= HandleError_None)
  1421 	{
  1422 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
  1423 	}
  1424 
  1425 	char data[256];
  1426 	bf_write buffer(data, sizeof(data));
  1427 
  1428 	buffer.WriteUBitLong(NET_SETCONVAR, NETMSG_BITS);
  1429 	buffer.WriteByte(1);
  1430 	buffer.WriteString(pConVar->GetName());
  1431 	buffer.WriteString(value);
  1432 
  1433 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
  1434 
  1435 	if (!pPlayer)
  1436 	{
  1437 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
  1438 	}
  1439 
  1440 	if (!pPlayer->IsConnected())
  1441 	{
  1442 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
  1443 	}
  1444 
  1445 	if (pPlayer->IsFakeClient())
  1446 	{
  1447 		return pContext->ThrowNativeError("Client %d is fake and cannot be targeted", params[1]);
  1448 	}
  1449 
  1450 	INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(params[1]));
  1451 	if (netchan == NULL)
  1452 	{
  1453 		return 0;
  1454 	}
  1455 
  1456 	netchan->SendData(buffer);
  1457 
  1458 	return 1;
  1459 }
  1460 
  1461 static cell_t AddServerTag(IPluginContext *pContext, const cell_t *params)
  1462 {
  1463 	return 0;
  1464 }
  1465 
  1466 static cell_t RemoveServerTag(IPluginContext *pContext, const cell_t *params)
  1467 {
  1468 	return 0;
  1469 }
  1470 
  1471 static cell_t AddCommandListener(IPluginContext *pContext, const cell_t *params)
  1472 {
  1473 	char *name;
  1474 	IPluginFunction *pFunction;
  1475 
  1476 	pContext->LocalToString(params[2], &name);
  1477 
  1478 	if (strcasecmp(name, "sm") == 0)
  1479 	{
  1480 		g_Logger.LogError("Request to register \"sm\" command denied.");
  1481 		return 0;
  1482 	}
  1483 
  1484 	pFunction = pContext->GetFunctionById(params[1]);
  1485 
  1486 	if (!pFunction)
  1487 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
  1488 
  1489 	if (!g_ConsoleDetours.AddListener(pFunction, name[0] == '\0' ? NULL : name))
  1490 		return pContext->ThrowNativeError("This game does not support command listeners");
  1491 
  1492 	return 1;
  1493 }
  1494 
  1495 static cell_t RemoveCommandListener(IPluginContext *pContext, const cell_t *params)
  1496 {
  1497 	char *name;
  1498 	IPluginFunction *pFunction;
  1499 
  1500 	pContext->LocalToString(params[2], &name);
  1501 	pFunction = pContext->GetFunctionById(params[1]);
  1502 	if (!pFunction)
  1503 		return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
  1504 
  1505 	if (!g_ConsoleDetours.RemoveListener(pFunction, name[0] == '\0' ? NULL : name))
  1506 		return pContext->ThrowNativeError("No matching callback was registered");
  1507 	
  1508 	return 1;
  1509 }
  1510 
  1511 REGISTER_NATIVES(consoleNatives)
  1512 {
  1513 	{"CreateConVar",		sm_CreateConVar},
  1514 	{"FindConVar",			sm_FindConVar},
  1515 	{"HookConVarChange",	sm_HookConVarChange},
  1516 	{"UnhookConVarChange",	sm_UnhookConVarChange},
  1517 	{"GetConVarBool",		sm_GetConVarBool},
  1518 	{"SetConVarBool",		sm_SetConVarNum},
  1519 	{"GetConVarInt",		sm_GetConVarInt},
  1520 	{"SetConVarInt",		sm_SetConVarNum},
  1521 	{"GetConVarFloat",		sm_GetConVarFloat},
  1522 	{"SetConVarFloat",		sm_SetConVarFloat},
  1523 	{"GetConVarString",		sm_GetConVarString},
  1524 	{"SetConVarString",		sm_SetConVarString},
  1525 	{"GetConVarFlags",		sm_GetConVarFlags},
  1526 	{"SetConVarFlags",		sm_SetConVarFlags},
  1527 	{"ResetConVar",			sm_ResetConVar},
  1528 	{"GetConVarName",		sm_GetConVarName},
  1529 	{"GetConVarBounds",		sm_GetConVarBounds},
  1530 	{"SetConVarBounds",		sm_SetConVarBounds},
  1531 	{"QueryClientConVar",	sm_QueryClientConVar},
  1532 	{"GetConVarDefault",	GetConVarDefault},
  1533 	{"RegServerCmd",		sm_RegServerCmd},
  1534 	{"RegConsoleCmd",		sm_RegConsoleCmd},
  1535 	{"GetCmdArgString",		sm_GetCmdArgString},
  1536 	{"GetCmdArgs",			sm_GetCmdArgs},
  1537 	{"GetCmdArg",			sm_GetCmdArg},
  1538 	{"PrintToServer",		sm_PrintToServer},
  1539 	{"PrintToConsole",		sm_PrintToConsole},
  1540 	{"RegAdminCmd",			sm_RegAdminCmd},
  1541 	{"ServerCommand",		sm_ServerCommand},
  1542 	{"ServerCommandEx",		sm_ServerCommandEx},
  1543 	{"InsertServerCommand",	sm_InsertServerCommand},
  1544 	{"ServerExecute",		sm_ServerExecute},
  1545 	{"ClientCommand",		sm_ClientCommand},
  1546 	{"FakeClientCommand",	FakeClientCommand},
  1547 	{"ReplyToCommand",		ReplyToCommand},
  1548 	{"GetCmdReplySource",	GetCmdReplyTarget},
  1549 	{"SetCmdReplySource",	SetCmdReplyTarget},
  1550 	{"GetCommandIterator",	GetCommandIterator},
  1551 	{"ReadCommandIterator",	ReadCommandIterator},
  1552 	{"CheckCommandAccess",	CheckCommandAccess},
  1553 	{"CheckAccess",			CheckAccess},
  1554 	{"FakeClientCommandEx",	FakeClientCommandEx},
  1555 	{"IsChatTrigger",		IsChatTrigger},
  1556 	{"SetCommandFlags",		SetCommandFlags},
  1557 	{"GetCommandFlags",		GetCommandFlags},
  1558 	{"FindFirstConCommand",	FindFirstConCommand},
  1559 	{"FindNextConCommand",	FindNextConCommand},
  1560 	{"SendConVarValue",		SendConVarValue},
  1561 	{"AddServerTag",		AddServerTag},
  1562 	{"RemoveServerTag",		RemoveServerTag},
  1563 	{"AddCommandListener",	AddCommandListener},
  1564 	{"RemoveCommandListener", RemoveCommandListener},
  1565 	{NULL,					NULL}
  1566 };