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