summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MCServer/Plugins/Core/ban.lua30
-rw-r--r--MCServer/Plugins/Core/console.lua250
-rw-r--r--MCServer/Plugins/Core/kick.lua32
-rw-r--r--MCServer/Plugins/Core/web_manageserver.lua16
-rw-r--r--VC2008/MCServer.vcproj8
-rw-r--r--source/Bindings.cpp84
-rw-r--r--source/Bindings.h2
-rw-r--r--source/CommandOutput.cpp71
-rw-r--r--source/CommandOutput.h82
-rw-r--r--source/Plugin.cpp4
-rw-r--r--source/Plugin.h6
-rw-r--r--source/PluginManager.cpp7
-rw-r--r--source/PluginManager.h7
-rw-r--r--source/Plugin_NewLua.cpp21
-rw-r--r--source/Plugin_NewLua.h2
-rw-r--r--source/RCONServer.cpp45
-rw-r--r--source/RCONServer.h3
-rw-r--r--source/Root.cpp94
-rw-r--r--source/Root.h52
-rw-r--r--source/Server.cpp12
-rw-r--r--source/Server.h6
-rw-r--r--source/StringUtils.cpp23
22 files changed, 599 insertions, 258 deletions
diff --git a/MCServer/Plugins/Core/ban.lua b/MCServer/Plugins/Core/ban.lua
index a4a4822bd..4e882b66f 100644
--- a/MCServer/Plugins/Core/ban.lua
+++ b/MCServer/Plugins/Core/ban.lua
@@ -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 \ No newline at end of file
diff --git a/MCServer/Plugins/Core/console.lua b/MCServer/Plugins/Core/console.lua
index efdf5c39e..df90a9b9a 100644
--- a/MCServer/Plugins/Core/console.lua
+++ b/MCServer/Plugins/Core/console.lua
@@ -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()
- 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
-end
-
-
-
-
-
+-------------------------------------------------------------------------------------------
+-- Helper functions:
-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) )
+--- 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
- LOGINFO( "Groups:" )
- LOGINFO( table.concat( Groups, ", " ) )
- return true
+ return table.concat(Banned, ", ");
end
-function HandleConsole(Split)
- 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
-
diff --git a/MCServer/Plugins/Core/kick.lua b/MCServer/Plugins/Core/kick.lua
index 5004c8c2f..4091fd701 100644
--- a/MCServer/Plugins/Core/kick.lua
+++ b/MCServer/Plugins/Core/kick.lua
@@ -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 \ No newline at end of file
diff --git a/MCServer/Plugins/Core/web_manageserver.lua b/MCServer/Plugins/Core/web_manageserver.lua
index 4e8a9a747..a2936c1a8 100644
--- a/MCServer/Plugins/Core/web_manageserver.lua
+++ b/MCServer/Plugins/Core/web_manageserver.lua
@@ -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">
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj
index d1e1a5e14..d1c82d8c4 100644
--- a/VC2008/MCServer.vcproj
+++ b/VC2008/MCServer.vcproj
@@ -363,6 +363,14 @@
>
</File>
<File
+ RelativePath="..\source\CommandOutput.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\source\CommandOutput.h"
+ >
+ </File>
+ <File
RelativePath="..\source\CraftingRecipes.cpp"
>
</File>
diff --git a/source/Bindings.cpp b/source/Bindings.cpp
index 386ec2e41..ec164b764 100644
--- a/source/Bindings.cpp
+++ b/source/Bindings.cpp
@@ -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);
diff --git a/source/Bindings.h b/source/Bindings.h
index df29c6201..c13cf32bd 100644
--- a/source/Bindings.h
+++ b/source/Bindings.h
@@ -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 */
diff --git a/source/CommandOutput.cpp b/source/CommandOutput.cpp
new file mode 100644
index 000000000..49102b38c
--- /dev/null
+++ b/source/CommandOutput.cpp
@@ -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();
+}
+
+
+
+
diff --git a/source/CommandOutput.h b/source/CommandOutput.h
new file mode 100644
index 000000000..68217dbb4
--- /dev/null
+++ b/source/CommandOutput.h
@@ -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;
+ }
+} ;
+
+
+
+
diff --git a/source/Plugin.cpp b/source/Plugin.cpp
index 389ef15e4..daf1d33c8 100644
--- a/source/Plugin.cpp
+++ b/source/Plugin.cpp
@@ -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;
}
diff --git a/source/Plugin.h b/source/Plugin.h
index 9107cce6f..91f5f5286 100644
--- a/source/Plugin.h
+++ b/source/Plugin.h
@@ -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) {} ;
diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp
index a180f5618..32f2a2a99 100644
--- a/source/PluginManager.cpp
+++ b/source/PluginManager.cpp
@@ -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);
}
diff --git a/source/PluginManager.h b/source/PluginManager.h
index 655081568..9268fe66d 100644
--- a/source/PluginManager.h
+++ b/source/PluginManager.h
@@ -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;
diff --git a/source/Plugin_NewLua.cpp b/source/Plugin_NewLua.cpp
index 335ac000a..1e4a6a94a 100644
--- a/source/Plugin_NewLua.cpp
+++ b/source/Plugin_NewLua.cpp
@@ -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;
}
diff --git a/source/Plugin_NewLua.h b/source/Plugin_NewLua.h
index 086568f76..7abd0f9fe 100644
--- a/source/Plugin_NewLua.h
+++ b/source/Plugin_NewLua.h
@@ -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;
diff --git a/source/RCONServer.cpp b/source/RCONServer.cpp
index 9558512eb..cf6ed8b0c 100644
--- a/source/RCONServer.cpp
+++ b/source/RCONServer.cpp
@@ -8,6 +8,7 @@
#include "RCONServer.h"
#include "Server.h"
#include "Root.h"
+#include "CommandOutput.h"
@@ -38,6 +39,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:
cRCONServer::cRCONServer(cServer & a_Server) :
@@ -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);
diff --git a/source/RCONServer.h b/source/RCONServer.h
index 95122ba5f..a4a0d20fe 100644
--- a/source/RCONServer.h
+++ b/source/RCONServer.h
@@ -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;
diff --git a/source/Root.cpp b/source/Root.cpp
index c7be00c20..771986003 100644
--- a/source/Root.cpp
+++ b/source/Root.cpp
@@ -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,28 @@ 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")
+ {
+ 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, &a_Output));
+}
+
+
+
+
+
+void cRoot::QueueExecuteConsoleCommand(const AString & a_Cmd)
{
// Some commands are built-in:
if (a_Cmd == "stop")
@@ -385,17 +409,27 @@ 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, new cLogCommandDeleteSelfOutputCallback));
}
-void cRoot::DoExecuteConsoleCommand(const AString & a_Cmd)
+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));
}
diff --git a/source/Root.h b/source/Root.h
index 9f94c4df3..1e2befcd4 100644
--- a/source/Root.h
+++ b/source/Root.h
@@ -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
diff --git a/source/Server.cpp b/source/Server.cpp
index 7dac3ea18..67a02f626 100644
--- a/source/Server.cpp
+++ b/source/Server.cpp
@@ -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();
}
diff --git a/source/Server.h b/source/Server.h
index 3f7a24699..542673b49 100644
--- a/source/Server.h
+++ b/source/Server.h
@@ -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);
diff --git a/source/StringUtils.cpp b/source/StringUtils.cpp
index da809c5a4..b55ce51e6 100644
--- a/source/StringUtils.cpp
+++ b/source/StringUtils.cpp
@@ -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);
-}
-
-
-
-