Advanced RCON: Command output is sent to the RCON client.

RCON authentication is now required before executing commands.
Console command handlers now return two values, bool (IsHandled) and string (CommandOutput).
API change: removed cRoot:ExecuteConsoleCommand(), added cRoot:QueueExecuteConsoleCommand().
API change: removed cPluginManager:ExecuteConsoleCommand(), use cRoot:QueueExecuteConsoleCommand() instead

git-svn-id: http://mc-server.googlecode.com/svn/trunk@1631 0a769ca7-a7f5-676a-18bf-c427514a06d6
master
madmaxoft@gmail.com 2013-06-29 15:30:05 +00:00
parent cff6ff2223
commit 7b75aaea7c
22 changed files with 599 additions and 258 deletions

View File

@ -18,23 +18,27 @@ function HandleBanCommand( Split, Player )
return true
end
function BanPlayer( PlayerName, Reason )
if( Reason == nil ) then
function BanPlayer(PlayerName, Reason)
-- Ban the player in the banned.ini:
BannedPlayersIni:SetValueB("Banned", PlayerName, true)
BannedPlayersIni:WriteFile()
-- Kick the player:
if (Reason == nil) then
Reason = "You have been banned"
end
local Success, RealName = KickPlayer( PlayerName, Reason )
if( Success == false ) then
return false
local Success = KickPlayer(PlayerName, Reason)
if (not(Success)) then
return false;
end
LOGINFO( "'" .. RealName .. "' is being banned for ( "..Reason..") " )
local Server = cRoot:Get():GetServer()
Server:SendMessage( "Banning " .. RealName )
BannedPlayersIni:SetValueB("Banned", RealName, true)
BannedPlayersIni:WriteFile()
LOGINFO("'" .. PlayerName .. "' has been banned (\"" .. Reason .. "\") ");
local Server = cRoot:Get():GetServer();
Server:SendMessage("Banned " .. PlayerName);
return true
end

View File

