3 * =============================================================================
5 * Copyright (C) 2004-2010 AlliedModders LLC. All rights reserved.
6 * =============================================================================
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.
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
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/>.
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>.
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 "AdminCache.h"
43 #include <inetchannel.h>
45 #include <sm_trie_tpl.h>
47 #include "ConsoleDetours.h"
49 #if SOURCE_ENGINE >= SE_ORANGEBOXVALVE
55 #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
56 #define NET_SETCONVAR 6
58 #define NET_SETCONVAR 5
63 ConVarBound_Upper = 0,
67 HandleType_t hCmdIterType = 0;
68 HandleType_t htConCmdIter = 0;
73 List<ConCmdInfo *>::iterator iter;
78 #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
79 ICvarIteratorInternal *pLast;
81 const ConCommandBase *pLast;
85 class ConsoleHelpers :
87 public IHandleTypeDispatch
90 virtual void OnSourceModAllInitialized()
94 g_HandleSys.InitAccessDefaults(NULL, &access);
96 htConCmdIter = g_HandleSys.CreateType("ConCmdIter", this, 0, NULL, &access, g_pCoreIdent, NULL);
98 access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER | HANDLE_RESTRICT_IDENTITY;
100 hCmdIterType = g_HandleSys.CreateType("CmdIter", this, 0, NULL, &access, g_pCoreIdent, NULL);
102 virtual void OnHandleDestroy(HandleType_t type, void *object)
104 if (type == hCmdIterType)
106 GlobCmdIter *iter = (GlobCmdIter *)object;
109 else if (type == htConCmdIter)
111 ConCmdIter *iter = (ConCmdIter * )object;
112 #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
113 // ICvarIteratorInternal has no virtual destructor
114 g_pMemAlloc->Free(iter->pLast);
119 virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
121 if (type == htConCmdIter)
123 *pSize = sizeof(ConCmdIter);
126 else if (type == hCmdIterType)
128 *pSize = sizeof(GlobCmdIter);
135 class CommandFlagsHelper : public IConCommandTracker
138 void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
140 m_CmdFlags.remove(name);
142 bool GetFlags(const char *name, int *flags)
144 ConCommandBase **ppCmd;
145 ConCommandBase *pCmd;
146 if ((ppCmd=m_CmdFlags.retrieve(name)))
148 TrackConCommandBase((*ppCmd), this);
149 *flags = (*ppCmd)->GetFlags();
152 else if ((pCmd=FindCommandBase(name)))
154 m_CmdFlags.insert(name, pCmd);
155 TrackConCommandBase(pCmd, this);
156 *flags = pCmd->GetFlags();
164 bool SetFlags(const char *name, int flags)
166 ConCommandBase **ppCmd;
167 ConCommandBase *pCmd;
168 if ((ppCmd=m_CmdFlags.retrieve(name)))
170 (*ppCmd)->SetFlags(flags);
171 TrackConCommandBase((*ppCmd), this);
174 else if ((pCmd=FindCommandBase(name)))
176 m_CmdFlags.insert(name, pCmd);
177 pCmd->SetFlags(flags);
178 TrackConCommandBase(pCmd, this);
187 KTrie<ConCommandBase *> m_CmdFlags;
188 } s_CommandFlagsHelper;
190 #if SOURCE_ENGINE >= SE_ORANGEBOX
191 static void ReplicateConVar(ConVar *pConVar)
193 int maxClients = g_Players.GetMaxClients();
196 bf_write buffer(data, sizeof(data));
198 buffer.WriteUBitLong(NET_SETCONVAR, NETMSG_BITS);
200 buffer.WriteString(pConVar->GetName());
201 buffer.WriteString(pConVar->GetString());
203 for (int i = 1; i <= maxClients; i++)
205 CPlayer *pPlayer = g_Players.GetPlayerByIndex(i);
207 if (pPlayer && pPlayer->IsInGame() && !pPlayer->IsFakeClient())
209 if (INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(i)))
210 netchan->SendData(buffer);
215 static void NotifyConVar(ConVar *pConVar)
217 IGameEvent *pEvent = gameevents->CreateEvent("server_cvar");
218 pEvent->SetString("cvarname", pConVar->GetName());
219 if (IsFlagSet(pConVar, FCVAR_PROTECTED))
221 pEvent->SetString("cvarvalue", "***PROTECTED***");
225 pEvent->SetString("cvarvalue", pConVar->GetString());
228 gameevents->FireEvent(pEvent);
232 static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
234 char *name, *defaultVal, *helpText;
236 pContext->LocalToString(params[1], &name);
238 // While the engine seems to accept a blank convar name, it causes a crash upon server quit
239 if (name == NULL || strcmp(name, "") == 0)
241 return pContext->ThrowNativeError("Convar with blank name is not permitted");
244 pContext->LocalToString(params[2], &defaultVal);
245 pContext->LocalToString(params[3], &helpText);
247 bool hasMin = params[5] ? true : false;
248 bool hasMax = params[7] ? true : false;
249 float min = sp_ctof(params[6]);
250 float max = sp_ctof(params[8]);
252 Handle_t hndl = g_ConVarManager.CreateConVar(pContext, name, defaultVal, helpText, params[4], hasMin, min, hasMax, max);
254 if (hndl == BAD_HANDLE)
256 return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same might already exist.", name);
262 static cell_t sm_FindConVar(IPluginContext *pContext, const cell_t *params)
266 pContext->LocalToString(params[1], &name);
268 return g_ConVarManager.FindConVar(name);
271 static cell_t sm_HookConVarChange(IPluginContext *pContext, const cell_t *params)
273 Handle_t hndl = static_cast<Handle_t>(params[1]);
277 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
280 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
283 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
287 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
290 g_ConVarManager.HookConVarChange(pConVar, pFunction);
295 static cell_t sm_UnhookConVarChange(IPluginContext *pContext, const cell_t *params)
297 Handle_t hndl = static_cast<Handle_t>(params[1]);
301 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
304 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
307 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
311 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
314 g_ConVarManager.UnhookConVarChange(pConVar, pFunction);
319 static cell_t sm_GetConVarBool(IPluginContext *pContext, const cell_t *params)
321 Handle_t hndl = static_cast<Handle_t>(params[1]);
325 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
328 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
331 return pConVar->GetBool();
334 static cell_t sm_GetConVarInt(IPluginContext *pContext, const cell_t *params)
336 Handle_t hndl = static_cast<Handle_t>(params[1]);
340 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
343 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
346 return pConVar->GetInt();
349 /* This handles both SetConVarBool() and SetConVarInt() */
350 static cell_t sm_SetConVarNum(IPluginContext *pContext, const cell_t *params)
352 Handle_t hndl = static_cast<Handle_t>(params[1]);
356 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
359 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
362 pConVar->SetValue(params[2]);
364 #if SOURCE_ENGINE >= SE_ORANGEBOX
365 /* Should we replicate it? */
366 if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
368 ReplicateConVar(pConVar);
371 /* Should we notify clients? */
372 if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
374 NotifyConVar(pConVar);
381 static cell_t sm_GetConVarFloat(IPluginContext *pContext, const cell_t *params)
383 Handle_t hndl = static_cast<Handle_t>(params[1]);
387 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
390 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
393 float value = pConVar->GetFloat();
395 return sp_ftoc(value);
398 static cell_t sm_SetConVarFloat(IPluginContext *pContext, const cell_t *params)
400 Handle_t hndl = static_cast<Handle_t>(params[1]);
404 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
407 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
410 float value = sp_ctof(params[2]);
411 pConVar->SetValue(value);
413 #if SOURCE_ENGINE >= SE_ORANGEBOX
414 /* Should we replicate it? */
415 if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
417 ReplicateConVar(pConVar);
420 /* Should we notify clients? */
421 if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
423 NotifyConVar(pConVar);
430 static cell_t sm_GetConVarString(IPluginContext *pContext, const cell_t *params)
432 Handle_t hndl = static_cast<Handle_t>(params[1]);
436 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
439 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
442 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetString(), NULL);
447 static cell_t sm_SetConVarString(IPluginContext *pContext, const cell_t *params)
449 Handle_t hndl = static_cast<Handle_t>(params[1]);
453 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
456 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
460 pContext->LocalToString(params[2], &value);
462 pConVar->SetValue(value);
464 #if SOURCE_ENGINE >= SE_ORANGEBOX
465 /* Should we replicate it? */
466 if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
468 ReplicateConVar(pConVar);
471 /* Should we notify clients? */
472 if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
474 NotifyConVar(pConVar);
481 static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
483 Handle_t hndl = static_cast<Handle_t>(params[1]);
487 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
490 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
495 #if SOURCE_ENGINE >= SE_ORANGEBOX
496 /* Should we replicate it? */
497 if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
499 ReplicateConVar(pConVar);
502 /* Should we notify clients? */
503 if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
505 NotifyConVar(pConVar);
512 static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params)
514 Handle_t hndl = static_cast<Handle_t>(params[1]);
518 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
521 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
524 return pConVar->GetFlags();
527 static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params)
529 Handle_t hndl = static_cast<Handle_t>(params[1]);
533 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
536 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
539 pConVar->SetFlags(params[2]);
544 static cell_t sm_GetConVarBounds(IPluginContext *pContext, const cell_t *params)
546 Handle_t hndl = static_cast<Handle_t>(params[1]);
550 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
553 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
562 case ConVarBound_Upper:
563 hasBound = pConVar->GetMax(bound);
565 case ConVarBound_Lower:
566 hasBound = pConVar->GetMin(bound);
569 return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
572 pContext->LocalToPhysAddr(params[3], &addr);
573 *addr = sp_ftoc(bound);
578 static cell_t sm_SetConVarBounds(IPluginContext *pContext, const cell_t *params)
580 Handle_t hndl = static_cast<Handle_t>(params[1]);
584 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
587 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
592 case ConVarBound_Upper:
593 pConVar->SetMax(params[3] ? true : false, sp_ctof(params[4]));
595 case ConVarBound_Lower:
596 pConVar->SetMin(params[3] ? true : false, sp_ctof(params[4]));
599 return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
605 static cell_t sm_GetConVarName(IPluginContext *pContext, const cell_t *params)
607 Handle_t hndl = static_cast<Handle_t>(params[1]);
611 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
614 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
617 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetName(), NULL);
622 static bool s_QueryAlreadyWarned = false;
624 static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params)
628 IPluginFunction *pCallback;
630 if (!g_ConVarManager.IsQueryingSupported())
632 if (!s_QueryAlreadyWarned)
634 s_QueryAlreadyWarned = true;
635 return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)");
641 pPlayer = g_Players.GetPlayerByIndex(params[1]);
645 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
648 if (!pPlayer->IsConnected())
650 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
653 /* Trying a query on a bot results in callback not be fired, so don't bother */
654 if (pPlayer->IsFakeClient())
659 pContext->LocalToString(params[2], &name);
660 pCallback = pContext->GetFunctionById(params[3]);
664 return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
667 return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]);
670 static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
673 IPluginFunction *pFunction;
675 pContext->LocalToString(params[1], &name);
677 if (strcasecmp(name, "sm") == 0)
679 return pContext->ThrowNativeError("Cannot register \"sm\" command");
682 pContext->LocalToString(params[3], &help);
683 pFunction = pContext->GetFunctionById(params[2]);
687 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
690 if (!g_ConCmds.AddServerCommand(pFunction, name, help, params[4]))
692 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
698 static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params)
701 IPluginFunction *pFunction;
703 pContext->LocalToString(params[1], &name);
705 if (strcasecmp(name, "sm") == 0)
707 return pContext->ThrowNativeError("Cannot register \"sm\" command");
710 pContext->LocalToString(params[3], &help);
711 pFunction = pContext->GetFunctionById(params[2]);
715 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
718 if (!g_ConCmds.AddConsoleCommand(pFunction, name, help, params[4]))
720 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
726 static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
730 IPluginFunction *pFunction;
731 FlagBits flags = params[3];
732 int cmdflags = params[6];
734 pContext->LocalToString(params[1], &name);
736 if (strcasecmp(name, "sm") == 0)
738 return pContext->ThrowNativeError("Cannot register \"sm\" command");
741 pContext->LocalToString(params[4], &help);
742 pContext->LocalToString(params[5], (char **)&group);
743 pFunction = pContext->GetFunctionById(params[2]);
745 if (group[0] == '\0')
747 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
748 group = pPlugin->GetFilename();
753 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
756 if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags))
758 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
764 static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
766 const CCommand *pCmd = g_HL2.PeekCommandStack();
770 return pContext->ThrowNativeError("No command callback available");
773 return pCmd->ArgC() - 1;
776 static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params)
778 const CCommand *pCmd = g_HL2.PeekCommandStack();
782 return pContext->ThrowNativeError("No command callback available");
787 const char *arg = pCmd->Arg(params[1]);
789 pContext->StringToLocalUTF8(params[2], params[3], arg ? arg : "", &length);
791 return (cell_t)length;
794 static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params)
796 const CCommand *pCmd = g_HL2.PeekCommandStack();
800 return pContext->ThrowNativeError("No command callback available");
803 const char *args = pCmd->ArgS();
811 pContext->StringToLocalUTF8(params[1], params[2], args, &length);
813 return (cell_t)length;
816 static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params)
822 pCtx->LocalToString(params[1], &fmt);
823 size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
825 buffer[res++] = '\n';
828 META_CONPRINT(buffer);
833 static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
835 int index = params[1];
836 if ((index < 0) || (index > g_Players.GetMaxClients()))
838 return pCtx->ThrowNativeError("Client index %d is invalid", index);
841 CPlayer *pPlayer = NULL;
844 pPlayer = g_Players.GetPlayerByIndex(index);
845 if (!pPlayer->IsInGame())
847 return pCtx->ThrowNativeError("Client %d is not in game", index);
850 /* Silent fail on bots, engine will crash */
851 if (pPlayer->IsFakeClient())
861 pCtx->LocalToString(params[2], &fmt);
862 size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
864 buffer[res++] = '\n';
869 engine->ClientPrintf(pPlayer->GetEdict(), buffer);
871 META_CONPRINT(buffer);
877 static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
879 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
882 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
884 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
889 /* One byte for null terminator, one for newline */
890 buffer[len++] = '\n';
893 engine->ServerCommand(buffer);
898 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
900 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
903 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
905 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
910 /* One byte for null terminator, one for newline */
911 buffer[len++] = '\n';
914 InsertServerCommand(buffer);
919 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
921 engine->ServerExecute();
926 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
928 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
932 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
935 if (!pPlayer->IsConnected())
937 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
940 g_SourceMod.SetGlobalTarget(params[1]);
943 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
945 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
950 engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
955 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
957 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
961 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
964 if (!pPlayer->IsConnected())
966 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
970 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
972 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
977 serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
982 static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params)
984 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
988 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
991 if (!pPlayer->IsConnected())
993 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
997 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
999 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
1004 g_HL2.AddToFakeCliCmdQueue(params[1], engine->GetPlayerUserId(pPlayer->GetEdict()), buffer);
1009 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
1011 g_SourceMod.SetGlobalTarget(params[1]);
1013 /* Build the format string */
1015 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
1017 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
1022 /* If we're printing to the server, shortcut out */
1026 buffer[len++] = '\n';
1028 META_CONPRINT(buffer);
1032 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
1036 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
1039 if (!pPlayer->IsConnected())
1041 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
1044 g_SourceMod.SetGlobalTarget(params[1]);
1046 unsigned int replyto = g_ChatTriggers.GetReplyTo();
1047 if (replyto == SM_REPLY_CONSOLE)
1049 buffer[len++] = '\n';
1051 engine->ClientPrintf(pPlayer->GetEdict(), buffer);
1052 } else if (replyto == SM_REPLY_CHAT) {
1058 g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
1064 static cell_t GetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
1066 return g_ChatTriggers.GetReplyTo();
1069 static cell_t SetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
1071 return g_ChatTriggers.SetReplyTo(params[1]);
1074 static cell_t GetCommandIterator(IPluginContext *pContext, const cell_t *params)
1076 GlobCmdIter *iter = new GlobCmdIter;
1077 iter->started = false;
1079 Handle_t hndl = g_HandleSys.CreateHandle(hCmdIterType, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
1080 if (hndl == BAD_HANDLE)
1088 static cell_t ReadCommandIterator(IPluginContext *pContext, const cell_t *params)
1092 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
1094 if ((err = g_HandleSys.ReadHandle(params[1], hCmdIterType, &sec, (void **)&iter))
1095 != HandleError_None)
1097 return pContext->ThrowNativeError("Invalid GlobCmdIter Handle %x", params[1]);
1100 const List<ConCmdInfo *> &cmds = g_ConCmds.GetCommandList();
1104 iter->iter = cmds.begin();
1105 iter->started = true;
1108 while (iter->iter != cmds.end()
1109 && !(*(iter->iter))->sourceMod)
1114 if (iter->iter == cmds.end())
1119 ConCmdInfo *pInfo = (*(iter->iter));
1121 pContext->StringToLocalUTF8(params[2], params[3], pInfo->pCmd->GetName(), NULL);
1122 pContext->StringToLocalUTF8(params[5], params[6], pInfo->pCmd->GetHelpText(), NULL);
1125 pContext->LocalToPhysAddr(params[4], &addr);
1126 *addr = pInfo->admin.eflags;
1133 static cell_t CheckCommandAccess(IPluginContext *pContext, const cell_t *params)
1141 pContext->LocalToString(params[2], &cmd);
1143 /* Auto-detect a command if we can */
1144 FlagBits bits = params[3];
1145 bool found_command = false;
1146 if (params[0] < 4 || !params[4])
1148 found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
1153 g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
1156 return g_ConCmds.CheckCommandAccess(params[1], cmd, bits) ? 1 : 0;
1159 static cell_t IsChatTrigger(IPluginContext *pContext, const cell_t *params)
1161 return g_ChatTriggers.IsChatTrigger() ? 1 : 0;
1164 static cell_t SetCommandFlags(IPluginContext *pContext, const cell_t *params)
1167 pContext->LocalToString(params[1], &name);
1169 return (s_CommandFlagsHelper.SetFlags(name, params[2])) ? 1 : 0;
1172 static cell_t GetCommandFlags(IPluginContext *pContext, const cell_t *params)
1176 pContext->LocalToString(params[1], &name);
1178 return (s_CommandFlagsHelper.GetFlags(name, &flags)) ? flags : -1;
1181 static cell_t FindFirstConCommand(IPluginContext *pContext, const cell_t *params)
1185 cell_t *pIsCmd, *pFlags;
1186 const ConCommandBase *pConCmd;
1189 pContext->LocalToPhysAddr(params[3], &pIsCmd);
1190 pContext->LocalToPhysAddr(params[4], &pFlags);
1192 #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
1193 ICvarIteratorInternal *cvarIter = icvar->FactoryInternalIterator();
1194 cvarIter->SetFirst();
1195 if (!cvarIter->IsValid())
1199 pConCmd = cvarIter->Get();
1201 pConCmd = icvar->GetCommands();
1202 if (pConCmd == NULL)
1208 pContext->StringToLocalUTF8(params[1], params[2], pConCmd->GetName(), NULL);
1209 *pIsCmd = pConCmd->IsCommand() ? 1 : 0;
1210 *pFlags = pConCmd->GetFlags();
1214 desc = pConCmd->GetHelpText();
1215 pContext->StringToLocalUTF8(params[5], params[6], (desc && desc[0]) ? desc : "", NULL);
1218 pIter = new ConCmdIter;
1219 #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
1220 pIter->pLast = cvarIter;
1222 pIter->pLast = pConCmd;
1225 if ((hndl = g_HandleSys.CreateHandle(htConCmdIter, pIter, pContext->GetIdentity(), g_pCoreIdent, NULL))
1235 static cell_t FindNextConCommand(IPluginContext *pContext, const cell_t *params)
1239 cell_t *pIsCmd, *pFlags;
1241 const ConCommandBase *pConCmd;
1242 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
1244 if ((err = g_HandleSys.ReadHandle(params[1], htConCmdIter, &sec, (void **)&pIter)) != HandleError_None)
1246 return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
1249 if (pIter->pLast == NULL)
1254 #if (SOURCE_ENGINE == SE_LEFT4DEAD) || (SOURCE_ENGINE == SE_LEFT4DEAD2)
1255 ICvarIteratorInternal *cvarIter = pIter->pLast;
1257 if (!cvarIter->IsValid())
1261 pConCmd = cvarIter->Get();
1263 pConCmd = pIter->pLast->GetNext();
1264 if (pConCmd == NULL)
1268 pIter->pLast = pConCmd;
1271 pContext->LocalToPhysAddr(params[4], &pIsCmd);
1272 pContext->LocalToPhysAddr(params[5], &pFlags);
1274 pContext->StringToLocalUTF8(params[2], params[3], pConCmd->GetName(), NULL);
1275 *pIsCmd = pConCmd->IsCommand() ? 1 : 0;
1276 *pFlags = pConCmd->GetFlags();
1280 desc = pConCmd->GetHelpText();
1281 pContext->StringToLocalUTF8(params[6], params[7], (desc && desc[0]) ? desc : "", NULL);
1287 static cell_t SendConVarValue(IPluginContext *pContext, const cell_t *params)
1289 Handle_t hndl = static_cast<Handle_t>(params[2]);
1294 pContext->LocalToString(params[3], &value);
1296 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
1297 != HandleError_None)
1299 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
1303 bf_write buffer(data, sizeof(data));
1305 buffer.WriteUBitLong(NET_SETCONVAR, NETMSG_BITS);
1306 buffer.WriteByte(1);
1307 buffer.WriteString(pConVar->GetName());
1308 buffer.WriteString(value);
1310 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
1314 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
1317 if (!pPlayer->IsConnected())
1319 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
1322 if (pPlayer->IsFakeClient())
1324 return pContext->ThrowNativeError("Client %d is fake and cannot be targeted", params[1]);
1327 INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(params[1]));
1328 if (netchan == NULL)
1333 netchan->SendData(buffer);
1338 static cell_t AddServerTag(IPluginContext *pContext, const cell_t *params)
1343 static cell_t RemoveServerTag(IPluginContext *pContext, const cell_t *params)
1348 static cell_t AddCommandListener(IPluginContext *pContext, const cell_t *params)
1351 IPluginFunction *pFunction;
1353 pContext->LocalToString(params[2], &name);
1355 if (strcasecmp(name, "sm") == 0)
1357 g_Logger.LogError("Request to register \"sm\" command denied.");
1361 pFunction = pContext->GetFunctionById(params[1]);
1364 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
1366 if (!g_ConsoleDetours.AddListener(pFunction, name[0] == '\0' ? NULL : name))
1367 return pContext->ThrowNativeError("This game does not support command listeners");
1372 static cell_t RemoveCommandListener(IPluginContext *pContext, const cell_t *params)
1375 IPluginFunction *pFunction;
1377 pContext->LocalToString(params[2], &name);
1378 pFunction = pContext->GetFunctionById(params[1]);
1380 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
1382 if (!g_ConsoleDetours.RemoveListener(pFunction, name[0] == '\0' ? NULL : name))
1383 return pContext->ThrowNativeError("No matching callback was registered");
1388 REGISTER_NATIVES(consoleNatives)
1390 {"CreateConVar", sm_CreateConVar},
1391 {"FindConVar", sm_FindConVar},
1392 {"HookConVarChange", sm_HookConVarChange},
1393 {"UnhookConVarChange", sm_UnhookConVarChange},
1394 {"GetConVarBool", sm_GetConVarBool},
1395 {"SetConVarBool", sm_SetConVarNum},
1396 {"GetConVarInt", sm_GetConVarInt},
1397 {"SetConVarInt", sm_SetConVarNum},
1398 {"GetConVarFloat", sm_GetConVarFloat},
1399 {"SetConVarFloat", sm_SetConVarFloat},
1400 {"GetConVarString", sm_GetConVarString},
1401 {"SetConVarString", sm_SetConVarString},
1402 {"GetConVarFlags", sm_GetConVarFlags},
1403 {"SetConVarFlags", sm_SetConVarFlags},
1404 {"ResetConVar", sm_ResetConVar},
1405 {"GetConVarName", sm_GetConVarName},
1406 {"GetConVarBounds", sm_GetConVarBounds},
1407 {"SetConVarBounds", sm_SetConVarBounds},
1408 {"QueryClientConVar", sm_QueryClientConVar},
1409 {"RegServerCmd", sm_RegServerCmd},
1410 {"RegConsoleCmd", sm_RegConsoleCmd},
1411 {"GetCmdArgString", sm_GetCmdArgString},
1412 {"GetCmdArgs", sm_GetCmdArgs},
1413 {"GetCmdArg", sm_GetCmdArg},
1414 {"PrintToServer", sm_PrintToServer},
1415 {"PrintToConsole", sm_PrintToConsole},
1416 {"RegAdminCmd", sm_RegAdminCmd},
1417 {"ServerCommand", sm_ServerCommand},
1418 {"InsertServerCommand", sm_InsertServerCommand},
1419 {"ServerExecute", sm_ServerExecute},
1420 {"ClientCommand", sm_ClientCommand},
1421 {"FakeClientCommand", FakeClientCommand},
1422 {"ReplyToCommand", ReplyToCommand},
1423 {"GetCmdReplySource", GetCmdReplyTarget},
1424 {"SetCmdReplySource", SetCmdReplyTarget},
1425 {"GetCommandIterator", GetCommandIterator},
1426 {"ReadCommandIterator", ReadCommandIterator},
1427 {"CheckCommandAccess", CheckCommandAccess},
1428 {"FakeClientCommandEx", FakeClientCommandEx},
1429 {"IsChatTrigger", IsChatTrigger},
1430 {"SetCommandFlags", SetCommandFlags},
1431 {"GetCommandFlags", GetCommandFlags},
1432 {"FindFirstConCommand", FindFirstConCommand},
1433 {"FindNextConCommand", FindNextConCommand},
1434 {"SendConVarValue", SendConVarValue},
1435 {"AddServerTag", AddServerTag},
1436 {"RemoveServerTag", RemoveServerTag},
1437 {"AddCommandListener", AddCommandListener},
1438 {"RemoveCommandListener", RemoveCommandListener},