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