@ -9,16 +9,60 @@
function InitConsoleCommands()
local PluginMgr = cPluginManager:Get();
-- Please keep the list alpha-sorted
PluginMgr:BindConsoleCommand("ban", HandleConsoleBan, "Bans a player by name");
PluginMgr:BindConsoleCommand("banlist", HandleConsoleBanList, "Lists all players banned by name");
PluginMgr:BindConsoleCommand("banlist ips", HandleConsoleBanList, "Lists all players banned by IP");
PluginMgr:BindConsoleCommand("help", HandleConsoleHelp, "Lists all commands");
PluginMgr:BindConsoleCommand("list", HandleConsoleList, "Lists all players in a machine-readable format");
PluginMgr:BindConsoleCommand("listgroups", HandleConsoleListGroups, "Shows a list of all the groups");
PluginMgr:BindConsoleCommand("numchunks", HandleConsoleNumChunks, "Shows number of chunks currently loaded");
PluginMgr:BindConsoleCommand("players", HandleConsolePlayers, "Lists all connected players");
PluginMgr:BindConsoleCommand("primaryserverversion", HandleConsolePrimaryServerVersion, "Gets or sets server version reported to 1.4+ clients");
PluginMgr:BindConsoleCommand("rank", HandleConsoleRank, " [Player] [Group] - add a player to a group");
PluginMgr:BindConsoleCommand("reload", HandleConsoleReload, "Reloads all plugins");
PluginMgr:BindConsoleCommand("save-all", HandleConsoleSaveAll, "Saves all chunks");
PluginMgr:BindConsoleCommand("say", HandleConsoleSay, "Sends a chat message to all players");
PluginMgr:BindConsoleCommand("unload", HandleConsoleUnload, "Unloads all unused chunks");
PluginMgr:BindConsoleCommand("rank", HandleConsoleRank, " [Player] [Rank] - to add someone to a group");
PluginMgr:BindConsoleCommand("listgroups", HandleConsoleListGroups, "Shows a list of all the groups");
end
function HandleConsoleBan(Split)
if (#Split < 2) then
return true, cChatColor.Green .. "Usage: /ban [Player] <Reason>";
end
local Reason = "You have been banned"
if (#Split > 2) then
Reason = table.concat(Split, " ", 3);
end
if (not(BanPlayer(Split[2], Reason))) then
return true, cChatColor.Green .. "Could not find player " .. Split[2];
end
return true, "Player " .. Split[2] .. " has been banned.";
end
function HandleConsoleBanList(Split)
if (#Split == 1) then
return true, BanListByName();
end
if (string.lower(Split[2]) == "ips") then
return true, BanListByIPs();
end
return true, "Unknown banlist subcommand";
end
@ -44,11 +88,55 @@ function HandleConsoleHelp(Split)
end
table.sort(Commands, CompareCommands);
local Out = "";
for i, Command in ipairs(Commands) do
local Cmd = Command[1] .. string.rep(" ", MaxLength - Command[1]:len()); -- Align to a table
LOG(Cmd .. " - " .. Command[2]);
Out = Out .. Command[1] .. string.rep(" ", MaxLength - Command[1]:len()); -- Align to a table
Out = Out .. " - " .. Command[2] .. "\n";
end
return true;
return true, Out;
end
function HandleConsoleList(Split)
-- Get a list of all players, one playername per line
local Out = "";
cRoot:Get():ForEachWorld(
function (a_World)
a_World:ForEachPlayer(
function (a_Player)
Out = Out .. a_Player:GetName() .. "\n";
end
);
end
);
return true, Out;
end
function HandleConsoleListGroups(Split)
-- Read the groups.ini file:
local GroupsIni = cIniFile("groups.ini");
if (not(GroupsIni:ReadFile())) then
return true, "No groups found";
end
-- Read the groups:
Number = GroupsIni:NumKeys();
Groups = {};
for i = 0, Number do
table.insert(Groups, GroupsIni:KeyName(i))
end
-- Output the groups, concatenated to a string:
local Out = "Groups:\n"
Out = Out .. table.concat(Groups, ", ");
return true, Out;
end
@ -64,13 +152,14 @@ function HandleConsoleNumChunks(Split)
cRoot:Get():ForEachWorld(AddNumChunks);
local Total = 0;
local Out = "";
for name, num in pairs(Output) do
LOG(" " .. name .. ": " .. num .. " chunks");
Out = Out .. " " .. name .. ": " .. num .. " chunks\n";
Total = Total + num;
end
LOG("Total: " .. Total .. " chunks");
Out = Out .. "Total: " .. Total .. " chunks\n";
return true;
return true, Out;
end
@ -89,14 +178,15 @@ function HandleConsolePlayers(Split)
cRoot:Get():ForEachPlayer(AddToTable);
local Out = "";
for WorldName, Players in pairs(PlayersInWorlds) do
LOG("World " .. WorldName .. ":");
Out = Out .. "World " .. WorldName .. ":\n";
for i, PlayerName in ipairs(Players) do
LOG(" " .. PlayerName);
Out = Out .. " " .. PlayerName .. "\n";
end
end
return true;
return true, Out;
end
@ -107,15 +197,62 @@ function HandleConsolePrimaryServerVersion(Split)
if (#Split == 1) then
-- Display current version:
local Version = cRoot:Get():GetPrimaryServerVersion();
LOG("Primary server version: #" .. Version .. ", " .. cRoot:GetProtocolVersionTextFromInt(Version));
return true;
return true, "Primary server version: #" .. Version .. ", " .. cRoot:GetProtocolVersionTextFromInt(Version);
end
-- Set new value as the version:
cRoot:Get():SetPrimaryServerVersion(tonumber(Split[2]));
local Version = cRoot:Get():GetPrimaryServerVersion();
LOG("Primary server version is now #" .. Version .. ", " .. cRoot:GetProtocolVersionTextFromInt(Version));
return true;
return true, "Primary server version is now #" .. Version .. ", " .. cRoot:GetProtocolVersionTextFromInt(Version);
end
function HandleConsoleRank(Split)
if (Split[2] == nil) or (Split[3] == nil) then
return true, "Usage: /rank [Player] [Group]";
end
local Out = "";
-- Read the groups.ini file:
local GroupsIni = cIniFile("groups.ini")
if (not(GroupsIni:ReadFile())) then
Out = "Could not read groups.ini, creating anew!\n"
end
-- Find the group:
if (GroupsIni:FindKey(Split[3]) == -1) then
return true, Out .. "Group does not exist";
end
-- Read the users.ini file:
local UsersIni = cIniFile("users.ini");
if (not(UsersIni:ReadFile())) then
Out = Out .. "Could not read users.ini, creating anew!\n";
end
-- Write the new group value to users.ini:
UsersIni:DeleteKey(Split[2]);
UsersIni:GetValueSet(Split[2], "Groups", Split[3]);
UsersIni:WriteFile();
-- Reload the player's permissions:
cRoot:Get():ForEachWorld(
function (World)
World:ForEachPlayer(
function (Player)
if (Player:GetName() == Split[2]) then
Player:SendMessage(cChatColor.Green .. "You were moved to group " .. Split[3]);
Player:LoadPermissionsFromDisk();
end
end
);
end
)
return true, Out .. "Player " .. Split[2] .. " was moved to " .. Split[3];
end
@ -162,10 +299,10 @@ function HandleConsoleUnload(Split)
World:UnloadUnusedChunks();
end
LOGINFO("Num loaded chunks before: " .. cRoot:Get():GetTotalChunkCount());
local Out = "Num loaded chunks before: " .. cRoot:Get():GetTotalChunkCount() .. "\n";
cRoot:Get():ForEachWorld(UnloadChunks);
LOGINFO("Num loaded chunks after: " .. cRoot:Get():GetTotalChunkCount());
return true;
Out = Out .. "Num loaded chunks after: " .. cRoot:Get():GetTotalChunkCount();
return true, Out;
end
@ -173,75 +310,34 @@ end
function HandleConsoleRank(Split)
if Split[2] == nil or Split[3] == nil then
LOG("Usage: /rank [Player] [Group]")
return true
end
local GroupsIni = cIniFile("groups.ini")
if( GroupsIni:ReadFile() == false ) then
LOG("Could not read groups.ini!")
end
if GroupsIni:FindKey(Split[3]) == -1 then
LOG("Group does not exist")
return true
end
local UsersIni = cIniFile("users.ini")
if( UsersIni:ReadFile() == false ) then
LOG("Could not read users.ini!")
end
UsersIni:DeleteKey(Split[2])
UsersIni:GetValueSet(Split[2], "Groups", Split[3])
UsersIni:WriteFile()
local loopPlayers = function( Player )
if Player:GetName() == Split[2] then
Player:SendMessage( cChatColor.Green .. "You were moved to group " .. Split[3] )
Player:LoadPermissionsFromDisk()
-------------------------------------------------------------------------------------------
-- Helper functions:
--- Returns the list of players banned by name, separated by ", "
function BanListByName()
local NumValues = BannedPlayersIni:NumValues("Banned");
local Banned = {};
local KeyID = BannedPlayersIni:FindKey("Banned");
for i = 1, NumValues do
local PlayerName = BannedPlayersIni:ValueName(KeyID, i - 1);
if (BannedPlayersIni:GetValueB("Banned", PlayerName)) then
-- Player listed AND banned
table.insert(Banned, PlayerName);
end
end
local loopWorlds = function ( World )
World:ForEachPlayer( loopPlayers )
end
cRoot:Get():ForEachWorld( loopWorlds )
LOG("Player " .. Split[2] .. " Was moved to " .. Split[3])
return true
return table.concat(Banned, ", ");
end
function HandleConsoleListGroups(Split)
local GroupsIni = cIniFile("groups.ini")
if GroupsIni:ReadFile() == false then
LOG( "No groups found" )
end
Number = GroupsIni:NumKeys()
Groups = {}
for i=0, Number do
table.insert( Groups, GroupsIni:KeyName(i) )
end
LOGINFO( "Groups:" )
LOGINFO( table.concat( Groups, ", " ) )
return true
end
function HandleConsole(Split)
return true;
--- Returns the list of players banned by IP, separated by ", "
function BanListByIPs()
-- TODO: No IP ban implemented yet
return "";
end
function HandleConsole(Split)
return true;
end

View File

@ -17,26 +17,30 @@ function HandleKickCommand( Split, Player )
end
function KickPlayer( PlayerName, Reason )
local RealName = ""
local FoundPlayerCallback = function( OtherPlayer )
if( Reason == nil ) then
Reason = "You have been kicked"
end
RealName = OtherPlayer:GetName()
--- Kicks a player by name, with the specified reason; returns bool whether found and player's real name
function KickPlayer(PlayerName, Reason)
local RealName = "";
if (Reason == nil) then
Reason = "You have been kicked";
end
local FoundPlayerCallback = function(a_Player)
RealName = a_Player:GetName()
local Server = cRoot:Get():GetServer()
LOGINFO( "'" .. RealName .. "' is being kicked for ( "..Reason..") " )
Server:SendMessage( "Kicking " .. RealName )
Server:SendMessage("Kicking " .. RealName)
local ClientHandle = OtherPlayer:GetClientHandle()
ClientHandle:Kick( Reason )
a_Player:GetClientHandle():Kick(Reason);
end
if( cRoot:Get():FindAndDoWithPlayer( PlayerName, FoundPlayerCallback ) == false ) then
return false -- could not find player
if (not(cRoot:Get():FindAndDoWithPlayer( PlayerName, FoundPlayerCallback))) then
-- Could not find player
return false;
end
return true, RealName -- player should be kicked now
return true, RealName; -- Player has been kicked
end

View File

@ -1,13 +1,13 @@
function HandleRequest_ManageServer( Request )
local Content = ""
if( Request.PostParams["RestartServer"] ~= nil ) then
cRoot:Get():ExecuteConsoleCommand("restart")
elseif( Request.PostParams["ReloadServer"] ~= nil ) then
cRoot:Get():GetPluginManager():ReloadPlugins()
elseif( Request.PostParams["StopServer"] ~= nil ) then
cRoot:Get():ExecuteConsoleCommand("stop")
elseif( Request.PostParams["WorldSaveAllChunks"] ~= nil ) then
cRoot:Get():GetWorld(Request.PostParams["WorldSaveAllChunks"]):SaveAllChunks()
if (Request.PostParams["RestartServer"] ~= nil) then
cRoot:Get():QueueExecuteConsoleCommand("restart");
elseif (Request.PostParams["ReloadServer"] ~= nil) then
cRoot:Get():GetPluginManager():ReloadPlugins();
elseif (Request.PostParams["StopServer"] ~= nil) then
cRoot:Get():QueueExecuteConsoleCommand("stop");
elseif (Request.PostParams["WorldSaveAllChunks"] ~= nil) then
cRoot:Get():GetWorld(Request.PostParams["WorldSaveAllChunks"]):SaveAllChunks();
end
Content = Content .. [[
<form method="POST">

View File

@ -362,6 +362,14 @@
RelativePath="..\source\ClientHandle.h"
>
</File>
<File
RelativePath="..\source\CommandOutput.cpp"
>
</File>
<File
RelativePath="..\source\CommandOutput.h"
>
</File>
<File
RelativePath="..\source\CraftingRecipes.cpp"
>

View File

@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 06/22/13 19:31:23.
** Generated automatically by tolua++-1.0.92 on 06/29/13 17:21:35.
*/
#ifndef __cplusplus
@ -195,7 +195,7 @@ static void tolua_reg_types (lua_State* tolua_S)
{
tolua_usertype(tolua_S,"TakeDamageInfo");
tolua_usertype(tolua_S,"cCraftingRecipe");
tolua_usertype(tolua_S,"cEntity");
tolua_usertype(tolua_S,"cPlugin_NewLua");
tolua_usertype(tolua_S,"cStringMap");
tolua_usertype(tolua_S,"cItemGrid");
tolua_usertype(tolua_S,"cBlockArea");
@ -205,48 +205,47 @@ static void tolua_reg_types (lua_State* tolua_S)
tolua_usertype(tolua_S,"cRoot");
tolua_usertype(tolua_S,"cWindow");
tolua_usertype(tolua_S,"cCraftingGrid");
tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cPickup");
tolua_usertype(tolua_S,"cItems");
tolua_usertype(tolua_S,"cGroup");
tolua_usertype(tolua_S,"cClientHandle");
tolua_usertype(tolua_S,"cChunkDesc");
tolua_usertype(tolua_S,"cFurnaceRecipe");
tolua_usertype(tolua_S,"cCuboid");
tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cChatColor");
tolua_usertype(tolua_S,"cCuboid");
tolua_usertype(tolua_S,"Vector3i");
tolua_usertype(tolua_S,"cPlugin_NewLua");
tolua_usertype(tolua_S,"Lua__cWebPlugin");
tolua_usertype(tolua_S,"Lua__cPawn");
tolua_usertype(tolua_S,"cWebAdmin");
tolua_usertype(tolua_S,"cEntity");
tolua_usertype(tolua_S,"cItem");
tolua_usertype(tolua_S,"Vector3f");
tolua_usertype(tolua_S,"cCraftingRecipes");
tolua_usertype(tolua_S,"cWebAdmin");
tolua_usertype(tolua_S,"cDropSpenserEntity");
tolua_usertype(tolua_S,"Lua__cPlayer");
tolua_usertype(tolua_S,"cItemGrid::cListener");
tolua_usertype(tolua_S,"cCraftingRecipes");
tolua_usertype(tolua_S,"cChestEntity");
tolua_usertype(tolua_S,"cDispenserEntity");
tolua_usertype(tolua_S,"Lua__cPickup");
tolua_usertype(tolua_S,"cPlugin");
tolua_usertype(tolua_S,"cBlockEntity");
tolua_usertype(tolua_S,"cCriticalSection");
tolua_usertype(tolua_S,"Lua__cPickup");
tolua_usertype(tolua_S,"cWebPlugin");
tolua_usertype(tolua_S,"HTTPRequest");
tolua_usertype(tolua_S,"HTTPFormData");
tolua_usertype(tolua_S,"cFurnaceEntity");
tolua_usertype(tolua_S,"cDropperEntity");
tolua_usertype(tolua_S,"cPluginManager");
tolua_usertype(tolua_S,"cWorld");
tolua_usertype(tolua_S,"cDropperEntity");
tolua_usertype(tolua_S,"cIniFile");
tolua_usertype(tolua_S,"cPlugin");
tolua_usertype(tolua_S,"AStringVector");
tolua_usertype(tolua_S,"cWorld");
tolua_usertype(tolua_S,"cListeners");
tolua_usertype(tolua_S,"cPawn");
tolua_usertype(tolua_S,"cPlayer");
tolua_usertype(tolua_S,"cGroupManager");
tolua_usertype(tolua_S,"cBlockEntityWindowOwner");
tolua_usertype(tolua_S,"cBlockEntityWithItems");
tolua_usertype(tolua_S,"cItemGrid::cListener");
tolua_usertype(tolua_S,"cServer");
tolua_usertype(tolua_S,"cListeners");
tolua_usertype(tolua_S,"cBlockEntityWithItems");
tolua_usertype(tolua_S,"Lua__cEntity");
tolua_usertype(tolua_S,"Vector3d");
}
@ -5878,18 +5877,20 @@ static int tolua_AllToLua_cEntity_Destroy00(lua_State* tolua_S)
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) ||
!tolua_isnoobj(tolua_S,2,&tolua_err)
!tolua_isboolean(tolua_S,2,1,&tolua_err) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
bool a_ShouldBroadcast = ((bool) tolua_toboolean(tolua_S,2,true));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Destroy'", NULL);
#endif
{
self->Destroy();
self->Destroy(a_ShouldBroadcast);
}
}
return 0;
@ -10169,40 +10170,6 @@ static int tolua_AllToLua_cPluginManager_IsConsoleCommandBound00(lua_State* tolu
}
#endif //#ifndef TOLUA_DISABLE
/* method: ExecuteConsoleCommand of class cPluginManager */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ExecuteConsoleCommand00
static int tolua_AllToLua_cPluginManager_ExecuteConsoleCommand00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
if (
!tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) ||
(tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const AStringVector",0,&tolua_err)) ||
!tolua_isnoobj(tolua_S,3,&tolua_err)
)
goto tolua_lerror;
else
#endif
{
cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0);
const AStringVector* a_Split = ((const AStringVector*) tolua_tousertype(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteConsoleCommand'", NULL);
#endif
{
bool tolua_ret = (bool) self->ExecuteConsoleCommand(*a_Split);
tolua_pushboolean(tolua_S,(bool)tolua_ret);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'ExecuteConsoleCommand'.",&tolua_err);
return 0;
#endif
}
#endif //#ifndef TOLUA_DISABLE
/* method: GetName of class cPlugin */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetName00
static int tolua_AllToLua_cPlugin_GetName00(lua_State* tolua_S)
@ -18771,9 +18738,9 @@ static int tolua_AllToLua_cRoot_GetPluginManager00(lua_State* tolua_S)
}
#endif //#ifndef TOLUA_DISABLE
/* method: ExecuteConsoleCommand of class cRoot */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_ExecuteConsoleCommand00
static int tolua_AllToLua_cRoot_ExecuteConsoleCommand00(lua_State* tolua_S)
/* method: QueueExecuteConsoleCommand of class cRoot */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00
static int tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00(lua_State* tolua_S)
{
#ifndef TOLUA_RELEASE
tolua_Error tolua_err;
@ -18789,17 +18756,17 @@ static int tolua_AllToLua_cRoot_ExecuteConsoleCommand00(lua_State* tolua_S)
cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteConsoleCommand'", NULL);
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueExecuteConsoleCommand'", NULL);
#endif
{
self->ExecuteConsoleCommand(a_Cmd);
self->QueueExecuteConsoleCommand(a_Cmd);
tolua_pushcppstring(tolua_S,(const char*)a_Cmd);
}
}
return 1;
#ifndef TOLUA_RELEASE
tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'ExecuteConsoleCommand'.",&tolua_err);
tolua_error(tolua_S,"#ferror in function 'QueueExecuteConsoleCommand'.",&tolua_err);
return 0;
#endif
}
@ -28187,7 +28154,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"ExecuteCommand",tolua_AllToLua_cPluginManager_ExecuteCommand00);
tolua_function(tolua_S,"ForceExecuteCommand",tolua_AllToLua_cPluginManager_ForceExecuteCommand00);
tolua_function(tolua_S,"IsConsoleCommandBound",tolua_AllToLua_cPluginManager_IsConsoleCommandBound00);
tolua_function(tolua_S,"ExecuteConsoleCommand",tolua_AllToLua_cPluginManager_ExecuteConsoleCommand00);
tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cPlugin","cPlugin","",NULL);
tolua_beginmodule(tolua_S,"cPlugin");
@ -28599,7 +28565,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S)
tolua_function(tolua_S,"GetFurnaceRecipe",tolua_AllToLua_cRoot_GetFurnaceRecipe00);
tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00);
tolua_function(tolua_S,"GetPluginManager",tolua_AllToLua_cRoot_GetPluginManager00);
tolua_function(tolua_S,"ExecuteConsoleCommand",tolua_AllToLua_cRoot_ExecuteConsoleCommand00);
tolua_function(tolua_S,"QueueExecuteConsoleCommand",tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00);
tolua_function(tolua_S,"GetTotalChunkCount",tolua_AllToLua_cRoot_GetTotalChunkCount00);
tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00);
tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00);

