core/smn_console.cpp
author Borja Ferrer <faluco@alliedmods.net>
Thu Jul 05 14:47:36 2007 +0000 (2007-07-05)
changeset 1041 f7b2073013df
parent 1011 b413072540fd
child 1071 d15a3e83a3d4
permissions -rw-r--r--
added a time stamp argument to FormatTime
     1 /**
     2  * vim: set ts=4 :
     3  * ===============================================================
     4  * SourceMod (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ===============================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include "sm_globals.h"
    16 #include "HalfLife2.h"
    17 #include "sourcemm_api.h"
    18 #include "HandleSys.h"
    19 #include "ConVarManager.h"
    20 #include "ConCmdManager.h"
    21 #include "PluginSys.h"
    22 #include "sm_stringutil.h"
    23 #include "PlayerManager.h"
    24 #include "ChatTriggers.h"
    25 #include <inetchannel.h>
    26 #include <bitbuf.h>
    27 
    28 #define NET_SETCONVAR	5
    29 
    30 enum ConVarBounds
    31 {
    32 	ConVarBound_Upper = 0,
    33 	ConVarBound_Lower
    34 };
    35 
    36 static void ReplicateConVar(ConVar *pConVar)
    37 {
    38 	int maxClients = g_Players.GetMaxClients();
    39 
    40 	char data[256];
    41 	bf_write buffer(data, sizeof(data));
    42 
    43 	buffer.WriteUBitLong(NET_SETCONVAR, 5);
    44 	buffer.WriteByte(1);
    45 	buffer.WriteString(pConVar->GetName());
    46 	buffer.WriteString(pConVar->GetString());
    47 
    48 	for (int i = 1; i <= maxClients; i++)
    49 	{
    50 		CPlayer *pPlayer = g_Players.GetPlayerByIndex(i);
    51 
    52 		if (pPlayer && pPlayer->IsInGame() && !pPlayer->IsFakeClient())
    53 		{
    54 			INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(i));
    55 			netchan->SendData(buffer);
    56 		}
    57 	}
    58 }
    59 
    60 static void NotifyConVar(ConVar *pConVar)
    61 {
    62 	IGameEvent *pEvent = gameevents->CreateEvent("server_cvar");
    63 	pEvent->SetString("cvarname", pConVar->GetName());
    64 	if (pConVar->IsBitSet(FCVAR_PROTECTED))
    65 	{
    66 		pEvent->SetString("cvarvalue", "***PROTECTED***");
    67 	} else {
    68 		pEvent->SetString("cvarvalue", pConVar->GetString());
    69 	}
    70 
    71 	gameevents->FireEvent(pEvent);
    72 }
    73 
    74 static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
    75 {
    76 	char *name, *defaultVal, *helpText;
    77 
    78 	pContext->LocalToString(params[1], &name);
    79 
    80 	// While the engine seems to accept a blank convar name, it causes a crash upon server quit
    81 	if (name == NULL || strcmp(name, "") == 0)
    82 	{
    83 		return pContext->ThrowNativeError("Convar with blank name is not permitted");
    84 	}
    85 
    86 	pContext->LocalToString(params[2], &defaultVal);
    87 	pContext->LocalToString(params[3], &helpText);
    88 
    89 	bool hasMin = params[5] ? true : false;
    90 	bool hasMax = params[7] ? true : false;
    91 	float min = sp_ctof(params[6]);
    92 	float max = sp_ctof(params[8]);
    93 
    94 	Handle_t hndl = g_ConVarManager.CreateConVar(pContext, name, defaultVal, helpText, params[4], hasMin, min, hasMax, max);
    95 
    96 	if (hndl == BAD_HANDLE)
    97 	{
    98 		return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same name already exists.", name);
    99 	}
   100 
   101 	return hndl;
   102 }
   103 
   104 static cell_t sm_FindConVar(IPluginContext *pContext, const cell_t *params)
   105 {
   106 	char *name;
   107 
   108 	pContext->LocalToString(params[1], &name);
   109 
   110 	return g_ConVarManager.FindConVar(name);
   111 }
   112 
   113 static cell_t sm_HookConVarChange(IPluginContext *pContext, const cell_t *params)
   114 {
   115 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   116 	HandleError err;
   117 	ConVar *pConVar;
   118 
   119 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   120 		!= HandleError_None)
   121 	{
   122 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   123 	}
   124 
   125 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
   126 
   127 	if (!pFunction)
   128 	{
   129 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   130 	}
   131 
   132 	g_ConVarManager.HookConVarChange(pConVar, pFunction);
   133 
   134 	return 1;
   135 }
   136 
   137 static cell_t sm_UnhookConVarChange(IPluginContext *pContext, const cell_t *params)
   138 {
   139 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   140 	HandleError err;
   141 	ConVar *pConVar;
   142 
   143 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   144 		!= HandleError_None)
   145 	{
   146 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   147 	}
   148 
   149 	IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
   150 
   151 	if (!pFunction)
   152 	{
   153 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   154 	}
   155 
   156 	g_ConVarManager.UnhookConVarChange(pConVar, pFunction);
   157 
   158 	return 1;
   159 }
   160 
   161 static cell_t sm_GetConVarBool(IPluginContext *pContext, const cell_t *params)
   162 {
   163 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   164 	HandleError err;
   165 	ConVar *pConVar;
   166 
   167 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   168 		!= HandleError_None)
   169 	{
   170 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   171 	}
   172 
   173 	return pConVar->GetBool();
   174 }
   175 
   176 static cell_t sm_GetConVarInt(IPluginContext *pContext, const cell_t *params)
   177 {
   178 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   179 	HandleError err;
   180 	ConVar *pConVar;
   181 
   182 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   183 		!= HandleError_None)
   184 	{
   185 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   186 	}
   187 
   188 	return pConVar->GetInt();
   189 }
   190 
   191 /* This handles both SetConVarBool() and SetConVarInt() */
   192 static cell_t sm_SetConVarNum(IPluginContext *pContext, const cell_t *params)
   193 {
   194 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   195 	HandleError err;
   196 	ConVar *pConVar;
   197 
   198 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   199 		!= HandleError_None)
   200 	{
   201 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   202 	}
   203 
   204 	pConVar->SetValue(params[2]);
   205 
   206 	/* Should we replicate it? */
   207 	if (params[3] && pConVar->IsBitSet(FCVAR_REPLICATED))
   208 	{
   209 		ReplicateConVar(pConVar);
   210 	}
   211 
   212 	/* Should we notify clients? */
   213 	if (params[4] && pConVar->IsBitSet(FCVAR_NOTIFY))
   214 	{
   215 		NotifyConVar(pConVar);
   216 	}
   217 
   218 	return 1;
   219 }
   220 
   221 static cell_t sm_GetConVarFloat(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 	float value = pConVar->GetFloat();
   234 
   235 	return sp_ftoc(value);
   236 }
   237 
   238 static cell_t sm_SetConVarFloat(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 	float value = sp_ctof(params[2]);
   251 	pConVar->SetValue(value);
   252 
   253 	/* Should we replicate it? */
   254 	if (params[3] && pConVar->IsBitSet(FCVAR_REPLICATED))
   255 	{
   256 		ReplicateConVar(pConVar);
   257 	}
   258 
   259 	/* Should we notify clients? */
   260 	if (params[4] && pConVar->IsBitSet(FCVAR_NOTIFY))
   261 	{
   262 		NotifyConVar(pConVar);
   263 	}
   264 
   265 	return 1;
   266 }
   267 
   268 static cell_t sm_GetConVarString(IPluginContext *pContext, const cell_t *params)
   269 {
   270 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   271 	HandleError err;
   272 	ConVar *pConVar;
   273 
   274 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   275 		!= HandleError_None)
   276 	{
   277 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   278 	}
   279 
   280 	pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetString(), NULL);
   281 
   282 	return 1;
   283 }
   284 
   285 static cell_t sm_SetConVarString(IPluginContext *pContext, const cell_t *params)
   286 {
   287 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   288 	HandleError err;
   289 	ConVar *pConVar;
   290 
   291 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   292 		!= HandleError_None)
   293 	{
   294 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   295 	}
   296 
   297 	char *value;
   298 	pContext->LocalToString(params[2], &value);
   299 
   300 	pConVar->SetValue(value);
   301 
   302 	/* Should we replicate it? */
   303 	if (params[3] && pConVar->IsBitSet(FCVAR_REPLICATED))
   304 	{
   305 		ReplicateConVar(pConVar);
   306 	}
   307 
   308 	/* Should we notify clients? */
   309 	if (params[4] && pConVar->IsBitSet(FCVAR_NOTIFY))
   310 	{
   311 		NotifyConVar(pConVar);
   312 	}
   313 
   314 	return 1;
   315 }
   316 
   317 static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params)
   318 {
   319 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   320 	HandleError err;
   321 	ConVar *pConVar;
   322 
   323 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   324 		!= HandleError_None)
   325 	{
   326 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   327 	}
   328 
   329 	return pConVar->GetFlags();
   330 }
   331 
   332 static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params)
   333 {
   334 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   335 	HandleError err;
   336 	ConVar *pConVar;
   337 
   338 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   339 		!= HandleError_None)
   340 	{
   341 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   342 	}
   343 
   344 	pConVar->SetFlags(params[2]);
   345 
   346 	return 1;
   347 }
   348 
   349 static cell_t sm_GetConVarBounds(IPluginContext *pContext, const cell_t *params)
   350 {
   351 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   352 	HandleError err;
   353 	ConVar *pConVar;
   354 
   355 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   356 		!= HandleError_None)
   357 	{
   358 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   359 	}
   360 
   361 	cell_t *addr;
   362 	bool hasBound;
   363 	float bound;
   364 
   365 	switch (params[2])
   366 	{
   367 	case ConVarBound_Upper:
   368 		hasBound = pConVar->GetMax(bound);
   369 		break;
   370 	case ConVarBound_Lower:
   371 		hasBound = pConVar->GetMin(bound);
   372 		break;
   373 	default:
   374 		return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
   375 	}
   376 	
   377 	pContext->LocalToPhysAddr(params[3], &addr);
   378 	*addr = sp_ftoc(bound);
   379 
   380 	return hasBound;
   381 }
   382 
   383 static cell_t sm_SetConVarBounds(IPluginContext *pContext, const cell_t *params)
   384 {
   385 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   386 	HandleError err;
   387 	ConVar *pConVar;
   388 
   389 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   390 		!= HandleError_None)
   391 	{
   392 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   393 	}
   394 
   395 	switch (params[2])
   396 	{
   397 	case ConVarBound_Upper:
   398 		pConVar->SetMax(params[3] ? true : false, sp_ctof(params[4]));
   399 		break;
   400 	case ConVarBound_Lower:
   401 		pConVar->SetMin(params[3] ? true : false, sp_ctof(params[4]));
   402 		break;
   403 	default:
   404 		return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
   405 	}
   406 
   407 	return 1;
   408 }
   409 
   410 static cell_t sm_GetConVarName(IPluginContext *pContext, const cell_t *params)
   411 {
   412 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   413 	HandleError err;
   414 	ConVar *pConVar;
   415 
   416 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   417 		!= HandleError_None)
   418 	{
   419 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   420 	}
   421 
   422 	pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetName(), NULL);
   423 
   424 	return 1;
   425 }
   426 
   427 static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
   428 {
   429 	Handle_t hndl = static_cast<Handle_t>(params[1]);
   430 	HandleError err;
   431 	ConVar *pConVar;
   432 
   433 	if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
   434 		!= HandleError_None)
   435 	{
   436 		return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
   437 	}
   438 
   439 	pConVar->Revert();
   440 
   441 	return 1;
   442 }
   443 
   444 static bool s_QueryAlreadyWarned = false;
   445 
   446 static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params)
   447 {
   448 	CPlayer *pPlayer;
   449 	char *name;
   450 	IPluginFunction *pCallback;
   451 
   452 	if (g_IsOriginalEngine)
   453 	{
   454 		if (!s_QueryAlreadyWarned)
   455 		{
   456 			s_QueryAlreadyWarned = true;
   457 			return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)");
   458 		}
   459 
   460 		return 0;
   461 	}
   462 
   463 	pPlayer = g_Players.GetPlayerByIndex(params[1]);
   464 
   465 	if (!pPlayer)
   466 	{
   467 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   468 	}
   469 
   470 	if (!pPlayer->IsConnected())
   471 	{
   472 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   473 	}
   474 
   475 	/* Trying a query on a bot results in callback not be fired, so don't bother */
   476 	if (pPlayer->IsFakeClient())
   477 	{
   478 		return 0;
   479 	}
   480 
   481 	pContext->LocalToString(params[2], &name);
   482 	pCallback = pContext->GetFunctionById(params[3]);
   483 
   484 	if (!pCallback)
   485 	{
   486 		return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
   487 	}
   488 
   489 	return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]);
   490 }
   491 
   492 static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
   493 {
   494 	char *name,*help;
   495 	IPluginFunction *pFunction;
   496 
   497 	pContext->LocalToString(params[1], &name);
   498 	pContext->LocalToString(params[3], &help);
   499 	pFunction = pContext->GetFunctionById(params[2]);
   500 
   501 	if (!pFunction)
   502 	{
   503 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   504 	}
   505 
   506 	if (!g_ConCmds.AddServerCommand(pFunction, name, help, params[4]))
   507 	{
   508 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   509 	}
   510 
   511 	return 1;
   512 }
   513 
   514 static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params)
   515 {
   516 	char *name,*help;
   517 	IPluginFunction *pFunction;
   518 
   519 	pContext->LocalToString(params[1], &name);
   520 	pContext->LocalToString(params[3], &help);
   521 	pFunction = pContext->GetFunctionById(params[2]);
   522 
   523 	if (!pFunction)
   524 	{
   525 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   526 	}
   527 
   528 	if (!g_ConCmds.AddConsoleCommand(pFunction, name, help, params[4]))
   529 	{
   530 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   531 	}
   532 
   533 	return 1;
   534 }
   535 
   536 static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
   537 {
   538 	char *name,*help;
   539 	const char *group;
   540 	IPluginFunction *pFunction;
   541 	FlagBits flags = params[3];
   542 	int cmdflags = params[6];
   543 
   544 	pContext->LocalToString(params[1], &name);
   545 	pContext->LocalToString(params[4], &help);
   546 	pContext->LocalToString(params[5], (char **)&group);
   547 	pFunction = pContext->GetFunctionById(params[2]);
   548 
   549 	if (group[0] == '\0')
   550 	{
   551 		CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
   552 		group = pPlugin->GetFilename();
   553 	}
   554 
   555 	if (!pFunction)
   556 	{
   557 		return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
   558 	}
   559 
   560 	if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags))
   561 	{
   562 		return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
   563 	}
   564 
   565 	return 1;
   566 }
   567 
   568 static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
   569 {
   570 	return engine->Cmd_Argc() - 1;
   571 }
   572 
   573 static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params)
   574 {
   575 	const char *arg = engine->Cmd_Argv(params[1]);
   576 	
   577 	pContext->StringToLocalUTF8(params[2], params[3], arg, NULL);
   578 
   579 	return 1;
   580 }
   581 
   582 static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params)
   583 {
   584 	const char *args = engine->Cmd_Args();
   585 
   586 	if (!args)
   587 	{
   588 		args = "";
   589 	}
   590 
   591 	pContext->StringToLocalUTF8(params[1], params[2], args, NULL);
   592 
   593 	return 1;
   594 }
   595 
   596 static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params)
   597 {
   598 	char buffer[1024];
   599 	char *fmt;
   600 	int arg = 2;
   601 
   602 	pCtx->LocalToString(params[1], &fmt);
   603 	size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
   604 
   605 	buffer[res++] = '\n';
   606 	buffer[res] = '\0';
   607 
   608 	META_CONPRINT(buffer);
   609 
   610 	return 1;
   611 }
   612 
   613 static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
   614 {
   615 	int index = params[1];
   616 	if ((index < 0) || (index > g_Players.GetMaxClients()))
   617 	{
   618 		return pCtx->ThrowNativeError("Client index %d is invalid", index);
   619 	}
   620 
   621 	CPlayer *pPlayer = NULL;
   622 	if (index != 0)
   623 	{
   624 		pPlayer = g_Players.GetPlayerByIndex(index);
   625 		if (!pPlayer->IsInGame())
   626 		{
   627 			return pCtx->ThrowNativeError("Client %d is not in game", index);
   628 		}
   629 		
   630 		/* Silent fail on bots, engine will crash */
   631 		if (pPlayer->IsFakeClient())
   632 		{
   633 			return 0;
   634 		}
   635 	}
   636 
   637 	char buffer[1024];
   638 	char *fmt;
   639 	int arg = 3;
   640 
   641 	pCtx->LocalToString(params[2], &fmt);
   642 	size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
   643 
   644 	buffer[res++] = '\n';
   645 	buffer[res] = '\0';
   646 
   647 	if (index != 0)
   648 	{
   649 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
   650 	} else {
   651 		META_CONPRINT(buffer);
   652 	}
   653 
   654 	return 1;
   655 }
   656 
   657 static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
   658 {
   659 	char buffer[1024];
   660 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
   661 
   662 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   663 
   664 	/* One byte for null terminator, one for newline */
   665 	buffer[len++] = '\n';
   666 	buffer[len] = '\0';
   667 
   668 	engine->ServerCommand(buffer);
   669 
   670 	return 1;
   671 }
   672 
   673 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
   674 {
   675 	char buffer[1024];
   676 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
   677 
   678 	g_SourceMod.SetGlobalTarget(LANG_SERVER);
   679 
   680 	/* One byte for null terminator, one for newline */
   681 	buffer[len++] = '\n';
   682 	buffer[len] = '\0';
   683 
   684 	engine->InsertServerCommand(buffer);
   685 
   686 	return 1;
   687 }
   688 
   689 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
   690 {
   691 	engine->ServerExecute();
   692 
   693 	return 1;
   694 }
   695 
   696 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
   697 {
   698 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   699 
   700 	if (!pPlayer)
   701 	{
   702 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   703 	}
   704 
   705 	if (!pPlayer->IsConnected())
   706 	{
   707 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   708 	}
   709 
   710 	g_SourceMod.SetGlobalTarget(params[1]);
   711 
   712 	char buffer[256];
   713 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   714 
   715 	engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
   716 
   717 	return 1;
   718 }
   719 
   720 
   721 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
   722 {
   723 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   724 
   725 	if (!pPlayer)
   726 	{
   727 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   728 	}
   729 
   730 	if (!pPlayer->IsConnected())
   731 	{
   732 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   733 	}
   734 
   735 	char buffer[256];
   736 	g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
   737 
   738 	unsigned int old = g_ChatTriggers.SetReplyTo(SM_REPLY_CONSOLE);
   739 	serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
   740 	g_ChatTriggers.SetReplyTo(old);
   741 
   742 	return 1;
   743 }
   744 
   745 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
   746 {
   747 	g_SourceMod.SetGlobalTarget(params[1]);
   748 
   749 	/* Build the format string */
   750 	char buffer[1024];
   751 	size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
   752 
   753 	/* If we're printing to the server, shortcut out */
   754 	if (params[1] == 0)
   755 	{
   756 		/* Print */
   757 		buffer[len++] = '\n';
   758 		buffer[len] = '\0';
   759 		META_CONPRINT(buffer);
   760 		return 1;
   761 	}
   762 
   763 	CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
   764 
   765 	if (!pPlayer)
   766 	{
   767 		return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
   768 	}
   769 
   770 	if (!pPlayer->IsConnected())
   771 	{
   772 		return pContext->ThrowNativeError("Client %d is not connected", params[1]);
   773 	}
   774 
   775 	g_SourceMod.SetGlobalTarget(params[1]);
   776 
   777 	unsigned int replyto = g_ChatTriggers.GetReplyTo();
   778 	if (replyto == SM_REPLY_CONSOLE)
   779 	{
   780 		buffer[len++] = '\n';
   781 		buffer[len] = '\0';
   782 		engine->ClientPrintf(pPlayer->GetEdict(), buffer);
   783 	} else if (replyto == SM_REPLY_CHAT) {
   784 		if (len >= 191)
   785 		{
   786 			len = 191;
   787 		}
   788 		buffer[len] = '\0';
   789 		g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
   790 	}
   791 
   792 	return 1;
   793 }
   794 
   795 static cell_t GetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
   796 {
   797 	return g_ChatTriggers.GetReplyTo();
   798 }
   799 
   800 REGISTER_NATIVES(consoleNatives)
   801 {
   802 	{"CreateConVar",		sm_CreateConVar},
   803 	{"FindConVar",			sm_FindConVar},
   804 	{"HookConVarChange",	sm_HookConVarChange},
   805 	{"UnhookConVarChange",	sm_UnhookConVarChange},
   806 	{"GetConVarBool",		sm_GetConVarBool},
   807 	{"SetConVarBool",		sm_SetConVarNum},
   808 	{"GetConVarInt",		sm_GetConVarInt},
   809 	{"SetConVarInt",		sm_SetConVarNum},
   810 	{"GetConVarFloat",		sm_GetConVarFloat},
   811 	{"SetConVarFloat",		sm_SetConVarFloat},
   812 	{"GetConVarString",		sm_GetConVarString},
   813 	{"SetConVarString",		sm_SetConVarString},
   814 	{"GetConVarFlags",		sm_GetConVarFlags},
   815 	{"SetConVarFlags",		sm_SetConVarFlags},
   816 	{"GetConVarName",		sm_GetConVarName},
   817 	{"GetConVarBounds",		sm_GetConVarBounds},
   818 	{"SetConVarBounds",		sm_SetConVarBounds},
   819 	{"ResetConVar",			sm_ResetConVar},
   820 	{"QueryClientConVar",	sm_QueryClientConVar},
   821 	{"RegServerCmd",		sm_RegServerCmd},
   822 	{"RegConsoleCmd",		sm_RegConsoleCmd},
   823 	{"GetCmdArgString",		sm_GetCmdArgString},
   824 	{"GetCmdArgs",			sm_GetCmdArgs},
   825 	{"GetCmdArg",			sm_GetCmdArg},
   826 	{"PrintToServer",		sm_PrintToServer},
   827 	{"PrintToConsole",		sm_PrintToConsole},
   828 	{"RegAdminCmd",			sm_RegAdminCmd},
   829 	{"ServerCommand",		sm_ServerCommand},
   830 	{"InsertServerCommand",	sm_InsertServerCommand},
   831 	{"ServerExecute",		sm_ServerExecute},
   832 	{"ClientCommand",		sm_ClientCommand},
   833 	{"FakeClientCommand",	FakeClientCommand},
   834 	{"ReplyToCommand",		ReplyToCommand},
   835 	{"GetCmdReplySource",	GetCmdReplyTarget},
   836 	{NULL,					NULL}
   837 };