diff -r 000000000000 -r b7aa2b626807 core/smn_console.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/core/smn_console.cpp Sun Mar 30 07:00:22 2008 +0000 @@ -0,0 +1,1307 @@ +/** + * vim: set ts=4 : + * ============================================================================= + * SourceMod + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. + * ============================================================================= + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License, version 3.0, as published by the + * Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + * + * As a special exception, AlliedModders LLC gives you permission to link the + * code of this program (as well as its derivative works) to "Half-Life 2," the + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software + * by the Valve Corporation. You must obey the GNU General Public License in + * all respects for all other code used. Additionally, AlliedModders LLC grants + * this exception to all derivative works. AlliedModders LLC defines further + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), + * or . + * + * Version: $Id$ + */ + +#include "sm_globals.h" +#include "HalfLife2.h" +#include "sourcemm_api.h" +#include "HandleSys.h" +#include "ConVarManager.h" +#include "ConCmdManager.h" +#include "PluginSys.h" +#include "sm_stringutil.h" +#include "PlayerManager.h" +#include "ChatTriggers.h" +#include "AdminCache.h" +#include +#include +#include + +#define NET_SETCONVAR 5 + +enum ConVarBounds +{ + ConVarBound_Upper = 0, + ConVarBound_Lower +}; + +HandleType_t hCmdIterType = 0; +HandleType_t htConCmdIter = 0; + +struct GlobCmdIter +{ + bool started; + List::iterator iter; +}; + +struct ConCmdIter +{ + const ConCommandBase *pLast; +}; + +class ConsoleHelpers : + public SMGlobalClass, + public IHandleTypeDispatch +{ +public: + virtual void OnSourceModAllInitialized() + { + HandleAccess access; + + g_HandleSys.InitAccessDefaults(NULL, &access); + + htConCmdIter = g_HandleSys.CreateType("ConCmdIter", this, 0, NULL, &access, g_pCoreIdent, NULL); + + access.access[HandleAccess_Clone] = HANDLE_RESTRICT_OWNER | HANDLE_RESTRICT_IDENTITY; + + hCmdIterType = g_HandleSys.CreateType("CmdIter", this, 0, NULL, &access, g_pCoreIdent, NULL); + } + virtual void OnHandleDestroy(HandleType_t type, void *object) + { + if (type == hCmdIterType) + { + GlobCmdIter *iter = (GlobCmdIter *)object; + delete iter; + } + else if (type == htConCmdIter) + { + ConCmdIter *iter = (ConCmdIter * )object; + delete iter; + } + } + virtual bool GetHandleApproxSize(HandleType_t type, void *object, unsigned int *pSize) + { + if (type == htConCmdIter) + { + *pSize = sizeof(ConCmdIter); + return true; + } + else if (type == hCmdIterType) + { + *pSize = sizeof(GlobCmdIter); + return true; + } + return false; + } +} s_ConsoleHelpers; + +class CommandFlagsHelper : public IConCommandTracker +{ +public: + void OnUnlinkConCommandBase(ConCommandBase *pBase, const char *name, bool is_read_safe) + { + m_CmdFlags.remove(name); + } + bool GetFlags(const char *name, int *flags) + { + ConCommandBase **ppCmd; + ConCommandBase *pCmd; + if ((ppCmd=m_CmdFlags.retrieve(name))) + { + TrackConCommandBase((*ppCmd), this); + *flags = (*ppCmd)->GetFlags(); + return true; + } + else if ((pCmd=FindConCommandBase(name))) + { + m_CmdFlags.insert(name, pCmd); + TrackConCommandBase(pCmd, this); + *flags = pCmd->GetFlags(); + return true; + } + else + { + return false; + } + } + bool SetFlags(const char *name, int flags) + { + ConCommandBase **ppCmd; + ConCommandBase *pCmd; + if ((ppCmd=m_CmdFlags.retrieve(name))) + { + (*ppCmd)->SetFlags(flags); + TrackConCommandBase((*ppCmd), this); + return true; + } + else if ((pCmd=FindConCommandBase(name))) + { + m_CmdFlags.insert(name, pCmd); + pCmd->SetFlags(flags); + TrackConCommandBase(pCmd, this); + return true; + } + else + { + return false; + } + } +private: + KTrie m_CmdFlags; +} s_CommandFlagsHelper; + +static void ReplicateConVar(ConVar *pConVar) +{ + int maxClients = g_Players.GetMaxClients(); + + char data[256]; + bf_write buffer(data, sizeof(data)); + + buffer.WriteUBitLong(NET_SETCONVAR, 5); + buffer.WriteByte(1); + buffer.WriteString(pConVar->GetName()); + buffer.WriteString(pConVar->GetString()); + + for (int i = 1; i <= maxClients; i++) + { + CPlayer *pPlayer = g_Players.GetPlayerByIndex(i); + + if (pPlayer && pPlayer->IsInGame() && !pPlayer->IsFakeClient()) + { + INetChannel *netchan = static_cast(engine->GetPlayerNetInfo(i)); + netchan->SendData(buffer); + } + } +} + +static void NotifyConVar(ConVar *pConVar) +{ + IGameEvent *pEvent = gameevents->CreateEvent("server_cvar"); + pEvent->SetString("cvarname", pConVar->GetName()); + if (IsFlagSet(pConVar, FCVAR_PROTECTED)) + { + pEvent->SetString("cvarvalue", "***PROTECTED***"); + } + else + { + pEvent->SetString("cvarvalue", pConVar->GetString()); + } + + gameevents->FireEvent(pEvent); +} + +static cell_t sm_CreateConVar(IPluginContext *pContext, const cell_t *params) +{ + char *name, *defaultVal, *helpText; + + pContext->LocalToString(params[1], &name); + + // While the engine seems to accept a blank convar name, it causes a crash upon server quit + if (name == NULL || strcmp(name, "") == 0) + { + return pContext->ThrowNativeError("Convar with blank name is not permitted"); + } + + pContext->LocalToString(params[2], &defaultVal); + pContext->LocalToString(params[3], &helpText); + + bool hasMin = params[5] ? true : false; + bool hasMax = params[7] ? true : false; + float min = sp_ctof(params[6]); + float max = sp_ctof(params[8]); + + Handle_t hndl = g_ConVarManager.CreateConVar(pContext, name, defaultVal, helpText, params[4], hasMin, min, hasMax, max); + + if (hndl == BAD_HANDLE) + { + return pContext->ThrowNativeError("Convar \"%s\" was not created. A console command with the same might already exist.", name); + } + + return hndl; +} + +static cell_t sm_FindConVar(IPluginContext *pContext, const cell_t *params) +{ + char *name; + + pContext->LocalToString(params[1], &name); + + return g_ConVarManager.FindConVar(name); +} + +static cell_t sm_HookConVarChange(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + IPluginFunction *pFunction = pContext->GetFunctionById(params[2]); + + if (!pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[2]); + } + + g_ConVarManager.HookConVarChange(pConVar, pFunction); + + return 1; +} + +static cell_t sm_UnhookConVarChange(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + IPluginFunction *pFunction = pContext->GetFunctionById(params[2]); + + if (!pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[2]); + } + + g_ConVarManager.UnhookConVarChange(pConVar, pFunction); + + return 1; +} + +static cell_t sm_GetConVarBool(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + return pConVar->GetBool(); +} + +static cell_t sm_GetConVarInt(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + return pConVar->GetInt(); +} + +/* This handles both SetConVarBool() and SetConVarInt() */ +static cell_t sm_SetConVarNum(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + pConVar->SetValue(params[2]); + + /* Should we replicate it? */ + if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED)) + { + ReplicateConVar(pConVar); + } + + /* Should we notify clients? */ + if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY)) + { + NotifyConVar(pConVar); + } + + return 1; +} + +static cell_t sm_GetConVarFloat(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + float value = pConVar->GetFloat(); + + return sp_ftoc(value); +} + +static cell_t sm_SetConVarFloat(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + float value = sp_ctof(params[2]); + pConVar->SetValue(value); + + /* Should we replicate it? */ + if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED)) + { + ReplicateConVar(pConVar); + } + + /* Should we notify clients? */ + if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY)) + { + NotifyConVar(pConVar); + } + + return 1; +} + +static cell_t sm_GetConVarString(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetString(), NULL); + + return 1; +} + +static cell_t sm_SetConVarString(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + char *value; + pContext->LocalToString(params[2], &value); + + pConVar->SetValue(value); + + /* Should we replicate it? */ + if (params[3] && IsFlagSet(pConVar, FCVAR_REPLICATED)) + { + ReplicateConVar(pConVar); + } + + /* Should we notify clients? */ + if (params[4] && IsFlagSet(pConVar, FCVAR_NOTIFY)) + { + NotifyConVar(pConVar); + } + + return 1; +} + +static cell_t sm_ResetConVar(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + pConVar->Revert(); + + /* Should we replicate it? */ + if (params[2] && IsFlagSet(pConVar, FCVAR_REPLICATED)) + { + ReplicateConVar(pConVar); + } + + /* Should we notify clients? */ + if (params[3] && IsFlagSet(pConVar, FCVAR_NOTIFY)) + { + NotifyConVar(pConVar); + } + + return 1; +} + +static cell_t sm_GetConVarFlags(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + return pConVar->GetFlags(); +} + +static cell_t sm_SetConVarFlags(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + pConVar->SetFlags(params[2]); + + return 1; +} + +static cell_t sm_GetConVarBounds(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + cell_t *addr; + bool hasBound; + float bound; + + switch (params[2]) + { + case ConVarBound_Upper: + hasBound = pConVar->GetMax(bound); + break; + case ConVarBound_Lower: + hasBound = pConVar->GetMin(bound); + break; + default: + return pContext->ThrowNativeError("Invalid ConVarBounds value %d"); + } + + pContext->LocalToPhysAddr(params[3], &addr); + *addr = sp_ftoc(bound); + + return hasBound; +} + +static cell_t sm_SetConVarBounds(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + switch (params[2]) + { + case ConVarBound_Upper: + pConVar->SetMax(params[3] ? true : false, sp_ctof(params[4])); + break; + case ConVarBound_Lower: + pConVar->SetMin(params[3] ? true : false, sp_ctof(params[4])); + break; + default: + return pContext->ThrowNativeError("Invalid ConVarBounds value %d"); + } + + return 1; +} + +static cell_t sm_GetConVarName(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[1]); + HandleError err; + ConVar *pConVar; + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + pContext->StringToLocalUTF8(params[2], params[3], pConVar->GetName(), NULL); + + return 1; +} + +static bool s_QueryAlreadyWarned = false; + +static cell_t sm_QueryClientConVar(IPluginContext *pContext, const cell_t *params) +{ + CPlayer *pPlayer; + char *name; + IPluginFunction *pCallback; + + if (!g_ConVarManager.IsQueryingSupported()) + { + if (!s_QueryAlreadyWarned) + { + s_QueryAlreadyWarned = true; + return pContext->ThrowNativeError("Game does not support client convar querying (one time warning)"); + } + + return 0; + } + + pPlayer = g_Players.GetPlayerByIndex(params[1]); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", params[1]); + } + + if (!pPlayer->IsConnected()) + { + return pContext->ThrowNativeError("Client %d is not connected", params[1]); + } + + /* Trying a query on a bot results in callback not be fired, so don't bother */ + if (pPlayer->IsFakeClient()) + { + return 0; + } + + pContext->LocalToString(params[2], &name); + pCallback = pContext->GetFunctionById(params[3]); + + if (!pCallback) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[3]); + } + + return g_ConVarManager.QueryClientConVar(pPlayer->GetEdict(), name, pCallback, params[4]); +} + +static cell_t sm_RegServerCmd(IPluginContext *pContext, const cell_t *params) +{ + char *name,*help; + IPluginFunction *pFunction; + + pContext->LocalToString(params[1], &name); + pContext->LocalToString(params[3], &help); + pFunction = pContext->GetFunctionById(params[2]); + + if (!pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[2]); + } + + if (!g_ConCmds.AddServerCommand(pFunction, name, help, params[4])) + { + return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name); + } + + return 1; +} + +static cell_t sm_RegConsoleCmd(IPluginContext *pContext, const cell_t *params) +{ + char *name,*help; + IPluginFunction *pFunction; + + pContext->LocalToString(params[1], &name); + pContext->LocalToString(params[3], &help); + pFunction = pContext->GetFunctionById(params[2]); + + if (!pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[2]); + } + + if (!g_ConCmds.AddConsoleCommand(pFunction, name, help, params[4])) + { + return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name); + } + + return 1; +} + +static cell_t sm_RegAdminCmd(IPluginContext *pContext, const cell_t *params) +{ + char *name,*help; + const char *group; + IPluginFunction *pFunction; + FlagBits flags = params[3]; + int cmdflags = params[6]; + + pContext->LocalToString(params[1], &name); + pContext->LocalToString(params[4], &help); + pContext->LocalToString(params[5], (char **)&group); + pFunction = pContext->GetFunctionById(params[2]); + + if (group[0] == '\0') + { + CPlugin *pPlugin = g_PluginSys.GetPluginByCtx(pContext->GetContext()); + group = pPlugin->GetFilename(); + } + + if (!pFunction) + { + return pContext->ThrowNativeError("Invalid function id (%X)", params[2]); + } + + if (!g_ConCmds.AddAdminCommand(pFunction, name, group, flags, help, cmdflags)) + { + return pContext->ThrowNativeError("Command \"%s\" could not be created. A convar with the same name already exists.", name); + } + + return 1; +} + +static cell_t sm_GetCmdArgs(IPluginContext *pContext, const cell_t *params) +{ + const CCommand *pCmd = g_HL2.PeekCommandStack(); + + if (!pCmd) + { + return pContext->ThrowNativeError("No command callback available"); + } + + return pCmd->ArgC() - 1; +} + +static cell_t sm_GetCmdArg(IPluginContext *pContext, const cell_t *params) +{ + const CCommand *pCmd = g_HL2.PeekCommandStack(); + + if (!pCmd) + { + return pContext->ThrowNativeError("No command callback available"); + } + + size_t length; + + const char *arg = pCmd->Arg(params[1]); + + pContext->StringToLocalUTF8(params[2], params[3], arg ? arg : "", &length); + + return (cell_t)length; +} + +static cell_t sm_GetCmdArgString(IPluginContext *pContext, const cell_t *params) +{ + const CCommand *pCmd = g_HL2.PeekCommandStack(); + + if (!pCmd) + { + return pContext->ThrowNativeError("No command callback available"); + } + + const char *args = pCmd->ArgS(); + size_t length; + + if (!args) + { + args = ""; + } + + pContext->StringToLocalUTF8(params[1], params[2], args, &length); + + return (cell_t)length; +} + +static cell_t sm_PrintToServer(IPluginContext *pCtx, const cell_t *params) +{ + char buffer[1024]; + char *fmt; + int arg = 2; + + pCtx->LocalToString(params[1], &fmt); + size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg); + + buffer[res++] = '\n'; + buffer[res] = '\0'; + + META_CONPRINT(buffer); + + return 1; +} + +static cell_t sm_PrintToConsole(IPluginContext *pCtx, const cell_t *params) +{ + int index = params[1]; + if ((index < 0) || (index > g_Players.GetMaxClients())) + { + return pCtx->ThrowNativeError("Client index %d is invalid", index); + } + + CPlayer *pPlayer = NULL; + if (index != 0) + { + pPlayer = g_Players.GetPlayerByIndex(index); + if (!pPlayer->IsInGame()) + { + return pCtx->ThrowNativeError("Client %d is not in game", index); + } + + /* Silent fail on bots, engine will crash */ + if (pPlayer->IsFakeClient()) + { + return 0; + } + } + + char buffer[1024]; + char *fmt; + int arg = 3; + + pCtx->LocalToString(params[2], &fmt); + size_t res = atcprintf(buffer, sizeof(buffer)-2, fmt, pCtx, params, &arg); + + buffer[res++] = '\n'; + buffer[res] = '\0'; + + if (index != 0) + { + engine->ClientPrintf(pPlayer->GetEdict(), buffer); + } else { + META_CONPRINT(buffer); + } + + return 1; +} + +static cell_t sm_ServerCommand(IPluginContext *pContext, const cell_t *params) +{ + g_SourceMod.SetGlobalTarget(LANG_SERVER); + + char buffer[1024]; + size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1); + + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + + /* One byte for null terminator, one for newline */ + buffer[len++] = '\n'; + buffer[len] = '\0'; + + engine->ServerCommand(buffer); + + return 1; +} + +static cell_t sm_InsertServerCommand(IPluginContext *pContext, const cell_t *params) +{ + g_SourceMod.SetGlobalTarget(LANG_SERVER); + + char buffer[1024]; + size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 1); + + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + + /* One byte for null terminator, one for newline */ + buffer[len++] = '\n'; + buffer[len] = '\0'; + + InsertServerCommand(buffer); + + return 1; +} + +static cell_t sm_ServerExecute(IPluginContext *pContext, const cell_t *params) +{ + engine->ServerExecute(); + + return 1; +} + +static cell_t sm_ClientCommand(IPluginContext *pContext, const cell_t *params) +{ + CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", params[1]); + } + + if (!pPlayer->IsConnected()) + { + return pContext->ThrowNativeError("Client %d is not connected", params[1]); + } + + g_SourceMod.SetGlobalTarget(params[1]); + + char buffer[256]; + g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2); + + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + + engine->ClientCommand(pPlayer->GetEdict(), "%s", buffer); + + return 1; +} + +static cell_t FakeClientCommand(IPluginContext *pContext, const cell_t *params) +{ + CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", params[1]); + } + + if (!pPlayer->IsConnected()) + { + return pContext->ThrowNativeError("Client %d is not connected", params[1]); + } + + char buffer[256]; + g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2); + + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + + serverpluginhelpers->ClientCommand(pPlayer->GetEdict(), buffer); + + return 1; +} + +static cell_t FakeClientCommandEx(IPluginContext *pContext, const cell_t *params) +{ + CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", params[1]); + } + + if (!pPlayer->IsConnected()) + { + return pContext->ThrowNativeError("Client %d is not connected", params[1]); + } + + char buffer[256]; + g_SourceMod.FormatString(buffer, sizeof(buffer), pContext, params, 2); + + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + + g_HL2.AddToFakeCliCmdQueue(params[1], engine->GetPlayerUserId(pPlayer->GetEdict()), buffer); + + return 1; +} + +static cell_t ReplyToCommand(IPluginContext *pContext, const cell_t *params) +{ + g_SourceMod.SetGlobalTarget(params[1]); + + /* Build the format string */ + char buffer[1024]; + size_t len = g_SourceMod.FormatString(buffer, sizeof(buffer)-2, pContext, params, 2); + + if (pContext->GetContext()->n_err != SP_ERROR_NONE) + { + return 0; + } + + /* If we're printing to the server, shortcut out */ + if (params[1] == 0) + { + /* Print */ + buffer[len++] = '\n'; + buffer[len] = '\0'; + META_CONPRINT(buffer); + return 1; + } + + CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", params[1]); + } + + if (!pPlayer->IsConnected()) + { + return pContext->ThrowNativeError("Client %d is not connected", params[1]); + } + + g_SourceMod.SetGlobalTarget(params[1]); + + unsigned int replyto = g_ChatTriggers.GetReplyTo(); + if (replyto == SM_REPLY_CONSOLE) + { + buffer[len++] = '\n'; + buffer[len] = '\0'; + engine->ClientPrintf(pPlayer->GetEdict(), buffer); + } else if (replyto == SM_REPLY_CHAT) { + if (len >= 191) + { + len = 191; + } + buffer[len] = '\0'; + g_HL2.TextMsg(params[1], HUD_PRINTTALK, buffer); + } + + return 1; +} + +static cell_t GetCmdReplyTarget(IPluginContext *pContext, const cell_t *params) +{ + return g_ChatTriggers.GetReplyTo(); +} + +static cell_t SetCmdReplyTarget(IPluginContext *pContext, const cell_t *params) +{ + return g_ChatTriggers.SetReplyTo(params[1]); +} + +static cell_t GetCommandIterator(IPluginContext *pContext, const cell_t *params) +{ + GlobCmdIter *iter = new GlobCmdIter; + iter->started = false; + + Handle_t hndl = g_HandleSys.CreateHandle(hCmdIterType, iter, pContext->GetIdentity(), g_pCoreIdent, NULL); + if (hndl == BAD_HANDLE) + { + delete iter; + } + + return hndl; +} + +static cell_t ReadCommandIterator(IPluginContext *pContext, const cell_t *params) +{ + GlobCmdIter *iter; + HandleError err; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], hCmdIterType, &sec, (void **)&iter)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid GlobCmdIter Handle %x", params[1]); + } + + const List &cmds = g_ConCmds.GetCommandList(); + + if (!iter->started) + { + iter->iter = cmds.begin(); + iter->started = true; + } + + while (iter->iter != cmds.end() + && !(*(iter->iter))->sourceMod) + { + iter->iter++; + } + + if (iter->iter == cmds.end()) + { + return 0; + } + + ConCmdInfo *pInfo = (*(iter->iter)); + + pContext->StringToLocalUTF8(params[2], params[3], pInfo->pCmd->GetName(), NULL); + pContext->StringToLocalUTF8(params[5], params[6], pInfo->pCmd->GetHelpText(), NULL); + + cell_t *addr; + pContext->LocalToPhysAddr(params[4], &addr); + *addr = pInfo->admin.eflags; + + iter->iter++; + + return 1; +} + +static cell_t CheckCommandAccess(IPluginContext *pContext, const cell_t *params) +{ + if (params[1] == 0) + { + return 1; + } + + char *cmd; + pContext->LocalToString(params[2], &cmd); + + /* Auto-detect a command if we can */ + FlagBits bits = params[3]; + bool found_command = false; + if (params[0] < 4 || !params[4]) + { + found_command = g_ConCmds.LookForCommandAdminFlags(cmd, &bits); + } + + if (!found_command) + { + g_Admins.GetCommandOverride(cmd, Override_Command, &bits); + } + + return g_ConCmds.CheckCommandAccess(params[1], cmd, bits) ? 1 : 0; +} + +static cell_t IsChatTrigger(IPluginContext *pContext, const cell_t *params) +{ + return g_ChatTriggers.IsChatTrigger() ? 1 : 0; +} + +static cell_t SetCommandFlags(IPluginContext *pContext, const cell_t *params) +{ + char *name; + pContext->LocalToString(params[1], &name); + + return (s_CommandFlagsHelper.SetFlags(name, params[2])) ? 1 : 0; +} + +static cell_t GetCommandFlags(IPluginContext *pContext, const cell_t *params) +{ + char *name; + int flags; + pContext->LocalToString(params[1], &name); + + return (s_CommandFlagsHelper.GetFlags(name, &flags)) ? flags : -1; +} + +static cell_t FindFirstConCommand(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl; + ConCmdIter *pIter; + cell_t *pIsCmd, *pFlags; + const ConCommandBase *pConCmd; + const char *desc; + + pContext->LocalToPhysAddr(params[3], &pIsCmd); + pContext->LocalToPhysAddr(params[4], &pFlags); + + pConCmd = icvar->GetCommands(); + + if (pConCmd == NULL) + { + return BAD_HANDLE; + } + + pContext->StringToLocalUTF8(params[1], params[2], pConCmd->GetName(), NULL); + *pIsCmd = pConCmd->IsCommand() ? 1 : 0; + *pFlags = pConCmd->GetFlags(); + + if (params[6]) + { + desc = pConCmd->GetHelpText(); + pContext->StringToLocalUTF8(params[5], params[6], (desc && desc[0]) ? desc : "", NULL); + } + + pIter = new ConCmdIter; + pIter->pLast = pConCmd; + + if ((hndl = g_HandleSys.CreateHandle(htConCmdIter, pIter, pContext->GetIdentity(), g_pCoreIdent, NULL)) + == BAD_HANDLE) + { + delete pIter; + return BAD_HANDLE; + } + + return hndl; +} + +static cell_t FindNextConCommand(IPluginContext *pContext, const cell_t *params) +{ + HandleError err; + ConCmdIter *pIter; + cell_t *pIsCmd, *pFlags; + const char *desc; + HandleSecurity sec(pContext->GetIdentity(), g_pCoreIdent); + + if ((err = g_HandleSys.ReadHandle(params[1], htConCmdIter, &sec, (void **)&pIter)) != HandleError_None) + { + return pContext->ThrowNativeError("Invalid Handle %x (error %d)", params[1], err); + } + + if (pIter->pLast == NULL) + { + return 0; + } + + if ((pIter->pLast = pIter->pLast->GetNext()) == NULL) + { + return 0; + } + + pContext->LocalToPhysAddr(params[4], &pIsCmd); + pContext->LocalToPhysAddr(params[5], &pFlags); + + pContext->StringToLocalUTF8(params[2], params[3], pIter->pLast->GetName(), NULL); + *pIsCmd = pIter->pLast->IsCommand() ? 1 : 0; + *pFlags = pIter->pLast->GetFlags(); + + if (params[7]) + { + desc = pIter->pLast->GetHelpText(); + pContext->StringToLocalUTF8(params[6], params[7], (desc && desc[0]) ? desc : "", NULL); + } + + return 1; +} + +static cell_t SendConVarValue(IPluginContext *pContext, const cell_t *params) +{ + Handle_t hndl = static_cast(params[2]); + HandleError err; + ConVar *pConVar; + + char *value; + pContext->LocalToString(params[3], &value); + + if ((err=g_ConVarManager.ReadConVarHandle(hndl, &pConVar)) + != HandleError_None) + { + return pContext->ThrowNativeError("Invalid convar handle %x (error %d)", hndl, err); + } + + char data[256]; + bf_write buffer(data, sizeof(data)); + + buffer.WriteUBitLong(NET_SETCONVAR, 5); + buffer.WriteByte(1); + buffer.WriteString(pConVar->GetName()); + buffer.WriteString(value); + + CPlayer *pPlayer = g_Players.GetPlayerByIndex(params[1]); + + if (!pPlayer) + { + return pContext->ThrowNativeError("Client index %d is invalid", params[1]); + } + + if (!pPlayer->IsConnected()) + { + return pContext->ThrowNativeError("Client %d is not connected", params[1]); + } + + if (pPlayer->IsFakeClient()) + { + return pContext->ThrowNativeError("Client %d is fake and cannot be targeted", params[1]); + } + + INetChannel *netchan = static_cast(engine->GetPlayerNetInfo(params[1])); + netchan->SendData(buffer); + + return 1; +} + +REGISTER_NATIVES(consoleNatives) +{ + {"CreateConVar", sm_CreateConVar}, + {"FindConVar", sm_FindConVar}, + {"HookConVarChange", sm_HookConVarChange}, + {"UnhookConVarChange", sm_UnhookConVarChange}, + {"GetConVarBool", sm_GetConVarBool}, + {"SetConVarBool", sm_SetConVarNum}, + {"GetConVarInt", sm_GetConVarInt}, + {"SetConVarInt", sm_SetConVarNum}, + {"GetConVarFloat", sm_GetConVarFloat}, + {"SetConVarFloat", sm_SetConVarFloat}, + {"GetConVarString", sm_GetConVarString}, + {"SetConVarString", sm_SetConVarString}, + {"GetConVarFlags", sm_GetConVarFlags}, + {"SetConVarFlags", sm_SetConVarFlags}, + {"ResetConVar", sm_ResetConVar}, + {"GetConVarName", sm_GetConVarName}, + {"GetConVarBounds", sm_GetConVarBounds}, + {"SetConVarBounds", sm_SetConVarBounds}, + {"QueryClientConVar", sm_QueryClientConVar}, + {"RegServerCmd", sm_RegServerCmd}, + {"RegConsoleCmd", sm_RegConsoleCmd}, + {"GetCmdArgString", sm_GetCmdArgString}, + {"GetCmdArgs", sm_GetCmdArgs}, + {"GetCmdArg", sm_GetCmdArg}, + {"PrintToServer", sm_PrintToServer}, + {"PrintToConsole", sm_PrintToConsole}, + {"RegAdminCmd", sm_RegAdminCmd}, + {"ServerCommand", sm_ServerCommand}, + {"InsertServerCommand", sm_InsertServerCommand}, + {"ServerExecute", sm_ServerExecute}, + {"ClientCommand", sm_ClientCommand}, + {"FakeClientCommand", FakeClientCommand}, + {"ReplyToCommand", ReplyToCommand}, + {"GetCmdReplySource", GetCmdReplyTarget}, + {"SetCmdReplySource", SetCmdReplyTarget}, + {"GetCommandIterator", GetCommandIterator}, + {"ReadCommandIterator", ReadCommandIterator}, + {"CheckCommandAccess", CheckCommandAccess}, + {"FakeClientCommandEx", FakeClientCommandEx}, + {"IsChatTrigger", IsChatTrigger}, + {"SetCommandFlags", SetCommandFlags}, + {"GetCommandFlags", GetCommandFlags}, + {"FindFirstConCommand", FindFirstConCommand}, + {"FindNextConCommand", FindNextConCommand}, + {"SendConVarValue", SendConVarValue}, + {NULL, NULL} +};