View File

@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 06/22/13 19:31:24.
** Generated automatically by tolua++-1.0.92 on 06/29/13 17:21:36.
*/
/* Exported function */

71
source/CommandOutput.cpp Normal file
View File

@ -0,0 +1,71 @@
// CommandOutput.cpp
// Implements the various classes that process command output
#include "Globals.h"
#include "CommandOutput.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cCommandOutputCallback:
void cCommandOutputCallback::Out(const char * a_Fmt, ...)
{
AString Output;
va_list args;
va_start(args, a_Fmt);
AppendVPrintf(Output, a_Fmt, args);
va_end(args);
Output.append("\n");
Out(Output);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cLogCommandOutputCallback:
void cLogCommandOutputCallback::Out(const AString & a_Text)
{
m_Buffer.append(a_Text);
}
void cLogCommandOutputCallback::Finished(void)
{
// Log each line separately:
size_t len = m_Buffer.length();
size_t last = 0;
for (size_t i = 0; i < len; i++)
{
switch (m_Buffer[i])
{
case '\n':
{
LOG(m_Buffer.substr(last, i - last).c_str());
last = i + 1;
break;
}
}
} // for i - m_Buffer[]
if (last < len)
{
LOG(m_Buffer.substr(last).c_str());
}
// Clear the buffer for the next command output:
m_Buffer.clear();
}

82
source/CommandOutput.h Normal file
View File

@ -0,0 +1,82 @@
// CommandOutput.h
// Declares various classes that process command output
/** Interface for a callback that receives command output
The Out() function is called for any output the command has produced.
Descendants override that function to provide specific processing of the output.
*/
class cCommandOutputCallback
{
public:
virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses
/// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n"
void Out(const char * a_Fmt, ...);
/// Called when the command wants to output anything; may be called multiple times
virtual void Out(const AString & a_Text) = 0;
/// Called when the command processing has been finished
virtual void Finished(void) {};
} ;
/// Class that discards all command output
class cNullCommandOutputCallback :
public cCommandOutputCallback
{
// cCommandOutputCallback overrides:
virtual void Out(const AString & a_Text) override
{
// Do nothing
}
} ;
/// Sends all command output to a log, line by line, when the command finishes processing
class cLogCommandOutputCallback :
public cCommandOutputCallback
{
public:
// cCommandOutputCallback overrides:
virtual void Out(const AString & a_Text) override;
virtual void Finished(void) override;
protected:
/// Output is stored here until the command finishes processing
AString m_Buffer;
} ;
/// Sends all command output to a log, line by line; deletes self when command finishes processing
class cLogCommandDeleteSelfOutputCallback :
public cLogCommandOutputCallback
{
typedef cLogCommandOutputCallback super;
virtual void Finished(void) override
{
super::Finished();
delete this;
}
} ;

View File

@ -5,6 +5,7 @@
#include "Pawn.h"
#include "Player.h"
#include "World.h"
#include "CommandOutput.h"
@ -546,9 +547,10 @@ bool cPlugin::HandleCommand(const AStringVector & a_Split, cPlayer * a_Player)
bool cPlugin::HandleConsoleCommand(const AStringVector & a_Split)
bool cPlugin::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
{
UNUSED(a_Split);
UNUSED(a_Output);
return false;
}

View File

@ -92,8 +92,10 @@ public:
*/
virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player);
/// Handles the console command split into a_Split. Returns true if command handled successfully.
virtual bool HandleConsoleCommand(const AStringVector & a_Split);
/** Handles the console command split into a_Split.
Returns true if command handled successfully. Output is to be sent to the a_Output callback.
*/
virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
/// All bound commands are to be removed, do any language-dependent cleanup here
virtual void ClearCommands(void) {} ;

