mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-10 10:08:18 +00:00
added moderator support for the remote console. #518
This commit is contained in:
parent
5008b5f8a5
commit
1ae474689d
|
@ -14,7 +14,10 @@ public:
|
|||
{
|
||||
OUTPUT_LEVEL_STANDARD=0,
|
||||
OUTPUT_LEVEL_ADDINFO,
|
||||
OUTPUT_LEVEL_DEBUG
|
||||
OUTPUT_LEVEL_DEBUG,
|
||||
|
||||
ACCESS_LEVEL_ADMIN=0,
|
||||
ACCESS_LEVEL_MOD,
|
||||
};
|
||||
|
||||
// TODO: rework this interface to reduce the amount of virtual calls
|
||||
|
@ -62,6 +65,8 @@ public:
|
|||
|
||||
virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) = 0;
|
||||
virtual void Print(int Level, const char *pFrom, const char *pStr) = 0;
|
||||
|
||||
virtual void SetAccessLevel(int AccessLevel) = 0;
|
||||
};
|
||||
|
||||
extern IConsole *CreateConsole(int FlagMask);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
|
||||
#include <engine/config.h>
|
||||
|
@ -181,6 +182,7 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta)
|
|||
m_MapReload = 0;
|
||||
|
||||
m_RconClientID = -1;
|
||||
m_RconAuthLevel = AUTHED_ADMIN;
|
||||
|
||||
Init();
|
||||
}
|
||||
|
@ -280,6 +282,11 @@ void CServer::Kick(int ClientID, const char *pReason)
|
|||
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't kick yourself");
|
||||
return;
|
||||
}
|
||||
else if(m_aClients[ClientID].m_Authed > m_RconAuthLevel)
|
||||
{
|
||||
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "kick command denied");
|
||||
return;
|
||||
}
|
||||
|
||||
m_NetServer.Drop(ClientID, pReason);
|
||||
}
|
||||
|
@ -571,7 +578,7 @@ int CServer::NewClientCallback(int ClientID, void *pUser)
|
|||
pThis->m_aClients[ClientID].m_aName[0] = 0;
|
||||
pThis->m_aClients[ClientID].m_aClan[0] = 0;
|
||||
pThis->m_aClients[ClientID].m_Country = -1;
|
||||
pThis->m_aClients[ClientID].m_Authed = 0;
|
||||
pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
|
||||
pThis->m_aClients[ClientID].m_AuthTries = 0;
|
||||
pThis->m_aClients[ClientID].Reset();
|
||||
return 0;
|
||||
|
@ -596,7 +603,7 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
|
|||
pThis->m_aClients[ClientID].m_aName[0] = 0;
|
||||
pThis->m_aClients[ClientID].m_aClan[0] = 0;
|
||||
pThis->m_aClients[ClientID].m_Country = -1;
|
||||
pThis->m_aClients[ClientID].m_Authed = 0;
|
||||
pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
|
||||
pThis->m_aClients[ClientID].m_AuthTries = 0;
|
||||
pThis->m_aClients[ClientID].m_Snapshots.PurgeAll();
|
||||
return 0;
|
||||
|
@ -635,7 +642,7 @@ void CServer::SendRconLineAuthed(const char *pLine, void *pUser)
|
|||
|
||||
for(i = 0; i < MAX_CLIENTS; i++)
|
||||
{
|
||||
if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed)
|
||||
if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel)
|
||||
pThis->SendRconLine(i, pLine);
|
||||
}
|
||||
|
||||
|
@ -813,8 +820,12 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
|
|||
str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd);
|
||||
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
|
||||
m_RconClientID = ClientID;
|
||||
m_RconAuthLevel = m_aClients[ClientID].m_Authed;
|
||||
Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD);
|
||||
Console()->ExecuteLine(pCmd);
|
||||
Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
|
||||
m_RconClientID = -1;
|
||||
m_RconAuthLevel = AUTHED_ADMIN;
|
||||
}
|
||||
}
|
||||
else if(Msg == NETMSG_RCON_AUTH)
|
||||
|
@ -825,9 +836,9 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
|
|||
|
||||
if(Unpacker.Error() == 0)
|
||||
{
|
||||
if(g_Config.m_SvRconPassword[0] == 0)
|
||||
if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0])
|
||||
{
|
||||
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
|
||||
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console.");
|
||||
}
|
||||
else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
|
||||
{
|
||||
|
@ -835,10 +846,22 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
|
|||
Msg.AddInt(1);
|
||||
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
|
||||
|
||||
m_aClients[ClientID].m_Authed = 1;
|
||||
SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
|
||||
m_aClients[ClientID].m_Authed = AUTHED_ADMIN;
|
||||
SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted.");
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID);
|
||||
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID);
|
||||
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
||||
}
|
||||
else if(str_comp(pPw, g_Config.m_SvRconModPassword) == 0)
|
||||
{
|
||||
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
|
||||
Msg.AddInt(1);
|
||||
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
|
||||
|
||||
m_aClients[ClientID].m_Authed = AUTHED_MOD;
|
||||
SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted.");
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID);
|
||||
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
|
||||
}
|
||||
else if(g_Config.m_SvRconMaxTries)
|
||||
|
@ -1308,6 +1331,20 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
|
|||
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
|
||||
return;
|
||||
}
|
||||
|
||||
for(int i = 0; i < MAX_CLIENTS; ++i)
|
||||
{
|
||||
if(i == pServer->m_RconClientID)
|
||||
continue;
|
||||
|
||||
AddrCheck = pServer->m_NetServer.ClientAddr(i);
|
||||
AddrCheck.port = 0;
|
||||
if(net_addr_comp(&Addr, &AddrCheck) == 0 && pServer->m_aClients[i].m_Authed > pServer->m_RconAuthLevel)
|
||||
{
|
||||
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
pServer->BanAdd(Addr, Minutes*60, pReason);
|
||||
}
|
||||
|
@ -1325,6 +1362,11 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
|
|||
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
|
||||
return;
|
||||
}
|
||||
else if(pServer->m_aClients[ClientID].m_Authed > pServer->m_RconAuthLevel)
|
||||
{
|
||||
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
|
||||
return;
|
||||
}
|
||||
|
||||
Addr = pServer->m_NetServer.ClientAddr(ClientID);
|
||||
pServer->BanAdd(Addr, Minutes*60, pReason);
|
||||
|
|
|
@ -49,6 +49,13 @@ public:
|
|||
class IConsole *Console() { return m_pConsole; }
|
||||
class IStorage *Storage() { return m_pStorage; }
|
||||
|
||||
enum
|
||||
{
|
||||
AUTHED_NO=0,
|
||||
AUTHED_MOD,
|
||||
AUTHED_ADMIN,
|
||||
};
|
||||
|
||||
class CClient
|
||||
{
|
||||
public:
|
||||
|
@ -110,6 +117,7 @@ public:
|
|||
int m_RunServer;
|
||||
int m_MapReload;
|
||||
int m_RconClientID;
|
||||
int m_RconAuthLevel;
|
||||
|
||||
int64 m_Lastheartbeat;
|
||||
//static NETADDR4 master_server;
|
||||
|
|
|
@ -82,7 +82,8 @@ MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER
|
|||
MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 4, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server")
|
||||
MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
|
||||
MACRO_CONFIG_INT(SvRegister, sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing")
|
||||
MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password")
|
||||
MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password (full access)")
|
||||
MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 32, "", CFGFLAG_SERVER, "Remote console password for moderators (limited access)")
|
||||
MACRO_CONFIG_INT(SvRconMaxTries, sv_rcon_max_tries, 3, 0, 100, CFGFLAG_SERVER, "Maximum number of tries for remote console authentication")
|
||||
MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if remote console authentication fails. 0 makes it just use kick")
|
||||
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
||||
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
||||
#include <new>
|
||||
|
||||
#include <base/math.h>
|
||||
#include <base/system.h>
|
||||
#include <engine/shared/protocol.h>
|
||||
|
||||
#include <engine/storage.h>
|
||||
#include "console.h"
|
||||
#include <engine/shared/protocol.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "console.h"
|
||||
#include "linereader.h"
|
||||
|
||||
const char *CConsole::CResult::GetString(unsigned Index)
|
||||
|
@ -247,38 +251,47 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
|
|||
if(ParseStart(&Result, pStr, (pEnd-pStr) + 1) != 0)
|
||||
return;
|
||||
|
||||
if (!*Result.m_pCommand)
|
||||
if(!*Result.m_pCommand)
|
||||
return;
|
||||
|
||||
CCommand *pCommand = FindCommand(Result.m_pCommand, m_FlagMask);
|
||||
|
||||
if(pCommand)
|
||||
{
|
||||
int IsStrokeCommand = 0;
|
||||
if(Result.m_pCommand[0] == '+')
|
||||
if(pCommand->m_AccessLevel >= m_AccessLevel)
|
||||
{
|
||||
// insert the stroke direction token
|
||||
Result.AddArgument(m_paStrokeStr[Stroke]);
|
||||
IsStrokeCommand = 1;
|
||||
}
|
||||
int IsStrokeCommand = 0;
|
||||
if(Result.m_pCommand[0] == '+')
|
||||
{
|
||||
// insert the stroke direction token
|
||||
Result.AddArgument(m_paStrokeStr[Stroke]);
|
||||
IsStrokeCommand = 1;
|
||||
}
|
||||
|
||||
if(Stroke || IsStrokeCommand)
|
||||
if(Stroke || IsStrokeCommand)
|
||||
{
|
||||
if(ParseArgs(&Result, pCommand->m_pParams))
|
||||
{
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
|
||||
Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
|
||||
}
|
||||
else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE)
|
||||
{
|
||||
m_ExecutionQueue.AddEntry();
|
||||
m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback;
|
||||
m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData;
|
||||
m_ExecutionQueue.m_pLast->m_Result = Result;
|
||||
}
|
||||
else
|
||||
pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
|
||||
}
|
||||
}
|
||||
else if(Stroke)
|
||||
{
|
||||
if(ParseArgs(&Result, pCommand->m_pParams))
|
||||
{
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
|
||||
Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
|
||||
}
|
||||
else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE)
|
||||
{
|
||||
m_ExecutionQueue.AddEntry();
|
||||
m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback;
|
||||
m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData;
|
||||
m_ExecutionQueue.m_pLast->m_Result = Result;
|
||||
}
|
||||
else
|
||||
pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
|
||||
char aBuf[256];
|
||||
str_format(aBuf, sizeof(aBuf), "Access for command %s denied.", Result.m_pCommand);
|
||||
Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
|
||||
}
|
||||
}
|
||||
else if(Stroke)
|
||||
|
@ -294,8 +307,7 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
|
|||
|
||||
void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser)
|
||||
{
|
||||
CCommand *pCommand;
|
||||
for(pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
|
||||
for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
|
||||
{
|
||||
if(pCommand->m_Flags&FlagMask)
|
||||
{
|
||||
|
@ -307,8 +319,7 @@ void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallbac
|
|||
|
||||
CConsole::CCommand *CConsole::FindCommand(const char *pName, int FlagMask)
|
||||
{
|
||||
CCommand *pCommand;
|
||||
for (pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
|
||||
for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
|
||||
{
|
||||
if(pCommand->m_Flags&FlagMask)
|
||||
{
|
||||
|
@ -383,6 +394,62 @@ void CConsole::Con_Exec(IResult *pResult, void *pUserData)
|
|||
((CConsole*)pUserData)->ExecuteFile(pResult->GetString(0));
|
||||
}
|
||||
|
||||
void CConsole::ConModCommandAccess(IResult *pResult, void *pUser)
|
||||
{
|
||||
CConsole* pConsole = static_cast<CConsole *>(pUser);
|
||||
char aBuf[128];
|
||||
CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), CFGFLAG_SERVER);
|
||||
if(pCommand)
|
||||
{
|
||||
if(pResult->NumArguments() == 2)
|
||||
{
|
||||
pCommand->m_AccessLevel = clamp(pResult->GetInteger(1), (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD));
|
||||
str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is now %s", pResult->GetString(0), pCommand->m_AccessLevel ? "enabled" : "disabled");
|
||||
}
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is %s", pResult->GetString(0), pCommand->m_AccessLevel ? "enabled" : "disabled");
|
||||
}
|
||||
else
|
||||
str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(0));
|
||||
|
||||
pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
|
||||
}
|
||||
|
||||
void CConsole::ConModCommandStatus(IResult *pResult, void *pUser)
|
||||
{
|
||||
CConsole* pConsole = static_cast<CConsole *>(pUser);
|
||||
char aBuf[240];
|
||||
mem_zero(aBuf, sizeof(aBuf));
|
||||
int Used = 0;
|
||||
|
||||
for(CCommand *pCommand = pConsole->m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
|
||||
{
|
||||
if(pCommand->m_Flags&pConsole->m_FlagMask && pCommand->m_AccessLevel == ACCESS_LEVEL_MOD)
|
||||
{
|
||||
int Length = str_length(pCommand->m_pName);
|
||||
if(Used + Length + 2 < (int)(sizeof(aBuf)))
|
||||
{
|
||||
if(Used > 0)
|
||||
{
|
||||
Used += 2;
|
||||
str_append(aBuf, ", ", sizeof(aBuf));
|
||||
}
|
||||
str_append(aBuf, pCommand->m_pName, sizeof(aBuf));
|
||||
Used += Length;
|
||||
}
|
||||
else
|
||||
{
|
||||
pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
|
||||
mem_zero(aBuf, sizeof(aBuf));
|
||||
str_copy(aBuf, pCommand->m_pName, sizeof(aBuf));
|
||||
Used = Length;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Used > 0)
|
||||
pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
|
||||
}
|
||||
|
||||
struct CIntVariableData
|
||||
{
|
||||
IConsole *m_pConsole;
|
||||
|
@ -463,6 +530,7 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData)
|
|||
CConsole::CConsole(int FlagMask)
|
||||
{
|
||||
m_FlagMask = FlagMask;
|
||||
m_AccessLevel = ACCESS_LEVEL_ADMIN;
|
||||
m_StoreCommands = true;
|
||||
m_paStrokeStr[0] = "0";
|
||||
m_paStrokeStr[1] = "1";
|
||||
|
@ -478,6 +546,9 @@ CConsole::CConsole(int FlagMask)
|
|||
Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text");
|
||||
Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file");
|
||||
|
||||
Register("mod_command", "s?i", CFGFLAG_SERVER, ConModCommandAccess, this, "Specify command accessibility for moderators");
|
||||
Register("mod_status", "", CFGFLAG_SERVER, ConModCommandStatus, this, "List all commands which are accessible for moderators");
|
||||
|
||||
// TODO: this should disappear
|
||||
#define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc) \
|
||||
{ \
|
||||
|
@ -524,14 +595,14 @@ void CConsole::ParseArguments(int NumArgs, const char **ppArguments)
|
|||
void CConsole::Register(const char *pName, const char *pParams,
|
||||
int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp)
|
||||
{
|
||||
CCommand *pCommand = (CCommand *)mem_alloc(sizeof(CCommand), sizeof(void*));
|
||||
CCommand *pCommand = new(mem_alloc(sizeof(CCommand), sizeof(void*))) CCommand;
|
||||
pCommand->m_pfnCallback = pfnFunc;
|
||||
pCommand->m_pUserData = pUser;
|
||||
pCommand->m_pHelp = pHelp;
|
||||
pCommand->m_pName = pName;
|
||||
pCommand->m_pParams = pParams;
|
||||
pCommand->m_Flags = Flags;
|
||||
|
||||
pCommand->m_AccessLevel = ACCESS_LEVEL_ADMIN;
|
||||
|
||||
pCommand->m_pNext = m_pFirstCommand;
|
||||
m_pFirstCommand = pCommand;
|
||||
|
|
|
@ -13,6 +13,7 @@ class CConsole : public IConsole
|
|||
public:
|
||||
CCommand *m_pNext;
|
||||
int m_Flags;
|
||||
int m_AccessLevel;
|
||||
FCommandCallback m_pfnCallback;
|
||||
void *m_pUserData;
|
||||
};
|
||||
|
@ -41,10 +42,13 @@ class CConsole : public IConsole
|
|||
|
||||
CExecFile *m_pFirstExec;
|
||||
class IStorage *m_pStorage;
|
||||
int m_AccessLevel;
|
||||
|
||||
static void Con_Chain(IResult *pResult, void *pUserData);
|
||||
static void Con_Echo(IResult *pResult, void *pUserData);
|
||||
static void Con_Exec(IResult *pResult, void *pUserData);
|
||||
static void ConModCommandAccess(IResult *pResult, void *pUser);
|
||||
static void ConModCommandStatus(IConsole::IResult *pResult, void *pUser);
|
||||
|
||||
void ExecuteFileRecurse(const char *pFilename);
|
||||
void ExecuteLineStroked(int Stroke, const char *pStr);
|
||||
|
@ -153,6 +157,8 @@ public:
|
|||
|
||||
virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData);
|
||||
virtual void Print(int Level, const char *pFrom, const char *pStr);
|
||||
|
||||
void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD)); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue