From 7b75aaea7c538f61518a60fe4af363383020e0bc Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sat, 29 Jun 2013 15:30:05 +0000 Subject: [PATCH] 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 --- MCServer/Plugins/Core/ban.lua | 30 +-- MCServer/Plugins/Core/console.lua | 250 ++++++++++++++------- MCServer/Plugins/Core/kick.lua | 32 +-- MCServer/Plugins/Core/web_manageserver.lua | 16 +- VC2008/MCServer.vcproj | 8 + source/Bindings.cpp | 84 +++---- source/Bindings.h | 2 +- source/CommandOutput.cpp | 71 ++++++ source/CommandOutput.h | 82 +++++++ source/Plugin.cpp | 4 +- source/Plugin.h | 6 +- source/PluginManager.cpp | 7 +- source/PluginManager.h | 7 +- source/Plugin_NewLua.cpp | 21 +- source/Plugin_NewLua.h | 2 +- source/RCONServer.cpp | 45 +++- source/RCONServer.h | 3 + source/Root.cpp | 94 +++++--- source/Root.h | 52 ++++- source/Server.cpp | 12 +- source/Server.h | 6 +- source/StringUtils.cpp | 23 -- 22 files changed, 599 insertions(+), 258 deletions(-) create mode 100644 source/CommandOutput.cpp create mode 100644 source/CommandOutput.h 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] "; + 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 - - - - - 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 .. [[
diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index d1e1a5e14..d1c82d8c4 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -362,6 +362,14 @@ RelativePath="..\source\ClientHandle.h" > + + + + 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" @@ -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); 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,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)); } 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 cPlayerListCallback; typedef cItemCallback cWorldListCallback; @@ -27,6 +27,7 @@ typedef cItemCallback 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 WorldMap; + typedef std::vector 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 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); -} - - - -