View File

@ -8,6 +8,7 @@
#include "Item.h"
#include "Root.h"
#include "Server.h"
#include "CommandOutput.h"
#include "../iniFile/iniFile.h"
#include "tolua++.h"
@ -1300,7 +1301,7 @@ bool cPluginManager::IsConsoleCommandBound(const AString & a_Command)
bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split)
bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
{
if (a_Split.empty())
{
@ -1323,11 +1324,11 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split)
// Ask plugins first if a command is okay to execute the console command:
if (CallHookExecuteCommand(NULL, a_Split))
{
LOGINFO("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str());
a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str());
return false;
}
return cmd->second.m_Plugin->HandleConsoleCommand(a_Split);
return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output);
}

View File

@ -29,6 +29,9 @@ class cPickup;
struct TakeDamageInfo;
class cPawn;
// fwd: CommandOutput.h
class cCommandOutputCallback;
@ -191,8 +194,8 @@ public: // tolua_export
/// Returns true if the console command is in the command map
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export
/// Executes the command split into a_Split, as if it was given on the console. Returns true if executed.
bool ExecuteConsoleCommand(const AStringVector & a_Split); // tolua_export
/// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback
bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
private:
friend class cRoot;

View File

@ -3,7 +3,7 @@
#define LUA_USE_POSIX
#include "Plugin_NewLua.h"
#include "MCLogger.h"
#include "CommandOutput.h"
extern "C"
{
@ -1378,13 +1378,15 @@ bool cPlugin_NewLua::HandleCommand(const AStringVector & a_Split, cPlayer * a_Pl
bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split)
bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output)
{
ASSERT(!a_Split.empty());
CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]);
if (cmd == m_ConsoleCommands.end())
{
LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\".", a_Split[0].c_str());
LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".",
a_Split[0].c_str(), GetName().c_str()
);
return false;
}
@ -1407,16 +1409,21 @@ bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split)
}
// Call function:
int s = lua_pcall(m_LuaState, 1, 1, 0);
int s = lua_pcall(m_LuaState, 1, 2, 0);
if (report_errors(m_LuaState, s))
{
LOGERROR("Lua error. Stack size: %i", lua_gettop(m_LuaState));
return false;
}
// Handle return value:
bool RetVal = (tolua_toboolean(m_LuaState, -1, 0) > 0);
lua_pop(m_LuaState, 1); // Pop return value
// Handle return values:
if (lua_isstring(m_LuaState, -1))
{
AString str = tolua_tocppstring(m_LuaState, -1, "");
a_Output.Out(str);
}
bool RetVal = (tolua_toboolean(m_LuaState, -2, 0) > 0);
lua_pop(m_LuaState, 2); // Pop return values
return RetVal;
}

