core/smn_console.cpp
author Michael McKoy <ferret99gt@gmail.com>
Sat Nov 24 03:28:33 2007 +0000 (2007-11-24)
changeset 1719 c9d5e55f0899
parent 1643 2ea224c9e9a1
child 1795 255de877ac25
permissions -rw-r--r--
The current map played shouldn't appear in MapChooser votes or RTV votes now. It can still be nominated in RTV though.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourceMod
     5  * Copyright (C) 2004-2007 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 
    47 #define NET_SETCONVAR	5
    48 
    49 enum ConVarBounds
    50 {
    51 	ConVarBound_Upper = 0,
    52 	ConVarBound_Lower
    53 };
    54 
    55 HandleType_t hCmdIterType = 0;
    56 
    57 struct GlobCmdIter
    58 {
    59 	bool started;
    60 	List<ConCmdInfo *>::iterator iter;
    61 };
    62 
    63 class ConsoleHelpers : 
    64 	public SMGlobalClass,
    65 	public IHandleTypeDispatch
    66 {
    67 public:
    68 	virtual void OnSourceModAllInitialized()
    69 	{
    70 		HandleAccess access;
    71 		g_HandleSys.InitAccessDefaults(NULL, &access);
    72 		access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER | HANDLE_RESTRICT_IDENTITY;
    73 
    74 		hCmdIterType = g_HandleSys.CreateType(NULL, this, 0, NULL, &access, g_pCoreIdent, NULL);
    75 	}
    76 	virtual void OnHandleDestroy(HandleType_t type, void *object)
    77 	{
    78 		GlobCmdIter *iter = (GlobCmdIter *)object;
    79 		delete iter;
    80 	}
    81 } s_ConsoleHelpers;
    82 
    83 class CommandFlagsHelper : public IConCommandTracker
    84 {
    85 public:
    86 	void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
    87 	{
    88 		m_CmdFlags.remove(name);
    89 	}
    90 	bool GetFlags(const char *name, int *flags)
    91 	{
    92 		ConCommandBase **ppCmd;
    93 		ConCommandBase *pCmd;
    94 		if ((ppCmd=m_CmdFlags.retrieve(name)))
    95 		{
    96 			TrackConCommandBase((*ppCmd), this);
    97 			*flags = (*ppCmd)->GetFlags();
    98 			return true;
    99 		}
   100 		else if ((pCmd=FindConCommandBase(name)))
   101 		{
   102 			m_CmdFlags.insert(name, pCmd);
   103 			TrackConCommandBase(pCmd, this);
   104 			*flags = pCmd->GetFlags();
   105 			return true;
   106 		}
   107 		else
   108 		{
   109 			return false;
   110 		}
   111 	}
   112 	bool SetFlags(const char *name, int flags)
   113 	{
   114 		ConCommandBase **ppCmd;
   115 		ConCommandBase *pCmd;
   116 		if ((ppCmd=m_CmdFlags.retrieve(name)))
   117 		{
   118 			(*ppCmd)->SetFlags(flags);
   119 			TrackConCommandBase((*ppCmd), this);
   120 			return true;
   121 		}
   122 		else if ((pCmd=FindConCommandBase(name)))
   123 		{
   124 			m_CmdFlags.insert(name, pCmd);
   125 			pCmd->SetFlags(flags);
   126 			TrackConCommandBase(pCmd, this);
   127 			return true;
   128 		}
   129 		else
   130 		{
   131 			return false;
   132 		}
   133 	}
   134 private:
   135 	KTrie<ConCommandBase *> m_CmdFlags;
   136 } s_CommandFlagsHelper;
   137 
   138 static void ReplicateConVar(ConVar *pConVar)
   139 {
   140 	int maxClients = g_Players.GetMaxClients();
   141 
   142 	char data[256];
   143 	bf_write buffer(data, sizeof(data));
   144 
   145 	buffer.WriteUBitLong(NET_SETCONVAR, 5);
   146 	buffer.WriteByte(1);
   147 	buffer.WriteString(pConVar->GetName());
   148 	buffer.WriteString(pConVar->GetString());
   149 
   150 	for (int i = 1; i <= maxClients; i++)
   151 	{
   152 		CPlayer *pPlayer = g_Players.GetPlayerByIndex(i);
   153 
   154 		if (pPlayer && pPlayer->IsInGame() && !pPlayer->IsFakeClient())
   155 		{
   156 			INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(i));
   157 			netchan->SendData(buffer);
   158 		}
   159 	}
   160 }
   161 
   162 static void NotifyConVar(ConVar *pConVar)
   163 {
   164 	IGameEvent *pEvent = gameevents->CreateEvent("server_cvar");
   165 	pEvent->SetString("cvarname", pConVar->GetName());
   166 	if (IsFlagSet(pConVar, FCVAR_PROTECTED))
   167 	{
   168 		pEvent->SetString("cvarvalue", "***PROTECTED***");
   169 	}
   170 	else
   171 	{
   172 		pEvent->SetString("cvarvalue", pConVar->GetString());
   173 	}
   174 
   175 	gameevents->FireEvent(pEvent);
   176 }
   177 
   178 static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
   179 {
   180 	char *name, *defaultVal, *helpText;
   181 
   182 	pContext->LocalToString(params[1], &name);
   183 
   184 	// While the engine seems to accept a blank convar name, it causes a crash upon server quit
   185 	if (name == NULL || strcmp(name, "") == 0)
   186 	{
   187 		return pContext->ThrowNativeError("Convar with blank name is not permitted");
   188 	}
   189 
   190 	pContext->LocalToString(params[2], &defaultVal);
   191 	pContext->LocalToString(params[3], &helpText);
   192 
   193 	bool hasMin = params[5] ? true : false;
   194 	bool hasMax = params[7] ? true : false;
   195 	float min = sp_ctof(params[6]);
   196 	float max = sp_ctof(params[8]);
   197 
   198 	Handle_t hndl = g_ConVarManager.CreateConVar(pContext, name, defaultVal, helpText, params[4], hasMin, min, hasMax, max);
   199 
   200 	if (hndl == BAD_HANDLE)
   201 	{
   202 		return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same might already exist.", name);
   203 	}
   204 
   205 	return hndl;
   206 }
   207 
   208 static cell_t sm_FindConVar(IPluginContext *pContext, const cell_t *params)
   209 {
   210 	char *name;
   211 
   212 	pContext->LocalToString(params[1], &name);
   213 
   214 	return g_ConVarManager.FindConVar(name);
   215 }
   216 
   217 static cell_t sm_HookConVarChange(IPluginContext *pContext, const cell_t *params)
   218 {
   219 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   220 	HandleError err;
   221 	ConVar *pConVar;
   222 
   223 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   224 		!= HandleError_None)
   225 	{
   226 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   227 	}
   228 
   229 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
   230 
   231 	if (!pFunction)
   232 	{
   233 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   234 	}
   235 
   236 	g_ConVarManager.HookConVarChange(pConVar, pFunction);
   237 
   238 	return 1;
   239 }
   240 
   241 static cell_t sm_UnhookConVarChange(IPluginContext *pContext, const cell_t *params)
   242 {
   243 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   244 	HandleError err;
   245 	ConVar *pConVar;
   246 
   247 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   248 		!= HandleError_None)
   249 	{
   250 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   251 	}
   252 
   253 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
   254 
   255 	if (!pFunction)
   256 	{
   257 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   258 	}
   259 
   260 	g_ConVarManager.UnhookConVarChange(pConVar, pFunction);
   261 
   262 	return 1;
   263 }
   264 
   265 static cell_t sm_GetConVarBool(IPluginContext *pContext, const cell_t *params)
   266 {
   267 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   268 	HandleError err;
   269 	ConVar *pConVar;
   270 
   271 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   272 		!= HandleError_None)
   273 	{
   274 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   275 	}
   276 
   277 	return pConVar->GetBool();
   278 }
   279 
   280 static cell_t sm_GetConVarInt(IPluginContext *pContext, const cell_t *params)
   281 {
   282 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   283 	HandleError err;
   284 	ConVar *pConVar;
   285 
   286 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   287 		!= HandleError_None)
   288 	{
   289 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   290 	}
   291 
   292 	return pConVar->GetInt();
   293 }
   294 
   295 /* This handles both SetConVarBool() and SetConVarInt() */
   296 static cell_t sm_SetConVarNum(IPluginContext *pContext, const cell_t *params)
   297 {
   298 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   299 	HandleError err;
   300 	ConVar *pConVar;
   301 
   302 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   303 		!= HandleError_None)
   304 	{
   305 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   306 	}
   307 
   308 	pConVar->SetValue(params[2]);
   309 
   310 	/* Should we replicate it? */
   311 	if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
   312 	{
   313 		ReplicateConVar(pConVar);
   314 	}
   315 
   316 	/* Should we notify clients? */
   317 	if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
   318 	{
   319 		NotifyConVar(pConVar);
   320 	}
   321 
   322 	return 1;
   323 }
   324 
   325 static cell_t sm_GetConVarFloat(IPluginContext *pContext, const cell_t *params)
   326 {
   327 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   328 	HandleError err;
   329 	ConVar *pConVar;
   330 
   331 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   332 		!= HandleError_None)
   333 	{
   334 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   335 	}
   336 	
   337 	float value = pConVar->GetFloat();
   338 
   339 	return sp_ftoc(value);
   340 }
   341 
   342 static cell_t sm_SetConVarFloat(IPluginContext *pContext, const cell_t *params)
   343 {
   344 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   345 	HandleError err;
   346 	ConVar *pConVar;
   347 
   348 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   349 		!= HandleError_None)
   350 	{
   351 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   352 	}
   353 
   354 	float value = sp_ctof(params[2]);
   355 	pConVar->SetValue(value);
   356 
   357 	/* Should we replicate it? */
   358 	if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
   359 	{
   360 		ReplicateConVar(pConVar);
   361 	}
   362 
   363 	/* Should we notify clients? */
   364 	if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
   365 	{
   366 		NotifyConVar(pConVar);
   367 	}
   368 
   369 	return 1;
   370 }
   371 
   372 static cell_t sm_GetConVarString(IPluginContext *pContext, const cell_t *params)
   373 {
   374 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   375 	HandleError err;
   376 	ConVar *pConVar;
   377 
   378 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   379 		!= HandleError_None)
   380 	{
   381 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   382 	}
   383 
   384 	pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetString(), NULL);
   385 
   386 	return 1;
   387 }
   388 
   389 static cell_t sm_SetConVarString(IPluginContext *pContext, const cell_t *params)
   390 {
   391 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   392 	HandleError err;
   393 	ConVar *pConVar;
   394 
   395 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   396 		!= HandleError_None)
   397 	{
   398 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   399 	}
   400 
   401 	char *value;
   402 	pContext->LocalToString(params[2], &value);
   403 
   404 	pConVar->SetValue(value);
   405 
   406 	/* Should we replicate it? */
   407 	if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
   408 	{
   409 		ReplicateConVar(pConVar);
   410 	}
   411 
   412 	/* Should we notify clients? */
   413 	if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
   414 	{
   415 		NotifyConVar(pConVar);
   416 	}
   417 
   418 	return 1;
   419 }
   420 
   421 static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
   422 {
   423 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   424 	HandleError err;
   425 	ConVar *pConVar;
   426 
   427 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   428 		!= HandleError_None)
   429 	{
   430 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   431 	}
   432 
   433 	pConVar->Revert();
   434 	
   435 	/* Should we replicate it? */
   436 	if (params[2] && IsFlagSet(pConVar, FCVAR_REPLICATED))
   437 	{
   438 		ReplicateConVar(pConVar);
   439 	}
   440 
   441 	/* Should we notify clients? */
   442 	if (params[3] && IsFlagSet(pConVar, FCVAR_NOTIFY))
   443 	{
   444 		NotifyConVar(pConVar);
   445 	}
   446 
   447 	return 1;
   448 }
   449 
   450 static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params)
   451 {
   452 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   453 	HandleError err;
   454 	ConVar *pConVar;
   455 
   456 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   457 		!= HandleError_None)
   458 	{
   459 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   460 	}
   461 
   462 	return pConVar->GetFlags();
   463 }
   464 
   465 static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params)
   466 {
   467 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   468 	HandleError err;
   469 	ConVar *pConVar;
   470 
   471 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   472 		!= HandleError_None)
   473 	{
   474 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   475 	}
   476 
   477 	pConVar->SetFlags(params[2]);
   478 
   479 	return 1;
   480 }
   481 
   482 static cell_t sm_GetConVarBounds(IPluginContext *pContext, const cell_t *params)
   483 {
   484 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   485 	HandleError err;
   486 	ConVar *pConVar;
   487 
   488 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   489 		!= HandleError_None)
   490 	{
   491 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   492 	}
   493 
   494 	cell_t *addr;
   495 	bool hasBound;
   496 	float bound;
   497 
   498 	switch (params[2])
   499 	{
   500 	case ConVarBound_Upper:
   501 		hasBound = pConVar->GetMax(bound);
   502 		break;
   503 	case ConVarBound_Lower:
   504 		hasBound = pConVar->GetMin(bound);
   505 		break;
   506 	default:
   507 		return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
   508 	}
   509 	
   510 	pContext->LocalToPhysAddr(params[3], &addr);
   511 	*addr = sp_ftoc(bound);
   512 
   513 	return hasBound;
   514 }
   515 
   516 static cell_t sm_SetConVarBounds(IPluginContext *pContext, const cell_t *params)
   517 {
   518 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   519 	HandleError err;
   520 	ConVar *pConVar;
   521 
   522 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   523 		!= HandleError_None)
   524 	{
   525 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   526 	}
   527 
   528 	switch (params[2])
   529 	{
   530 	case ConVarBound_Upper:
   531 		pConVar->SetMax(params[3] ? true : false, sp_ctof(params[4]));
   532 		break;
   533 	case ConVarBound_Lower:
   534 		pConVar->SetMin(params[3] ? true : false, sp_ctof(params[4]));
   535 		break;
   536 	default:
   537 		return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
   538 	}
   539 
   540 	return 1;
   541 }
   542 
   543 static cell_t sm_GetConVarName(IPluginContext *pContext, const cell_t *params)
   544 {
   545 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   546 	HandleError err;
   547 	ConVar *pConVar;
   548 
   549 	if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
   550 		!= HandleError_None)
   551 	{
   552 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   553 	}
   554 
   555 	pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetName(), NULL);
   556 
   557 	return 1;
   558 }
   559 
   560 static bool s_QueryAlreadyWarned = false;
   561 
   562 static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params)
   563 {
   564 	CPlayer *pPlayer;
   565 	char *name;
   566 	IPluginFunction *pCallback;
   567 
   568 	if (!g_ConVarManager.IsQueryingSupported())
   569 	{
   570 		if (!s_QueryAlreadyWarned)
   571 		{
   572 			s_QueryAlreadyWarned = true;
   573 			return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)");
   574 		}
   575 
   576 		return 0;
   577 	}
   578 
   579 	pPlayer = g_Players.GetPlayerByIndex(params[1]);
   580 
   581 	if (!pPlayer)
   582 	{
   583 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   584 	}
   585 
   586 	if (!pPlayer->IsConnected())
   587 	{
   588 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   589 	}
   590 
   591 	/* Trying a query on a bot results in callback not be fired, so don't bother */
   592 	if (pPlayer->IsFakeClient())
   593 	{
   594 		return 0;
   595 	}
   596 
   597 	pContext->LocalToString(params[2], &name);
   598 	pCallback = pContext->GetFunctionById(params[3]);
   599 
   600 	if (!pCallback)
   601 	{
   602 		return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
   603 	}
   604 
   605 	return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]);
   606 }
   607 
   608 static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
   609 {
   610 	char *name,*help;
   611 	IPluginFunction *pFunction;
   612 
   613 	pContext->LocalToString(params[1], &name);
   614 	pContext->LocalToString(params[3], &help);
   615 	pFunction = pContext->GetFunctionById(params[2]);
   616 
   617 	if (!pFunction)
   618 	{
   619 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   620 	}
   621 
   622 	if (!g_ConCmds.AddServerCommand(pFunction, name, help, params[4]))
   623 	{
   624 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   625 	}
   626 
   627 	return 1;
   628 }
   629 
   630 static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params)
   631 {
   632 	char *name,*help;
   633 	IPluginFunction *pFunction;
   634 
   635 	pContext->LocalToString(params[1], &name);
   636 	pContext->LocalToString(params[3], &help);
   637 	pFunction = pContext->GetFunctionById(params[2]);
   638 
   639 	if (!pFunction)
   640 	{
   641 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   642 	}
   643 
   644 	if (!g_ConCmds.AddConsoleCommand(pFunction, name, help, params[4]))
   645 	{
   646 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   647 	}
   648 
   649 	return 1;
   650 }
   651 
   652 static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
   653 {
   654 	char *name,*help;
   655 	const char *group;
   656 	IPluginFunction *pFunction;
   657 	FlagBits flags = params[3];
   658 	int cmdflags = params[6];
   659 
   660 	pContext->LocalToString(params[1], &name);
   661 	pContext->LocalToString(params[4], &help);
   662 	pContext->LocalToString(params[5], (char **)&group);
   663 	pFunction = pContext->GetFunctionById(params[2]);
   664 
   665 	if (group[0] == '\0')
   666 	{
   667 		CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
   668 		group = pPlugin->GetFilename();
   669 	}
   670 
   671 	if (!pFunction)
   672 	{
   673 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   674 	}
   675 
   676 	if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags))
   677 	{
   678 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   679 	}
   680 
   681 	return 1;
   682 }
   683 
   684 static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
   685 {
   686 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   687 
   688 	if (!pCmd)
   689 	{
   690 		return pContext->ThrowNativeError("No command callback available");
   691 	}
   692 
   693 	return pCmd->ArgC() - 1;
   694 }
   695 
   696 static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params)
   697 {
   698 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   699 
   700 	if (!pCmd)
   701 	{
   702 		return pContext->ThrowNativeError("No command callback available");
   703 	}
   704 
   705 	size_t length;
   706 
   707 	const char *arg = pCmd->Arg(params[1]);
   708 	
   709 	pContext->StringToLocalUTF8(params[2], params[3], arg ? arg : "", &length);
   710 
   711 	return (cell_t)length;
   712 }
   713 
   714 static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params)
   715 {
   716 	const CCommand *pCmd = g_HL2.PeekCommandStack();
   717 
   718 	if (!pCmd)
   719 	{
   720 		return pContext->ThrowNativeError("No command callback available");
   721 	}
   722 
   723 	const char *args = pCmd->ArgS();
   724 	size_t length;
   725 
   726 	if (!args)
   727 	{
   728 		args = "";
   729 	}
   730 
   731 	pContext->StringToLocalUTF8(params[1], params[2], args, &length);
   732 
   733 	return (cell_t)length;
   734 }
   735 
   736 static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params)
   737 {
   738 	char buffer[1024];
   739 	char *fmt;
   740 	int arg = 2;
   741 
   742 	pCtx->LocalToString(params[1], &fmt);
   743 	size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
   744 
   745 	buffer[res++] = '\n';
   746 	buffer[res] = '\0';
   747 
   748 	META_CONPRINT(buffer);
   749 
   750 	return 1;
   751 }
   752 
   753 static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
   754 {
   755 	int index = params[1];
   756 	if ((index < 0) || (index > g_Players.GetMaxClients()))
   757 	{
   758 		return pCtx->ThrowNativeError("Client index %d is invalid", index);
   759 	}
   760 
   761 	CPlayer *pPlayer = NULL;
   762 	if (index != 0)
   763 	{
   764 		pPlayer = g_Players.GetPlayerByIndex(index);
   765 		if (!pPlayer->IsInGame())
   766 		{
   767 			return pCtx->ThrowNativeError("Client %d is not in game", index);
   768 		}
   769 		
   770 		/* Silent fail on bots, engine will crash */
   771 		if (pPlayer->IsFakeClient())
   772 		{
   773 			return 0;
   774 		}
   775 	}
   776 
   777 	char buffer[1024];
   778 	char *fmt;
   779 	int arg = 3;
   780 
   781 	pCtx->LocalToString(params[2], &fmt);
   782 	size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
   783 
   784 	buffer[res++] = '\n';
   785 	buffer[res] = '\0';
   786 
   787 	if (index != 0)
   788 	{
   789 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
   790 	} else {
   791 		META_CONPRINT(buffer);
   792 	}
   793 
   794 	return 1;
   795 }
   796 
   797 static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
   798 {
   799 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   800 
   801 	char buffer[1024];
   802 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
   803 
   804 	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
   805 	{
   806 		return 0;
   807 	}
   808 
   809 	/* One byte for null terminator, one for newline */
   810 	buffer[len++] = '\n';
   811 	buffer[len] = '\0';
   812 
   813 	engine->ServerCommand(buffer);
   814 
   815 	return 1;
   816 }
   817 
   818 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
   819 {
   820 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   821 
   822 	char buffer[1024];
   823 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
   824 
   825 	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
   826 	{
   827 		return 0;
   828 	}
   829 
   830 	/* One byte for null terminator, one for newline */
   831 	buffer[len++] = '\n';
   832 	buffer[len] = '\0';
   833 
   834 	InsertServerCommand(buffer);
   835 
   836 	return 1;
   837 }
   838 
   839 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
   840 {
   841 	engine->ServerExecute();
   842 
   843 	return 1;
   844 }
   845 
   846 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
   847 {
   848 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   849 
   850 	if (!pPlayer)
   851 	{
   852 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   853 	}
   854 
   855 	if (!pPlayer->IsConnected())
   856 	{
   857 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   858 	}
   859 
   860 	g_SourceMod.SetGlobalTarget(params[1]);
   861 
   862 	char buffer[256];
   863 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   864 
   865 	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
   866 	{
   867 		return 0;
   868 	}
   869 
   870 	engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
   871 
   872 	return 1;
   873 }
   874 
   875 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
   876 {
   877 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   878 
   879 	if (!pPlayer)
   880 	{
   881 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   882 	}
   883 
   884 	if (!pPlayer->IsConnected())
   885 	{
   886 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   887 	}
   888 
   889 	char buffer[256];
   890 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   891 
   892 	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
   893 	{
   894 		return 0;
   895 	}
   896 
   897 	serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
   898 
   899 	return 1;
   900 }
   901 
   902 static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params)
   903 {
   904 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   905 
   906 	if (!pPlayer)
   907 	{
   908 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   909 	}
   910 
   911 	if (!pPlayer->IsConnected())
   912 	{
   913 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   914 	}
   915 
   916 	char buffer[256];
   917 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   918 
   919 	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
   920 	{
   921 		return 0;
   922 	}
   923 
   924 	g_HL2.AddToFakeCliCmdQueue(params[1], engine->GetPlayerUserId(pPlayer->GetEdict()), buffer);
   925 
   926 	return 1;
   927 }
   928 
   929 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
   930 {
   931 	g_SourceMod.SetGlobalTarget(params[1]);
   932 
   933 	/* Build the format string */
   934 	char buffer[1024];
   935 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
   936 
   937 	if (pContext->GetContext()->n_err != SP_ERROR_NONE)
   938 	{
   939 		return 0;
   940 	}
   941 
   942 	/* If we're printing to the server, shortcut out */
   943 	if (params[1] == 0)
   944 	{
   945 		/* Print */
   946 		buffer[len++] = '\n';
   947 		buffer[len] = '\0';
   948 		META_CONPRINT(buffer);
   949 		return 1;
   950 	}
   951 
   952 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   953 
   954 	if (!pPlayer)
   955 	{
   956 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   957 	}
   958 
   959 	if (!pPlayer->IsConnected())
   960 	{
   961 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   962 	}
   963 
   964 	g_SourceMod.SetGlobalTarget(params[1]);
   965 
   966 	unsigned int replyto = g_ChatTriggers.GetReplyTo();
   967 	if (replyto == SM_REPLY_CONSOLE)
   968 	{
   969 		buffer[len++] = '\n';
   970 		buffer[len] = '\0';
   971 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
   972 	} else if (replyto == SM_REPLY_CHAT) {
   973 		if (len >= 191)
   974 		{
   975 			len = 191;
   976 		}
   977 		buffer[len] = '\0';
   978 		g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
   979 	}
   980 
   981 	return 1;
   982 }
   983 
   984 static cell_t GetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
   985 {
   986 	return g_ChatTriggers.GetReplyTo();
   987 }
   988 
   989 static cell_t SetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
   990 {
   991 	return g_ChatTriggers.SetReplyTo(params[1]);
   992 }
   993 
   994 static cell_t GetCommandIterator(IPluginContext *pContext, const cell_t *params)
   995 {
   996 	GlobCmdIter *iter = new GlobCmdIter;
   997 	iter->started = false;
   998 
   999 	Handle_t hndl = g_HandleSys.CreateHandle(hCmdIterType, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
  1000 	if (hndl == BAD_HANDLE)
  1001 	{
  1002 		delete iter;
  1003 	}
  1004 
  1005 	return hndl;
  1006 }
  1007 
  1008 static cell_t ReadCommandIterator(IPluginContext *pContext, const cell_t *params)
  1009 {
  1010 	GlobCmdIter *iter;
  1011 	HandleError err;
  1012 	HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
  1013 
  1014 	if ((err = g_HandleSys.ReadHandle(params[1], hCmdIterType, &sec, (void **)&iter))
  1015 		!= HandleError_None)
  1016 	{
  1017 		return pContext->ThrowNativeError("Invalid GlobCmdIter Handle %x", params[1]);
  1018 	}
  1019 
  1020 	const List<ConCmdInfo *> &cmds = g_ConCmds.GetCommandList();
  1021 
  1022 	if (!iter->started)
  1023 	{
  1024 		iter->iter = cmds.begin();
  1025 		iter->started = true;
  1026 	}
  1027 
  1028 	while (iter->iter != cmds.end()
  1029 			&& !(*(iter->iter))->sourceMod)
  1030 	{
  1031 		iter->iter++;
  1032 	}
  1033 
  1034 	if (iter->iter == cmds.end())
  1035 	{
  1036 		return 0;
  1037 	}
  1038 
  1039 	ConCmdInfo *pInfo = (*(iter->iter));
  1040 
  1041 	pContext->StringToLocalUTF8(params[2], params[3], pInfo->pCmd->GetName(), NULL);
  1042 	pContext->StringToLocalUTF8(params[5], params[6], pInfo->pCmd->GetHelpText(), NULL);
  1043 	
  1044 	cell_t *addr;
  1045 	pContext->LocalToPhysAddr(params[4], &addr);
  1046 	*addr = pInfo->admin.eflags;
  1047 
  1048 	iter->iter++;
  1049 
  1050 	return 1;
  1051 }
  1052 
  1053 static cell_t CheckCommandAccess(IPluginContext *pContext, const cell_t *params)
  1054 {
  1055 	if (params[1] == 0)
  1056 	{
  1057 		return 1;
  1058 	}
  1059 
  1060 	char *cmd;
  1061 	pContext->LocalToString(params[2], &cmd);
  1062 
  1063 	/* Auto-detect a command if we can */
  1064 	FlagBits bits = params[3];
  1065 	bool found_command = false;
  1066 	if (params[0] < 4 || !params[4])
  1067 	{
  1068 		found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
  1069 	}
  1070 	
  1071 	if (!found_command)
  1072 	{
  1073 		g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
  1074 	}
  1075 
  1076 	return g_ConCmds.CheckCommandAccess(params[1], cmd, bits) ? 1 : 0;
  1077 }
  1078 
  1079 static cell_t IsChatTrigger(IPluginContext *pContext, const cell_t *params)
  1080 {
  1081 	return g_ChatTriggers.IsChatTrigger() ? 1 : 0;
  1082 }
  1083 
  1084 static cell_t SetCommandFlags(IPluginContext *pContext, const cell_t *params)
  1085 {
  1086 	char *name;
  1087 	pContext->LocalToString(params[1], &name);
  1088 
  1089 	return (s_CommandFlagsHelper.SetFlags(name, params[2])) ? 1 : 0;
  1090 }
  1091 
  1092 static cell_t GetCommandFlags(IPluginContext *pContext, const cell_t *params)
  1093 {
  1094 	char *name;
  1095 	int flags;
  1096 	pContext->LocalToString(params[1], &name);
  1097 
  1098 	return (s_CommandFlagsHelper.GetFlags(name, &flags)) ? flags : -1;
  1099 }
  1100 
  1101 REGISTER_NATIVES(consoleNatives)
  1102 {
  1103 	{"CreateConVar",		sm_CreateConVar},
  1104 	{"FindConVar",			sm_FindConVar},
  1105 	{"HookConVarChange",	sm_HookConVarChange},
  1106 	{"UnhookConVarChange",	sm_UnhookConVarChange},
  1107 	{"GetConVarBool",		sm_GetConVarBool},
  1108 	{"SetConVarBool",		sm_SetConVarNum},
  1109 	{"GetConVarInt",		sm_GetConVarInt},
  1110 	{"SetConVarInt",		sm_SetConVarNum},
  1111 	{"GetConVarFloat",		sm_GetConVarFloat},
  1112 	{"SetConVarFloat",		sm_SetConVarFloat},
  1113 	{"GetConVarString",		sm_GetConVarString},
  1114 	{"SetConVarString",		sm_SetConVarString},
  1115 	{"GetConVarFlags",		sm_GetConVarFlags},
  1116 	{"SetConVarFlags",		sm_SetConVarFlags},
  1117 	{"ResetConVar",			sm_ResetConVar},
  1118 	{"GetConVarName",		sm_GetConVarName},
  1119 	{"GetConVarBounds",		sm_GetConVarBounds},
  1120 	{"SetConVarBounds",		sm_SetConVarBounds},
  1121 	{"QueryClientConVar",	sm_QueryClientConVar},
  1122 	{"RegServerCmd",		sm_RegServerCmd},
  1123 	{"RegConsoleCmd",		sm_RegConsoleCmd},
  1124 	{"GetCmdArgString",		sm_GetCmdArgString},
  1125 	{"GetCmdArgs",			sm_GetCmdArgs},
  1126 	{"GetCmdArg",			sm_GetCmdArg},
  1127 	{"PrintToServer",		sm_PrintToServer},
  1128 	{"PrintToConsole",		sm_PrintToConsole},
  1129 	{"RegAdminCmd",			sm_RegAdminCmd},
  1130 	{"ServerCommand",		sm_ServerCommand},
  1131 	{"InsertServerCommand",	sm_InsertServerCommand},
  1132 	{"ServerExecute",		sm_ServerExecute},
  1133 	{"ClientCommand",		sm_ClientCommand},
  1134 	{"FakeClientCommand",	FakeClientCommand},
  1135 	{"ReplyToCommand",		ReplyToCommand},
  1136 	{"GetCmdReplySource",	GetCmdReplyTarget},
  1137 	{"SetCmdReplySource",	SetCmdReplyTarget},
  1138 	{"GetCommandIterator",	GetCommandIterator},
  1139 	{"ReadCommandIterator",	ReadCommandIterator},
  1140 	{"CheckCommandAccess",	CheckCommandAccess},
  1141 	{"FakeClientCommandEx",	FakeClientCommandEx},
  1142 	{"IsChatTrigger",		IsChatTrigger},
  1143 	{"SetCommandFlags",		SetCommandFlags},
  1144 	{"GetCommandFlags",		GetCommandFlags},
  1145 	{NULL,					NULL}
  1146 };