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>
46 #include <tier0/dbg.h>
48 #include "ConsoleDetours.h"
49 #include "ConCommandBaseIterator.h"
51 #if SOURCE_ENGINE >= SE_EYE
57 #if SOURCE_ENGINE >= SE_LEFT4DEAD
58 #define NET_SETCONVAR 6
60 #define NET_SETCONVAR 5
65 ConVarBound_Upper = 0,
69 HandleType_t hCmdIterType = 0;
70 HandleType_t htConCmdIter = 0;
75 List<ConCmdInfo *>::iterator iter;
78 class ConsoleHelpers :
80 public IHandleTypeDispatch
83 virtual void OnSourceModAllInitialized()
87 g_HandleSys.InitAccessDefaults(NULL, &access);
89 htConCmdIter = g_HandleSys.CreateType("ConCmdIter", this, 0, NULL, &access, g_pCoreIdent, NULL);
91 access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER | HANDLE_RESTRICT_IDENTITY;
93 hCmdIterType = g_HandleSys.CreateType("CmdIter", this, 0, NULL, &access, g_pCoreIdent, NULL);
95 virtual void OnHandleDestroy(HandleType_t type, void *object)
97 if (type == hCmdIterType)
99 GlobCmdIter *iter = (GlobCmdIter *)object;
102 else if (type == htConCmdIter)
104 ConCommandBaseIterator *iter = (ConCommandBaseIterator * )object;
108 virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize)
110 if (type == htConCmdIter)
112 *pSize = sizeof(ConCommandBaseIterator);
115 else if (type == hCmdIterType)
117 *pSize = sizeof(GlobCmdIter);
124 class CommandFlagsHelper : public IConCommandTracker
127 void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe)
129 m_CmdFlags.remove(name);
131 bool GetFlags(const char *name, int *flags)
133 ConCommandBase **ppCmd;
134 ConCommandBase *pCmd;
135 if ((ppCmd=m_CmdFlags.retrieve(name)))
137 TrackConCommandBase((*ppCmd), this);
138 *flags = (*ppCmd)->GetFlags();
141 else if ((pCmd=FindCommandBase(name)))
143 m_CmdFlags.insert(name, pCmd);
144 TrackConCommandBase(pCmd, this);
145 *flags = pCmd->GetFlags();
153 bool SetFlags(const char *name, int flags)
155 ConCommandBase **ppCmd;
156 ConCommandBase *pCmd;
157 if ((ppCmd=m_CmdFlags.retrieve(name)))
159 (*ppCmd)->SetFlags(flags);
160 TrackConCommandBase((*ppCmd), this);
163 else if ((pCmd=FindCommandBase(name)))
165 m_CmdFlags.insert(name, pCmd);
166 pCmd->SetFlags(flags);
167 TrackConCommandBase(pCmd, this);
176 KTrie<ConCommandBase *> m_CmdFlags;
177 } s_CommandFlagsHelper;
179 #if SOURCE_ENGINE < SE_ORANGEBOX
180 static void ReplicateConVar(ConVar *pConVar)
182 int maxClients = g_Players.GetMaxClients();
185 bf_write buffer(data, sizeof(data));
187 buffer.WriteUBitLong(NET_SETCONVAR, NETMSG_BITS);
189 buffer.WriteString(pConVar->GetName());
190 buffer.WriteString(pConVar->GetString());
192 for (int i = 1; i <= maxClients; i++)
194 CPlayer *pPlayer = g_Players.GetPlayerByIndex(i);
196 if (pPlayer && pPlayer->IsInGame() && !pPlayer->IsFakeClient())
198 if (INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(i)))
199 netchan->SendData(buffer);
204 static void NotifyConVar(ConVar *pConVar)
206 IGameEvent *pEvent = gameevents->CreateEvent("server_cvar");
207 pEvent->SetString("cvarname", pConVar->GetName());
208 if (IsFlagSet(pConVar, FCVAR_PROTECTED))
210 pEvent->SetString("cvarvalue", "***PROTECTED***");
214 pEvent->SetString("cvarvalue", pConVar->GetString());
217 gameevents->FireEvent(pEvent);
221 static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params)
223 char *name, *defaultVal, *helpText;
225 pContext->LocalToString(params[1], &name);
227 // While the engine seems to accept a blank convar name, it causes a crash upon server quit
228 if (name == NULL || strcmp(name, "") == 0)
230 return pContext->ThrowNativeError("Convar with blank name is not permitted");
233 pContext->LocalToString(params[2], &defaultVal);
234 pContext->LocalToString(params[3], &helpText);
236 bool hasMin = params[5] ? true : false;
237 bool hasMax = params[7] ? true : false;
238 float min = sp_ctof(params[6]);
239 float max = sp_ctof(params[8]);
241 Handle_t hndl = g_ConVarManager.CreateConVar(pContext, name, defaultVal, helpText, params[4], hasMin, min, hasMax, max);
243 if (hndl == BAD_HANDLE)
245 return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same might already exist.", name);
251 static cell_t sm_FindConVar(IPluginContext *pContext, const cell_t *params)
255 pContext->LocalToString(params[1], &name);
257 return g_ConVarManager.FindConVar(name);
260 static cell_t sm_HookConVarChange(IPluginContext *pContext, const cell_t *params)
262 Handle_t hndl = static_cast<Handle_t>(params[1]);
266 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
269 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
272 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
276 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
279 g_ConVarManager.HookConVarChange(pConVar, pFunction);
284 static cell_t sm_UnhookConVarChange(IPluginContext *pContext, const cell_t *params)
286 Handle_t hndl = static_cast<Handle_t>(params[1]);
290 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
293 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
296 IPluginFunction *pFunction = pContext->GetFunctionById(params[2]);
300 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
303 g_ConVarManager.UnhookConVarChange(pConVar, pFunction);
308 static cell_t sm_GetConVarBool(IPluginContext *pContext, const cell_t *params)
310 Handle_t hndl = static_cast<Handle_t>(params[1]);
314 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
317 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
320 return pConVar->GetBool();
323 static cell_t sm_GetConVarInt(IPluginContext *pContext, const cell_t *params)
325 Handle_t hndl = static_cast<Handle_t>(params[1]);
329 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
332 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
335 return pConVar->GetInt();
338 /* This handles both SetConVarBool() and SetConVarInt() */
339 static cell_t sm_SetConVarNum(IPluginContext *pContext, const cell_t *params)
341 Handle_t hndl = static_cast<Handle_t>(params[1]);
345 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
348 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
351 pConVar->SetValue(params[2]);
353 #if SOURCE_ENGINE < SE_ORANGEBOX
354 /* Should we replicate it? */
355 if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
357 ReplicateConVar(pConVar);
360 /* Should we notify clients? */
361 if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
363 NotifyConVar(pConVar);
370 static cell_t sm_GetConVarFloat(IPluginContext *pContext, const cell_t *params)
372 Handle_t hndl = static_cast<Handle_t>(params[1]);
376 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
379 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
382 float value = pConVar->GetFloat();
384 return sp_ftoc(value);
387 static cell_t sm_SetConVarFloat(IPluginContext *pContext, const cell_t *params)
389 Handle_t hndl = static_cast<Handle_t>(params[1]);
393 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
396 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
399 float value = sp_ctof(params[2]);
400 pConVar->SetValue(value);
402 #if SOURCE_ENGINE < SE_ORANGEBOX
403 /* Should we replicate it? */
404 if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
406 ReplicateConVar(pConVar);
409 /* Should we notify clients? */
410 if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
412 NotifyConVar(pConVar);
419 static cell_t sm_GetConVarString(IPluginContext *pContext, const cell_t *params)
421 Handle_t hndl = static_cast<Handle_t>(params[1]);
425 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
428 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
431 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetString(), NULL);
436 static cell_t sm_SetConVarString(IPluginContext *pContext, const cell_t *params)
438 Handle_t hndl = static_cast<Handle_t>(params[1]);
442 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
445 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
449 pContext->LocalToString(params[2], &value);
451 pConVar->SetValue(value);
453 #if SOURCE_ENGINE < SE_ORANGEBOX
454 /* Should we replicate it? */
455 if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
457 ReplicateConVar(pConVar);
460 /* Should we notify clients? */
461 if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
463 NotifyConVar(pConVar);
470 static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params)
472 Handle_t hndl = static_cast<Handle_t>(params[1]);
476 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
479 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
484 #if SOURCE_ENGINE < SE_ORANGEBOX
485 /* Should we replicate it? */
486 if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED))
488 ReplicateConVar(pConVar);
491 /* Should we notify clients? */
492 if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY))
494 NotifyConVar(pConVar);
501 static cell_t GetConVarDefault(IPluginContext *pContext, const cell_t *params)
503 Handle_t hndl = static_cast<Handle_t>(params[1]);
507 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
510 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
514 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetDefault(), &bytes);
519 static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params)
521 Handle_t hndl = static_cast<Handle_t>(params[1]);
525 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
528 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
531 return pConVar->GetFlags();
534 static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params)
536 Handle_t hndl = static_cast<Handle_t>(params[1]);
540 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
543 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
546 pConVar->SetFlags(params[2]);
551 static cell_t sm_GetConVarBounds(IPluginContext *pContext, const cell_t *params)
553 Handle_t hndl = static_cast<Handle_t>(params[1]);
557 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
560 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
569 case ConVarBound_Upper:
570 hasBound = pConVar->GetMax(bound);
572 case ConVarBound_Lower:
573 hasBound = pConVar->GetMin(bound);
576 return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
579 pContext->LocalToPhysAddr(params[3], &addr);
580 *addr = sp_ftoc(bound);
585 static cell_t sm_SetConVarBounds(IPluginContext *pContext, const cell_t *params)
587 Handle_t hndl = static_cast<Handle_t>(params[1]);
591 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
594 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
599 case ConVarBound_Upper:
600 pConVar->SetMax(params[3] ? true : false, sp_ctof(params[4]));
602 case ConVarBound_Lower:
603 pConVar->SetMin(params[3] ? true : false, sp_ctof(params[4]));
606 return pContext->ThrowNativeError("Invalid ConVarBounds value %d");
612 static cell_t sm_GetConVarName(IPluginContext *pContext, const cell_t *params)
614 Handle_t hndl = static_cast<Handle_t>(params[1]);
618 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
621 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
624 pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetName(), NULL);
629 static bool s_QueryAlreadyWarned = false;
631 static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params)
635 IPluginFunction *pCallback;
637 if (!g_ConVarManager.IsQueryingSupported())
639 if (!s_QueryAlreadyWarned)
641 s_QueryAlreadyWarned = true;
642 return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)");
648 pPlayer = g_Players.GetPlayerByIndex(params[1]);
652 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
655 if (!pPlayer->IsConnected())
657 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
660 /* Trying a query on a bot results in callback not be fired, so don't bother */
661 if (pPlayer->IsFakeClient())
666 pContext->LocalToString(params[2], &name);
667 pCallback = pContext->GetFunctionById(params[3]);
671 return pContext->ThrowNativeError("Invalid function id (%X)", params[3]);
674 return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]);
677 static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params)
680 IPluginFunction *pFunction;
682 pContext->LocalToString(params[1], &name);
684 if (strcasecmp(name, "sm") == 0)
686 return pContext->ThrowNativeError("Cannot register \"sm\" command");
689 pContext->LocalToString(params[3], &help);
690 pFunction = pContext->GetFunctionById(params[2]);
694 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
697 if (!g_ConCmds.AddServerCommand(pFunction, name, help, params[4]))
699 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
705 static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params)
708 IPluginFunction *pFunction;
710 pContext->LocalToString(params[1], &name);
712 if (strcasecmp(name, "sm") == 0)
714 return pContext->ThrowNativeError("Cannot register \"sm\" command");
717 pContext->LocalToString(params[3], &help);
718 pFunction = pContext->GetFunctionById(params[2]);
722 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
725 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
726 const char *group = pPlugin->GetFilename();
727 if (!g_ConCmds.AddAdminCommand(pFunction, name, group, 0, help, params[4]))
729 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
735 static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params)
739 IPluginFunction *pFunction;
740 FlagBits flags = params[3];
741 int cmdflags = params[6];
743 pContext->LocalToString(params[1], &name);
745 if (strcasecmp(name, "sm") == 0)
747 return pContext->ThrowNativeError("Cannot register \"sm\" command");
750 pContext->LocalToString(params[4], &help);
751 pContext->LocalToString(params[5], (char **)&group);
752 pFunction = pContext->GetFunctionById(params[2]);
754 if (group[0] == '\0')
756 CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext());
757 group = pPlugin->GetFilename();
762 return pContext->ThrowNativeError("Invalid function id (%X)", params[2]);
765 if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags))
767 return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name);
773 static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params)
775 const CCommand *pCmd = g_HL2.PeekCommandStack();
779 return pContext->ThrowNativeError("No command callback available");
782 return pCmd->ArgC() - 1;
785 static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params)
787 const CCommand *pCmd = g_HL2.PeekCommandStack();
791 return pContext->ThrowNativeError("No command callback available");
796 const char *arg = pCmd->Arg(params[1]);
798 pContext->StringToLocalUTF8(params[2], params[3], arg ? arg : "", &length);
800 return (cell_t)length;
803 static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params)
805 const CCommand *pCmd = g_HL2.PeekCommandStack();
809 return pContext->ThrowNativeError("No command callback available");
812 const char *args = pCmd->ArgS();
820 pContext->StringToLocalUTF8(params[1], params[2], args, &length);
822 return (cell_t)length;
825 static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params)
831 pCtx->LocalToString(params[1], &fmt);
832 size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
834 buffer[res++] = '\n';
837 META_CONPRINT(buffer);
842 static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params)
844 int index = params[1];
845 if ((index < 0) || (index > g_Players.GetMaxClients()))
847 return pCtx->ThrowNativeError("Client index %d is invalid", index);
850 CPlayer *pPlayer = NULL;
853 pPlayer = g_Players.GetPlayerByIndex(index);
854 if (!pPlayer->IsInGame())
856 return pCtx->ThrowNativeError("Client %d is not in game", index);
859 /* Silent fail on bots, engine will crash */
860 if (pPlayer->IsFakeClient())
870 pCtx->LocalToString(params[2], &fmt);
871 size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg);
873 buffer[res++] = '\n';
878 engine->ClientPrintf(pPlayer->GetEdict(), buffer);
880 META_CONPRINT(buffer);
886 static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params)
888 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
891 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
893 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
898 /* One byte for null terminator, one for newline */
899 buffer[len++] = '\n';
902 engine->ServerCommand(buffer);
907 char *g_ServerCommandBuffer = NULL;
908 cell_t g_ServerCommandBufferLength;
910 bool g_ShouldCatchSpew = false;
912 #if SOURCE_ENGINE < SE_LEFT4DEAD2
913 SpewOutputFunc_t g_OriginalSpewOutputFunc = NULL;
915 SpewRetval_t SourcemodSpewOutputFunc(SpewType_t spewType, tchar const *pMsg)
917 if (g_ServerCommandBuffer)
919 V_strcat(g_ServerCommandBuffer, pMsg, g_ServerCommandBufferLength);
922 if (g_OriginalSpewOutputFunc)
924 return g_OriginalSpewOutputFunc(spewType, pMsg);
926 return SPEW_CONTINUE;
930 class CSourcemodLoggingListener: public ILoggingListener
933 void Log(const LoggingContext_t *pContext, const tchar *pMessage)
935 if (g_ServerCommandBuffer)
937 V_strcat(g_ServerCommandBuffer, pMessage, g_ServerCommandBufferLength);
940 } g_SourcemodLoggingListener;
943 CON_COMMAND(sm_conhook_start, "")
945 if (!g_ShouldCatchSpew)
947 Warning("You shouldn't run this command!\n");
951 #if SOURCE_ENGINE < SE_LEFT4DEAD2
952 g_OriginalSpewOutputFunc = GetSpewOutputFunc();
953 SpewOutputFunc(SourcemodSpewOutputFunc);
955 #if SOURCE_ENGINE < SE_ALIENSWARM
956 LoggingSystem_PushLoggingState(false);
958 LoggingSystem_PushLoggingState(false, false);
960 LoggingSystem_RegisterLoggingListener(&g_SourcemodLoggingListener);
964 CON_COMMAND(sm_conhook_stop, "")
966 if (!g_ShouldCatchSpew)
968 Warning("You shouldn't run this command!\n");
972 #if SOURCE_ENGINE < SE_LEFT4DEAD2
973 SpewOutputFunc(g_OriginalSpewOutputFunc);
975 LoggingSystem_PopLoggingState(false);
978 g_ShouldCatchSpew = false;
981 static cell_t sm_ServerCommandEx(IPluginContext *pContext, const cell_t *params)
983 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
986 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 3);
988 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
993 /* One byte for null terminator, one for newline */
994 buffer[len++] = '\n';
997 pContext->LocalToString(params[1], &g_ServerCommandBuffer);
998 g_ServerCommandBufferLength = params[2];
1000 if (g_ServerCommandBufferLength > 0)
1002 g_ServerCommandBuffer[0] = '\0';
1005 engine->ServerExecute();
1007 g_ShouldCatchSpew = true;
1008 engine->ServerCommand("sm_conhook_start\n");
1009 engine->ServerCommand(buffer);
1010 engine->ServerCommand("sm_conhook_stop\n");
1012 engine->ServerExecute();
1014 if (g_ServerCommandBufferLength > 0)
1016 g_ServerCommandBuffer[g_ServerCommandBufferLength-1] = '\0';
1019 g_ServerCommandBuffer = NULL;
1020 g_ServerCommandBufferLength = 0;
1025 static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params)
1027 g_SourceMod.SetGlobalTarget(SOURCEMOD_SERVER_LANGUAGE);
1030 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1);
1032 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
1037 /* One byte for null terminator, one for newline */
1038 buffer[len++] = '\n';
1041 InsertServerCommand(buffer);
1046 static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params)
1048 engine->ServerExecute();
1053 static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params)
1055 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
1059 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
1062 if (!pPlayer->IsConnected())
1064 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
1067 g_SourceMod.SetGlobalTarget(params[1]);
1070 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
1072 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
1077 engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer);
1082 static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params)
1084 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
1088 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
1091 if (!pPlayer->IsConnected())
1093 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
1097 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
1099 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
1104 serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer);
1109 static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params)
1111 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
1115 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
1118 if (!pPlayer->IsConnected())
1120 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
1124 g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2);
1126 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
1131 g_HL2.AddToFakeCliCmdQueue(params[1], engine->GetPlayerUserId(pPlayer->GetEdict()), buffer);
1136 static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params)
1138 g_SourceMod.SetGlobalTarget(params[1]);
1140 /* Build the format string */
1142 size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2);
1144 if (pContext->GetLastNativeError() != SP_ERROR_NONE)
1149 /* If we're printing to the server, shortcut out */
1153 buffer[len++] = '\n';
1155 META_CONPRINT(buffer);
1159 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
1163 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
1166 if (!pPlayer->IsConnected())
1168 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
1171 g_SourceMod.SetGlobalTarget(params[1]);
1173 unsigned int replyto = g_ChatTriggers.GetReplyTo();
1174 if (replyto == SM_REPLY_CONSOLE)
1176 buffer[len++] = '\n';
1178 engine->ClientPrintf(pPlayer->GetEdict(), buffer);
1179 } else if (replyto == SM_REPLY_CHAT) {
1185 g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer);
1191 static cell_t GetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
1193 return g_ChatTriggers.GetReplyTo();
1196 static cell_t SetCmdReplyTarget(IPluginContext *pContext, const cell_t *params)
1198 return g_ChatTriggers.SetReplyTo(params[1]);
1201 static cell_t GetCommandIterator(IPluginContext *pContext, const cell_t *params)
1203 GlobCmdIter *iter = new GlobCmdIter;
1204 iter->started = false;
1206 Handle_t hndl = g_HandleSys.CreateHandle(hCmdIterType, iter, pContext->GetIdentity(), g_pCoreIdent, NULL);
1207 if (hndl == BAD_HANDLE)
1215 static cell_t ReadCommandIterator(IPluginContext *pContext, const cell_t *params)
1219 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
1221 if ((err = g_HandleSys.ReadHandle(params[1], hCmdIterType, &sec, (void **)&iter))
1222 != HandleError_None)
1224 return pContext->ThrowNativeError("Invalid GlobCmdIter Handle %x", params[1]);
1227 const List<ConCmdInfo *> &cmds = g_ConCmds.GetCommandList();
1231 iter->iter = cmds.begin();
1232 iter->started = true;
1235 while (iter->iter != cmds.end()
1236 && !(*(iter->iter))->sourceMod)
1241 if (iter->iter == cmds.end())
1246 ConCmdInfo *pInfo = (*(iter->iter));
1248 pContext->StringToLocalUTF8(params[2], params[3], pInfo->pCmd->GetName(), NULL);
1249 pContext->StringToLocalUTF8(params[5], params[6], pInfo->pCmd->GetHelpText(), NULL);
1252 pContext->LocalToPhysAddr(params[4], &addr);
1253 *addr = pInfo->admin.eflags;
1260 static cell_t CheckCommandAccess(IPluginContext *pContext, const cell_t *params)
1268 pContext->LocalToString(params[2], &cmd);
1270 /* Match up with an admin command if possible */
1271 FlagBits bits = params[3];
1272 bool found_command = false;
1273 if (params[0] < 4 || !params[4])
1275 found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
1280 g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
1283 return g_ConCmds.CheckClientCommandAccess(params[1], cmd, bits) ? 1 : 0;
1286 static cell_t CheckAccess(IPluginContext *pContext, const cell_t *params)
1289 pContext->LocalToString(params[2], &cmd);
1291 /* Match up with an admin command if possible */
1292 FlagBits bits = params[3];
1293 bool found_command = false;
1294 if (params[0] < 4 || !params[4])
1296 found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits);
1301 g_Admins.GetCommandOverride(cmd, Override_Command, &bits);
1304 return g_ConCmds.CheckAdminCommandAccess(params[1], cmd, bits) ? 1 : 0;
1307 static cell_t IsChatTrigger(IPluginContext *pContext, const cell_t *params)
1309 return g_ChatTriggers.IsChatTrigger() ? 1 : 0;
1312 static cell_t SetCommandFlags(IPluginContext *pContext, const cell_t *params)
1315 pContext->LocalToString(params[1], &name);
1317 return (s_CommandFlagsHelper.SetFlags(name, params[2])) ? 1 : 0;
1320 static cell_t GetCommandFlags(IPluginContext *pContext, const cell_t *params)
1324 pContext->LocalToString(params[1], &name);
1326 return (s_CommandFlagsHelper.GetFlags(name, &flags)) ? flags : -1;
1329 static cell_t FindFirstConCommand(IPluginContext *pContext, const cell_t *params)
1332 ConCommandBaseIterator *pIter;
1333 cell_t *pIsCmd, *pFlags;
1334 const ConCommandBase *pConCmd;
1337 pContext->LocalToPhysAddr(params[3], &pIsCmd);
1338 pContext->LocalToPhysAddr(params[4], &pFlags);
1340 pIter = new ConCommandBaseIterator();
1342 if (!pIter->IsValid())
1348 pConCmd = pIter->Get();
1350 pContext->StringToLocalUTF8(params[1], params[2], pConCmd->GetName(), NULL);
1351 *pIsCmd = pConCmd->IsCommand() ? 1 : 0;
1352 *pFlags = pConCmd->GetFlags();
1356 desc = pConCmd->GetHelpText();
1357 pContext->StringToLocalUTF8(params[5], params[6], (desc && desc[0]) ? desc : "", NULL);
1360 if ((hndl = g_HandleSys.CreateHandle(htConCmdIter, pIter, pContext->GetIdentity(), g_pCoreIdent, NULL))
1370 static cell_t FindNextConCommand(IPluginContext *pContext, const cell_t *params)
1373 ConCommandBaseIterator *pIter;
1374 cell_t *pIsCmd, *pFlags;
1376 const ConCommandBase *pConCmd;
1377 HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent);
1379 if ((err = g_HandleSys.ReadHandle(params[1], htConCmdIter, &sec, (void **)&pIter)) != HandleError_None)
1381 return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err);
1384 if (!pIter->IsValid())
1390 if (!pIter->IsValid())
1394 pConCmd = pIter->Get();
1396 pContext->LocalToPhysAddr(params[4], &pIsCmd);
1397 pContext->LocalToPhysAddr(params[5], &pFlags);
1399 pContext->StringToLocalUTF8(params[2], params[3], pConCmd->GetName(), NULL);
1400 *pIsCmd = pConCmd->IsCommand() ? 1 : 0;
1401 *pFlags = pConCmd->GetFlags();
1405 desc = pConCmd->GetHelpText();
1406 pContext->StringToLocalUTF8(params[6], params[7], (desc && desc[0]) ? desc : "", NULL);
1412 static cell_t SendConVarValue(IPluginContext *pContext, const cell_t *params)
1414 Handle_t hndl = static_cast<Handle_t>(params[2]);
1419 pContext->LocalToString(params[3], &value);
1421 if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar))
1422 != HandleError_None)
1424 return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err);
1428 bf_write buffer(data, sizeof(data));
1430 buffer.WriteUBitLong(NET_SETCONVAR, NETMSG_BITS);
1431 buffer.WriteByte(1);
1432 buffer.WriteString(pConVar->GetName());
1433 buffer.WriteString(value);
1435 CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]);
1439 return pContext->ThrowNativeError("Client index %d is invalid", params[1]);
1442 if (!pPlayer->IsConnected())
1444 return pContext->ThrowNativeError("Client %d is not connected", params[1]);
1447 if (pPlayer->IsFakeClient())
1449 return pContext->ThrowNativeError("Client %d is fake and cannot be targeted", params[1]);
1452 INetChannel *netchan = static_cast<INetChannel *>(engine->GetPlayerNetInfo(params[1]));
1453 if (netchan == NULL)
1458 netchan->SendData(buffer);
1463 static cell_t AddServerTag(IPluginContext *pContext, const cell_t *params)
1468 static cell_t RemoveServerTag(IPluginContext *pContext, const cell_t *params)
1473 static cell_t AddCommandListener(IPluginContext *pContext, const cell_t *params)
1476 IPluginFunction *pFunction;
1478 pContext->LocalToString(params[2], &name);
1480 if (strcasecmp(name, "sm") == 0)
1482 g_Logger.LogError("Request to register \"sm\" command denied.");
1486 pFunction = pContext->GetFunctionById(params[1]);
1489 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
1491 if (!g_ConsoleDetours.AddListener(pFunction, name[0] == '\0' ? NULL : name))
1492 return pContext->ThrowNativeError("This game does not support command listeners");
1497 static cell_t RemoveCommandListener(IPluginContext *pContext, const cell_t *params)
1500 IPluginFunction *pFunction;
1502 pContext->LocalToString(params[2], &name);
1503 pFunction = pContext->GetFunctionById(params[1]);
1505 return pContext->ThrowNativeError("Invalid function id (%X)", params[1]);
1507 if (!g_ConsoleDetours.RemoveListener(pFunction, name[0] == '\0' ? NULL : name))
1508 return pContext->ThrowNativeError("No matching callback was registered");
1513 REGISTER_NATIVES(consoleNatives)
1515 {"CreateConVar", sm_CreateConVar},
1516 {"FindConVar", sm_FindConVar},
1517 {"HookConVarChange", sm_HookConVarChange},
1518 {"UnhookConVarChange", sm_UnhookConVarChange},
1519 {"GetConVarBool", sm_GetConVarBool},
1520 {"SetConVarBool", sm_SetConVarNum},
1521 {"GetConVarInt", sm_GetConVarInt},
1522 {"SetConVarInt", sm_SetConVarNum},
1523 {"GetConVarFloat", sm_GetConVarFloat},
1524 {"SetConVarFloat", sm_SetConVarFloat},
1525 {"GetConVarString", sm_GetConVarString},
1526 {"SetConVarString", sm_SetConVarString},
1527 {"GetConVarFlags", sm_GetConVarFlags},
1528 {"SetConVarFlags", sm_SetConVarFlags},
1529 {"ResetConVar", sm_ResetConVar},
1530 {"GetConVarName", sm_GetConVarName},
1531 {"GetConVarBounds", sm_GetConVarBounds},
1532 {"SetConVarBounds", sm_SetConVarBounds},
1533 {"QueryClientConVar", sm_QueryClientConVar},
1534 {"GetConVarDefault", GetConVarDefault},
1535 {"RegServerCmd", sm_RegServerCmd},
1536 {"RegConsoleCmd", sm_RegConsoleCmd},
1537 {"GetCmdArgString", sm_GetCmdArgString},
1538 {"GetCmdArgs", sm_GetCmdArgs},
1539 {"GetCmdArg", sm_GetCmdArg},
1540 {"PrintToServer", sm_PrintToServer},
1541 {"PrintToConsole", sm_PrintToConsole},
1542 {"RegAdminCmd", sm_RegAdminCmd},
1543 {"ServerCommand", sm_ServerCommand},
1544 {"ServerCommandEx", sm_ServerCommandEx},
1545 {"InsertServerCommand", sm_InsertServerCommand},
1546 {"ServerExecute", sm_ServerExecute},
1547 {"ClientCommand", sm_ClientCommand},
1548 {"FakeClientCommand", FakeClientCommand},
1549 {"ReplyToCommand", ReplyToCommand},
1550 {"GetCmdReplySource", GetCmdReplyTarget},
1551 {"SetCmdReplySource", SetCmdReplyTarget},
1552 {"GetCommandIterator", GetCommandIterator},
1553 {"ReadCommandIterator", ReadCommandIterator},
1554 {"CheckCommandAccess", CheckCommandAccess},
1555 {"CheckAccess", CheckAccess},
1556 {"FakeClientCommandEx", FakeClientCommandEx},
1557 {"IsChatTrigger", IsChatTrigger},
1558 {"SetCommandFlags", SetCommandFlags},
1559 {"GetCommandFlags", GetCommandFlags},
1560 {"FindFirstConCommand", FindFirstConCommand},
1561 {"FindNextConCommand", FindNextConCommand},
1562 {"SendConVarValue", SendConVarValue},
1563 {"AddServerTag", AddServerTag},
1564 {"RemoveServerTag", RemoveServerTag},
1565 {"AddCommandListener", AddCommandListener},
1566 {"RemoveCommandListener", RemoveCommandListener},