View File

@ -77,7 +77,7 @@ public:
virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override;
virtual bool HandleConsoleCommand(const AStringVector & a_Split) override;
virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override;
virtual void ClearCommands(void) override;

View File

@ -8,6 +8,7 @@
#include "RCONServer.h"
#include "Server.h"
#include "Root.h"
#include "CommandOutput.h"
@ -37,6 +38,41 @@ enum
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cRCONCommandOutput:
class cRCONCommandOutput :
public cCommandOutputCallback
{
public:
cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) :
m_Connection(a_Connection),
m_RequestID(a_RequestID)
{
}
// cCommandOutputCallback overrides:
virtual void Out(const AString & a_Text) override
{
m_Buffer.append(a_Text);
}
virtual void Finished(void) override
{
m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str());
delete this;
}
protected:
cRCONServer::cConnection & m_Connection;
int m_RequestID;
AString m_Buffer;
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cRCONServer:
@ -218,9 +254,16 @@ bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType,
case RCON_PACKET_COMMAND:
{
if (!m_IsAuthenticated)
{
char AuthNeeded[] = "You need to authenticate first!";
SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded);
return false;
}
AString cmd(a_Payload, a_PayloadLength);
LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str());
cRoot::Get()->ExecuteConsoleCommand(cmd);
cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID)));
// Send an empty response:
SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL);

