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