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 static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
34 char *name, *defaultVal, *helpText;
36 pContext->LocalToString(params[1], &name);
38 // While the engine seems to accept a blank convar name, it causes a crash upon server quit
39 if (name == NULL || strcmp(name, "") == 0)
41 return pContext->ThrowNativeError("Convar with blank name is not permitted");
44 pContext->LocalToString(params[2], &defaultVal);
45 pContext->LocalToString(params[3], &helpText);
47 bool hasMin = params[5] ? true : false;
48 bool hasMax = params[7] ? true : false;
49 float min = sp_ctof(params[6]);
50 float max = sp_ctof(params[8]);
52 Handle_t hndl = g_ConVarManager.CreateConVar(pContext, name, defaultVal, helpText, params[4], hasMin, min, hasMax, max);
54 if (hndl == BAD_HANDLE)
56 return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same name already exists.", name);
62 static cell_t sm_FindConVar(IPluginContext *pContext, const cell_t *params)
66 pContext->LocalToString(params[1], &name);
68 return g_ConVarManager.FindConVar(name);
71 static cell_t sm_HookConVarChange(IPluginContext *pContext, const cell_t *params)
73 Handle_t hndl = static_cast<Handle_t>(params[1]);
77 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
80 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
83 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
87 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
90 g_ConVarManager.HookConVarChange(pConVar, pFunction);
95 static cell_t sm_UnhookConVarChange(IPluginContext *pContext, const cell_t *params)
97 Handle_t hndl = static_cast<Handle_t>(params[1]);
101 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
104 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
107 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
111 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
114 g_ConVarManager.UnhookConVarChange(pConVar, pFunction);
119 static cell_t sm_GetConVarBool(IPluginContext *pContext, const cell_t *params)
121 Handle_t hndl = static_cast<Handle_t>(params[1]);
125 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
128 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
131 return pConVar->GetBool();
134 static cell_t sm_GetConVarInt(IPluginContext *pContext, const cell_t *params)
136 Handle_t hndl = static_cast<Handle_t>(params[1]);
140 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
143 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
146 return pConVar->GetInt();
149 /* This handles both SetConVarBool() and SetConVarInt() */
150 static cell_t sm_SetConVarNum(IPluginContext *pContext, const cell_t *params)
152 Handle_t hndl = static_cast<Handle_t>(params[1]);
156 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
159 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
162 pConVar->SetValue(params[2]);
167 static cell_t sm_GetConVarFloat(IPluginContext *pContext, const cell_t *params)
169 Handle_t hndl = static_cast<Handle_t>(params[1]);
173 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
176 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
179 float value = pConVar->GetFloat();
181 return sp_ftoc(value);
184 static cell_t sm_SetConVarFloat(IPluginContext *pContext, const cell_t *params)
186 Handle_t hndl = static_cast<Handle_t>(params[1]);
190 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
193 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
196 float value = sp_ctof(params[2]);
197 pConVar->SetValue(value);
202 static cell_t sm_GetConVarString(IPluginContext *pContext, const cell_t *params)
204 Handle_t hndl = static_cast<Handle_t>(params[1]);
208 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
211 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
214 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetString(), NULL);
219 static cell_t sm_SetConVarString(IPluginContext *pContext, const cell_t *params)
221 Handle_t hndl = static_cast<Handle_t>(params[1]);
225 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
228 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
232 pContext->LocalToString(params[2], &value);
234 pConVar->SetValue(value);
239 static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params)
241 Handle_t hndl = static_cast<Handle_t>(params[1]);
245 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
248 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
251 return pConVar->GetFlags();
254 static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params)
256 Handle_t hndl = static_cast<Handle_t>(params[1]);
260 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
263 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
266 pConVar->SetFlags(params[2]);
271 static cell_t sm_GetConVarBounds(IPluginContext *pContext, const cell_t *params)
273 Handle_t hndl = static_cast<Handle_t>(params[1]);
277 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
280 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
289 case ConVarBound_Upper:
290 hasBound = pConVar->GetMax(bound);
292 case ConVarBound_Lower:
293 hasBound = pConVar->GetMin(bound);
296 return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
299 pContext->LocalToPhysAddr(params[3], &addr);
300 *addr = sp_ftoc(bound);
305 static cell_t sm_SetConVarBounds(IPluginContext *pContext, const cell_t *params)
307 Handle_t hndl = static_cast<Handle_t>(params[1]);
311 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
314 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
319 case ConVarBound_Upper:
320 pConVar->SetMax(params[3] ? true : false, sp_ctof(params[4]));
322 case ConVarBound_Lower:
323 pConVar->SetMin(params[3] ? true : false, sp_ctof(params[4]));
326 return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
332 static cell_t sm_GetConVarName(IPluginContext *pContext, const cell_t *params)
334 Handle_t hndl = static_cast<Handle_t>(params[1]);
338 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
341 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
344 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetName(), NULL);
349 static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
351 Handle_t hndl = static_cast<Handle_t>(params[1]);
355 if ((err=g_HandleSys.ReadHandle(hndl, g_ConVarManager.GetHandleType(), NULL, (void **)&pConVar))
358 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
366 static bool s_QueryAlreadyWarned = false;
368 static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params)
372 IPluginFunction *pCallback;
374 if (g_IsOriginalEngine)
376 if (!s_QueryAlreadyWarned)
378 s_QueryAlreadyWarned = true;
379 return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)");
385 pPlayer = g_Players.GetPlayerByIndex(params[1]);
389 return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
392 if (!pPlayer->IsConnected())
394 return pContext->ThrowNativeError("Player %d is not connected", params[1]);
397 /* Trying a query on a bot results in callback not be fired, so don't bother */
398 if (pPlayer->IsFakeClient())
403 pContext->LocalToString(params[2], &name);
404 pCallback = pContext->GetFunctionById(params[3]);
408 return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
411 return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]);
414 static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
417 IPluginFunction *pFunction;
419 pContext->LocalToString(params[1], &name);
420 pContext->LocalToString(params[3], &help);
421 pFunction = pContext->GetFunctionById(params[2]);
425 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
428 if (!g_ConCmds.AddServerCommand(pFunction, name, help, params[4]))
430 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
436 static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params)
439 IPluginFunction *pFunction;
441 pContext->LocalToString(params[1], &name);
442 pContext->LocalToString(params[3], &help);
443 pFunction = pContext->GetFunctionById(params[2]);
447 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
450 if (!g_ConCmds.AddConsoleCommand(pFunction, name, help, params[4]))
452 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
458 static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
462 IPluginFunction *pFunction;
463 FlagBits flags = params[3];
464 int cmdflags = params[6];
466 pContext->LocalToString(params[1], &name);
467 pContext->LocalToString(params[4], &help);
468 pContext->LocalToString(params[5], (char **)&group);
469 pFunction = pContext->GetFunctionById(params[2]);
471 if (group[0] == '\0')
473 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
474 group = pPlugin->GetFilename();
479 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
482 if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags))
484 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
490 static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
492 return engine->Cmd_Argc() - 1;
495 static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params)
497 const char *arg = engine->Cmd_Argv(params[1]);
499 pContext->StringToLocalUTF8(params[2], params[3], arg, NULL);
504 static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params)
506 const char *args = engine->Cmd_Args();
513 pContext->StringToLocalUTF8(params[1], params[2], args, NULL);
518 static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params)
524 pCtx->LocalToString(params[1], &fmt);
525 size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
527 buffer[res++] = '\n';
530 META_CONPRINT(buffer);
535 static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
537 int index = params[1];
538 if ((index < 0) || (index > g_Players.GetMaxClients()))
540 return pCtx->ThrowNativeError("Invalid client index %d", index);
543 CPlayer *pPlayer = NULL;
546 pPlayer = g_Players.GetPlayerByIndex(index);
547 if (!pPlayer->IsInGame())
549 return pCtx->ThrowNativeError("Client %d is not in game", index);
552 /* Silent fail on bots, engine will crash */
553 if (pPlayer->IsFakeClient())
563 pCtx->LocalToString(params[2], &fmt);
564 size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
566 buffer[res++] = '\n';
571 engine->ClientPrintf(pPlayer->GetEdict(), buffer);
573 META_CONPRINT(buffer);
579 static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
582 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
584 /* One byte for null terminator, one for newline */
585 buffer[len++] = '\n';
588 engine->ServerCommand(buffer);
593 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
596 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
598 /* One byte for null terminator, one for newline */
599 buffer[len++] = '\n';
602 engine->InsertServerCommand(buffer);
607 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
609 engine->ServerExecute();
614 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
616 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
620 return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
623 if (!pPlayer->IsConnected())
625 return pContext->ThrowNativeError("Player %d is not connected", params[1]);
629 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
631 engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
637 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
639 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
643 return pContext->ThrowNativeError("Player %d is not a valid player", params[1]);
646 if (!pPlayer->IsConnected())
648 return pContext->ThrowNativeError("Player %d is not connected", params[1]);
652 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
654 unsigned int old = g_ChatTriggers.SetReplyTo(SM_REPLY_CONSOLE);
655 serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
656 g_ChatTriggers.SetReplyTo(old);
661 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
663 /* Build the format string */
665 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
667 /* If we're printing to the server, shortcut out */
671 buffer[len++] = '\n';
673 META_CONPRINT(buffer);
677 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
681 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
684 if (!pPlayer->IsConnected())
686 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
689 unsigned int replyto = g_ChatTriggers.GetReplyTo();
690 if (replyto == SM_REPLY_CONSOLE)
692 buffer[len++] = '\n';
694 engine->ClientPrintf(pPlayer->GetEdict(), buffer);
695 } else if (replyto == SM_REPLY_CHAT) {
701 g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
707 REGISTER_NATIVES(consoleNatives)
709 {"CreateConVar", sm_CreateConVar},
710 {"FindConVar", sm_FindConVar},
711 {"HookConVarChange", sm_HookConVarChange},
712 {"UnhookConVarChange", sm_UnhookConVarChange},
713 {"GetConVarBool", sm_GetConVarBool},
714 {"SetConVarBool", sm_SetConVarNum},
715 {"GetConVarInt", sm_GetConVarInt},
716 {"SetConVarInt", sm_SetConVarNum},
717 {"GetConVarFloat", sm_GetConVarFloat},
718 {"SetConVarFloat", sm_SetConVarFloat},
719 {"GetConVarString", sm_GetConVarString},
720 {"SetConVarString", sm_SetConVarString},
721 {"GetConVarFlags", sm_GetConVarFlags},
722 {"SetConVarFlags", sm_SetConVarFlags},
723 {"GetConVarName", sm_GetConVarName},
724 {"GetConVarBounds", sm_GetConVarBounds},
725 {"SetConVarBounds", sm_SetConVarBounds},
726 {"ResetConVar", sm_ResetConVar},
727 {"QueryClientConVar", sm_QueryClientConVar},
728 {"RegServerCmd", sm_RegServerCmd},
729 {"RegConsoleCmd", sm_RegConsoleCmd},
730 {"GetCmdArgString", sm_GetCmdArgString},
731 {"GetCmdArgs", sm_GetCmdArgs},
732 {"GetCmdArg", sm_GetCmdArg},
733 {"PrintToServer", sm_PrintToServer},
734 {"PrintToConsole", sm_PrintToConsole},
735 {"RegAdminCmd", sm_RegAdminCmd},
736 {"ServerCommand", sm_ServerCommand},
737 {"InsertServerCommand", sm_InsertServerCommand},
738 {"ServerExecute", sm_ServerExecute},
739 {"ClientCommand", sm_ClientCommand},
740 {"FakeClientCommand", FakeClientCommand},
741 {"ReplyToCommand", ReplyToCommand},