View File

@ -34,6 +34,8 @@ public:
void Initialize(cIniFile & a_IniFile);
protected:
friend class cRCONCommandOutput;
class cConnection :
public cSocketThreads::cCallback
{
@ -41,6 +43,7 @@ protected:
cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket);
protected:
friend class cRCONCommandOutput;
/// Set to true if the client has successfully authenticated
bool m_IsAuthenticated;

View File

@ -15,6 +15,7 @@
#include "Items/ItemHandler.h"
#include "Chunk.h"
#include "Protocol/ProtocolRecognizer.h" // for protocol version constants
#include "CommandOutput.h"
#ifdef USE_SQUIRREL
#include "squirrelbindings/SquirrelFunctions.h"
@ -69,11 +70,13 @@ void cRoot::InputThread(void * a_Params)
{
cRoot & self = *(cRoot*)a_Params;
cLogCommandOutputCallback Output;
while (!(self.m_bStop || self.m_bRestart))
{
std::string Command;
std::getline(std::cin, Command);
self.ExecuteConsoleCommand(Command);
self.ExecuteConsoleCommand(Command, Output);
}
}
@ -350,14 +353,14 @@ bool cRoot::ForEachWorld(cWorldListCallback & a_Callback)
void cRoot::TickWorlds(float a_Dt)
{
// Execute any pending commands:
AStringVector PendingCommands;
cCommandQueue PendingCommands;
{
cCSLock Lock(m_CSPendingCommands);
std::swap(PendingCommands, m_PendingCommands);
}
for (AStringVector::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr)
for (cCommandQueue::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr)
{
DoExecuteConsoleCommand(*itr);
ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output));
}
// Tick the worlds:
@ -371,7 +374,7 @@ void cRoot::TickWorlds(float a_Dt)
void cRoot::ExecuteConsoleCommand(const AString & a_Cmd)
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{
// Some commands are built-in:
if (a_Cmd == "stop")
@ -385,17 +388,48 @@ void cRoot::ExecuteConsoleCommand(const AString & a_Cmd)
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock(m_CSPendingCommands);
m_PendingCommands.push_back(a_Cmd);
m_PendingCommands.push_back(cCommand(a_Cmd, &a_Output));
}
void cRoot::DoExecuteConsoleCommand(const AString & a_Cmd)
void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
{
// Some commands are built-in:
if (a_Cmd == "stop")
{
m_bStop = true;
}
else if (a_Cmd == "restart")
{
m_bRestart = true;
}
// Put the command into a queue (Alleviates FS #363):
cCSLock Lock(m_CSPendingCommands);
m_PendingCommands.push_back(cCommand(a_Cmd, new cLogCommandDeleteSelfOutputCallback));
}
void cRoot::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{
// Some commands are built-in:
if (a_Cmd == "stop")
{
m_bStop = true;
}
else if (a_Cmd == "restart")
{
m_bRestart = true;
}
LOG("Executing console command: \"%s\"", a_Cmd.c_str());
m_Server->ExecuteConsoleCommand(a_Cmd);
m_Server->ExecuteConsoleCommand(a_Cmd, a_Output);
}
@ -524,7 +558,7 @@ AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion)
void cRoot::LogChunkStats(void)
void cRoot::LogChunkStats(cCommandOutputCallback & a_Output)
{
int SumNumValid = 0;
int SumNumDirty = 0;
@ -541,35 +575,35 @@ void cRoot::LogChunkStats(void)
int NumDirty = 0;
int NumInLighting = 0;
World->GetChunkStats(NumValid, NumDirty, NumInLighting);
LOG("World %s:", World->GetName().c_str());
LOG(" Num loaded chunks: %d", NumValid);
LOG(" Num dirty chunks: %d", NumDirty);
LOG(" Num chunks in lighting queue: %d", NumInLighting);
LOG(" Num chunks in generator queue: %d", NumInGenerator);
LOG(" Num chunks in storage load queue: %d", NumInLoadQueue);
LOG(" Num chunks in storage save queue: %d", NumInSaveQueue);
a_Output.Out("World %s:", World->GetName().c_str());
a_Output.Out(" Num loaded chunks: %d", NumValid);
a_Output.Out(" Num dirty chunks: %d", NumDirty);
a_Output.Out(" Num chunks in lighting queue: %d", NumInLighting);
a_Output.Out(" Num chunks in generator queue: %d", NumInGenerator);
a_Output.Out(" Num chunks in storage load queue: %d", NumInLoadQueue);
a_Output.Out(" Num chunks in storage save queue: %d", NumInSaveQueue);
int Mem = NumValid * sizeof(cChunk);
LOG(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024));
LOG(" Per-chunk memory size breakdown:");
LOG(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024);
LOG(" block metadata: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024);
LOG(" block lighting: %6d bytes (%3d KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024);
LOG(" heightmap: %6d bytes (%3d KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024);
LOG(" biomemap: %6d bytes (%3d KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024);
a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024));
a_Output.Out(" Per-chunk memory size breakdown:");
a_Output.Out(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024);
a_Output.Out(" block metadata: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockNibbles), (sizeof(cChunkDef::BlockNibbles) + 1023) / 1024);
a_Output.Out(" block lighting: %6d bytes (%3d KiB)", 2 * sizeof(cChunkDef::BlockNibbles), (2 * sizeof(cChunkDef::BlockNibbles) + 1023) / 1024);
a_Output.Out(" heightmap: %6d bytes (%3d KiB)", sizeof(cChunkDef::HeightMap), (sizeof(cChunkDef::HeightMap) + 1023) / 1024);
a_Output.Out(" biomemap: %6d bytes (%3d KiB)", sizeof(cChunkDef::BiomeMap), (sizeof(cChunkDef::BiomeMap) + 1023) / 1024);
int Rest = sizeof(cChunk) - sizeof(cChunkDef::BlockTypes) - 3 * sizeof(cChunkDef::BlockNibbles) - sizeof(cChunkDef::HeightMap) - sizeof(cChunkDef::BiomeMap);
LOG(" other: %6d bytes (%3d KiB)", Rest, (Rest + 1023) / 1024);
a_Output.Out(" other: %6d bytes (%3d KiB)", Rest, (Rest + 1023) / 1024);
SumNumValid += NumValid;
SumNumDirty += NumDirty;
SumNumInLighting += NumInLighting;
SumNumInGenerator += NumInGenerator;
SumMem += Mem;
}
LOG("Totals:");
LOG(" Num loaded chunks: %d", SumNumValid);
LOG(" Num dirty chunks: %d", SumNumDirty);
LOG(" Num chunks in lighting queue: %d", SumNumInLighting);
LOG(" Num chunks in generator queue: %d", SumNumInGenerator);
LOG(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024));
a_Output.Out("Totals:");
a_Output.Out(" Num loaded chunks: %d", SumNumValid);
a_Output.Out(" Num dirty chunks: %d", SumNumDirty);
a_Output.Out(" Num chunks in lighting queue: %d", SumNumInLighting);
a_Output.Out(" Num chunks in generator queue: %d", SumNumInGenerator);
a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024));
}

