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