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