View File

@ -1,15 +1,13 @@
#pragma once
#include "Authenticator.h"
// fwd:
class cThread;
class cMonsterConfig;
class cGroupManager;
@ -20,6 +18,8 @@ class cPluginManager;
class cServer;
class cWorld;
class cPlayer;
class cCommandOutputCallback ;
typedef cItemCallback<cPlayer> cPlayerListCallback;
typedef cItemCallback<cWorld> cWorldListCallback;
@ -27,6 +27,7 @@ typedef cItemCallback<cWorld> cWorldListCallback;
/// The root of the object hierarchy
class cRoot // tolua_export
{ // tolua_export
public:
@ -47,8 +48,8 @@ public:
/// Calls the callback for each world; returns true if the callback didn't abort (return true)
bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings <<
/// Logs chunkstats for each world and totals
void LogChunkStats(void);
/// Writes chunkstats, for each world and totals, to the output callback
void LogChunkStats(cCommandOutputCallback & a_Output);
int GetPrimaryServerVersion(void) const { return m_PrimaryServerVersion; } // tolua_export
void SetPrimaryServerVersion(int a_Version) { m_PrimaryServerVersion = a_Version; } // tolua_export
@ -62,8 +63,22 @@ public:
cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export
cAuthenticator & GetAuthenticator (void) { return m_Authenticator; }
/// Queues a console command for execution through the cServer class; does special handling for "stop" and "restart".
void ExecuteConsoleCommand(const AString & a_Cmd); // tolua_export
/** Queues a console command for execution through the cServer class.
The command will be executed in the tick thread
The command's output will be written to the a_Output callback
"stop" and "restart" commands have special handling.
*/
void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
/** Queues a console command for execution through the cServer class.
The command will be executed in the tick thread
The command's output will be sent to console
"stop" and "restart" commands have special handling.
*/
void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export
/// Executes a console command through the cServer class; does special handling for "stop" and "restart".
void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
/// Kicks the user, no matter in what world they are. Used from cAuthenticator
void KickUser(int a_ClientID, const AString & a_Reason);
@ -89,12 +104,27 @@ public:
static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); // tolua_export
private:
typedef std::map< AString, cWorld* > WorldMap;
cWorld* m_pDefaultWorld;
class cCommand
{
public:
cCommand(const AString & a_Command, cCommandOutputCallback * a_Output) :
m_Command(a_Command),
m_Output(a_Output)
{
}
AString m_Command;
cCommandOutputCallback * m_Output;
} ;
typedef std::map<AString, cWorld *> WorldMap;
typedef std::vector<cCommand> cCommandQueue;
cWorld * m_pDefaultWorld;
WorldMap m_WorldsByName;
cCriticalSection m_CSPendingCommands;
AStringVector m_PendingCommands;
cCommandQueue m_PendingCommands;
cThread * m_InputThread;
@ -131,7 +161,7 @@ private:
void DoExecuteConsoleCommand(const AString & a_Cmd);
static void InputThread(void* a_Params);
static cRoot* s_Root;
}; // tolua_export

