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