3 * ===============================================================
4 * SourceMod (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ===============================================================
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
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"
28 ConVarBound_Upper = 0,
32 #define HUD_PRINTTALK 3
34 static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
36 char *name, *defaultVal, *helpText;
38 pContext->LocalToString(params[1], &name);
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)
43 return pContext->ThrowNativeError("Convar with blank name is not permitted");
46 pContext->LocalToString(params[2], &defaultVal);
47 pContext->LocalToString(params[3], &helpText);
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]);
54 Handle_t hndl = g_ConVarManager.CreateConVar(pContext, name, defaultVal, helpText, params[4], hasMin, min, hasMax, max);
56 if (hndl == BAD_HANDLE)
58 return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same name already exists.", name);
64 static cell_t sm_FindConVar(IPluginContext *pContext, const cell_t *params)
68 pContext->LocalToString(params[1], &name);
70 return g_ConVarManager.FindConVar(name);
73 static cell_t sm_HookConVarChange(IPluginContext *pContext, const cell_t *params)
75 Handle_t hndl = static_cast<Handle_t>(params[1]);
79 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
82 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
85 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
89 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
92 g_ConVarManager.HookConVarChange(pConVar, pFunction);
97 static cell_t sm_UnhookConVarChange(IPluginContext *pContext, const cell_t *params)
99 Handle_t hndl = static_cast<Handle_t>(params[1]);
103 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
106 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
109 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
113 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
116 g_ConVarManager.UnhookConVarChange(pConVar, pFunction);
121 static cell_t sm_GetConVarBool(IPluginContext *pContext, const cell_t *params)
123 Handle_t hndl = static_cast<Handle_t>(params[1]);
127 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
130 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
133 return pConVar->GetBool();
136 static cell_t sm_GetConVarInt(IPluginContext *pContext, const cell_t *params)
138 Handle_t hndl = static_cast<Handle_t>(params[1]);
142 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
145 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
148 return pConVar->GetInt();
151 /* This handles both SetConVarBool() and SetConVarInt() */
152 static cell_t sm_SetConVarNum(IPluginContext *pContext, const cell_t *params)
154 Handle_t hndl = static_cast<Handle_t>(params[1]);
158 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
161 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
164 pConVar->SetValue(params[2]);
169 static cell_t sm_GetConVarFloat(IPluginContext *pContext, const cell_t *params)
171 Handle_t hndl = static_cast<Handle_t>(params[1]);
175 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
178 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
181 float value = pConVar->GetFloat();
183 return sp_ftoc(value);
186 static cell_t sm_SetConVarFloat(IPluginContext *pContext, const cell_t *params)
188 Handle_t hndl = static_cast<Handle_t>(params[1]);
192 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
195 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
198 float value = sp_ctof(params[2]);
199 pConVar->SetValue(value);
204 static cell_t sm_GetConVarString(IPluginContext *pContext, const cell_t *params)
206 Handle_t hndl = static_cast<Handle_t>(params[1]);
210 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
213 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
216 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetString(), NULL);
221 static cell_t sm_SetConVarString(IPluginContext *pContext, const cell_t *params)
223 Handle_t hndl = static_cast<Handle_t>(params[1]);
227 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
230 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
234 pContext->LocalToString(params[2], &value);
236 pConVar->SetValue(value);
241 static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params)
243 Handle_t hndl = static_cast<Handle_t>(params[1]);
247 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
250 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
253 return pConVar->GetFlags();
256 static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params)
258 Handle_t hndl = static_cast<Handle_t>(params[1]);
262 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
265 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
268 pConVar->SetFlags(params[2]);
273 static cell_t sm_GetConVarBounds(IPluginContext *pContext, const cell_t *params)
275 Handle_t hndl = static_cast<Handle_t>(params[1]);
279 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
282 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
291 case ConVarBound_Upper:
292 hasBound = pConVar->GetMax(bound);
294 case ConVarBound_Lower:
295 hasBound = pConVar->GetMin(bound);
298 return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
301 pContext->LocalToPhysAddr(params[3], &addr);
302 *addr = sp_ftoc(bound);
307 static cell_t sm_SetConVarBounds(IPluginContext *pContext, const cell_t *params)
309 Handle_t hndl = static_cast<Handle_t>(params[1]);
313 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
316 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
321 case ConVarBound_Upper:
322 pConVar->SetMax(params[3] ? true : false, sp_ctof(params[4]));
324 case ConVarBound_Lower:
325 pConVar->SetMin(params[3] ? true : false, sp_ctof(params[4]));
328 return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
334 static cell_t sm_GetConVarName(IPluginContext *pContext, const cell_t *params)
336 Handle_t hndl = static_cast<Handle_t>(params[1]);
340 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
343 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
346 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetName(), NULL);
351 static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
353 Handle_t hndl = static_cast<Handle_t>(params[1]);
357 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
360 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
368 static bool s_QueryAlreadyWarned = false;
370 static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params)
374 IPluginFunction *pCallback;
376 if (g_IsOriginalEngine)
378 if (!s_QueryAlreadyWarned)
380 s_QueryAlreadyWarned = true;
381 return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)");
387 pPlayer = g_Players.GetPlayerByIndex(params[1]);
391 return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
394 if (!pPlayer->IsConnected())
396 return pContext->ThrowNativeError("Player %d is not connected", params[1]);
399 /* Trying a query on a bot results in callback not be fired, so don't bother */
400 if (pPlayer->IsFakeClient())
405 pContext->LocalToString(params[2], &name);
406 pCallback = pContext->GetFunctionById(params[3]);
410 return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
413 return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]);
416 static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
419 IPluginFunction *pFunction;
421 pContext->LocalToString(params[1], &name);
422 pContext->LocalToString(params[3], &help);
423 pFunction = pContext->GetFunctionById(params[2]);
427 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
430 if (!g_ConCmds.AddServerCommand(pFunction, name, help, params[4]))
432 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
438 static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params)
441 IPluginFunction *pFunction;
443 pContext->LocalToString(params[1], &name);
444 pContext->LocalToString(params[3], &help);
445 pFunction = pContext->GetFunctionById(params[2]);
449 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
452 if (!g_ConCmds.AddConsoleCommand(pFunction, name, help, params[4]))
454 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
460 static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
464 IPluginFunction *pFunction;
465 FlagBits flags = params[3];
466 int cmdflags = params[6];
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]);
473 if (group[0] == '\0')
475 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
476 group = pPlugin->GetFilename();
481 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
484 if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags))
486 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
492 static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
494 return engine->Cmd_Argc() - 1;
497 static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params)
499 const char *arg = engine->Cmd_Argv(params[1]);
501 pContext->StringToLocalUTF8(params[2], params[3], arg, NULL);
506 static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params)
508 const char *args = engine->Cmd_Args();
515 pContext->StringToLocalUTF8(params[1], params[2], args, NULL);
520 static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params)
526 pCtx->LocalToString(params[1], &fmt);
527 size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
529 buffer[res++] = '\n';
532 META_CONPRINT(buffer);
537 static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
539 int index = params[1];
540 if ((index < 0) || (index > g_Players.GetMaxClients()))
542 return pCtx->ThrowNativeError("Invalid client index %d", index);
545 CPlayer *pPlayer = NULL;
548 pPlayer = g_Players.GetPlayerByIndex(index);
549 if (!pPlayer->IsInGame())
551 return pCtx->ThrowNativeError("Client %d is not in game", index);
554 /* Silent fail on bots, engine will crash */
555 if (pPlayer->IsFakeClient())
565 pCtx->LocalToString(params[2], &fmt);
566 size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
568 buffer[res++] = '\n';
573 engine->ClientPrintf(pPlayer->GetEdict(), buffer);
575 META_CONPRINT(buffer);
581 static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
584 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
586 /* One byte for null terminator, one for newline */
587 buffer[len++] = '\n';
590 engine->ServerCommand(buffer);
595 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
598 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
600 /* One byte for null terminator, one for newline */
601 buffer[len++] = '\n';
604 engine->InsertServerCommand(buffer);
609 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
611 engine->ServerExecute();
616 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
618 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
622 return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
625 if (!pPlayer->IsConnected())
627 return pContext->ThrowNativeError("Player %d is not connected", params[1]);
631 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
633 engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
639 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
641 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
645 return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
648 if (!pPlayer->IsConnected())
650 return pContext->ThrowNativeError("Player %d is not connected", params[1]);
654 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
656 unsigned int old = g_ChatTriggers.SetReplyTo(SM_REPLY_CONSOLE);
657 serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
658 g_ChatTriggers.SetReplyTo(old);
663 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
665 /* Build the format string */
667 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
669 /* If we're printing to the server, shortcut out */
673 buffer[len++] = '\n';
675 META_CONPRINT(buffer);
679 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
683 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
686 if (!pPlayer->IsConnected())
688 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
691 unsigned int replyto = g_ChatTriggers.GetReplyTo();
692 if (replyto == SM_REPLY_CONSOLE)
694 buffer[len++] = '\n';
696 engine->ClientPrintf(pPlayer->GetEdict(), buffer);
697 } else if (replyto == SM_REPLY_CHAT) {
703 g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
709 REGISTER_NATIVES(consoleNatives)
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},