View File

@ -21,6 +21,7 @@
#include "Tracer.h"
#include "WebAdmin.h"
#include "Protocol/ProtocolRecognizer.h"
#include "CommandOutput.h"
#include "MersenneTwister.h"
@ -425,7 +426,7 @@ bool cServer::Command(cClientHandle & a_Client, AString & a_Cmd)
void cServer::ExecuteConsoleCommand(const AString & a_Cmd)
void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output)
{
AStringVector split = StringSplit(a_Cmd, " ");
if (split.empty())
@ -442,7 +443,8 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd)
// There is currently no way a plugin can do these (and probably won't ever be):
if (split[0].compare("chunkstats") == 0)
{
cRoot::Get()->LogChunkStats();
cRoot::Get()->LogChunkStats(a_Output);
a_Output.Finished();
return;
}
#if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER)
@ -462,12 +464,14 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd)
}
#endif
if (cPluginManager::Get()->ExecuteConsoleCommand(split))
if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output))
{
a_Output.Finished();
return;
}
LOG("Unknown command, type 'help' for all commands.\n");
a_Output.Out("Unknown command, type 'help' for all commands.");
a_Output.Finished();
}

View File

@ -19,9 +19,11 @@
// fwd:
class cPlayer;
class cClientHandle;
class cIniFile;
class cCommandOutputCallback ;
typedef std::list<cClientHandle *> cClientHandleList;
@ -44,7 +46,9 @@ public: // tolua_export
bool Start(void);
bool Command(cClientHandle & a_Client, AString & a_Cmd);
void ExecuteConsoleCommand(const AString & a_Cmd);
/// Executes the console command, sends output through the specified callback
void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output);
/// Binds the built-in console commands with the plugin manager
static void BindBuiltInConsoleCommands(void);

View File

@ -569,26 +569,3 @@ AString & CreateHexDump(AString & a_Out, const void * a_Data, int a_Size, int a_
AString Trim(const AString & a_Text)
{
if (a_Text.empty())
{
return "";
}
size_t Beginning = a_Text.find_first_not_of(" \r\n\t");
if (Beginning == AString::npos)
{
Beginning = 0;
}
size_t End = a_Text.find_last_not_of(" \r\n\t");
if (End == AString::npos)
{
End = a_Text.length();
}
return a_Text.substr(Beginning, End - Beginning + 1);
}