diff options
-rw-r--r-- | MCServer/Plugins/Core/ban.lua | 30 | ||||
-rw-r--r-- | MCServer/Plugins/Core/console.lua | 250 | ||||
-rw-r--r-- | MCServer/Plugins/Core/kick.lua | 32 | ||||
-rw-r--r-- | MCServer/Plugins/Core/web_manageserver.lua | 16 | ||||
-rw-r--r-- | VC2008/MCServer.vcproj | 8 | ||||
-rw-r--r-- | source/Bindings.cpp | 84 | ||||
-rw-r--r-- | source/Bindings.h | 2 | ||||
-rw-r--r-- | source/CommandOutput.cpp | 71 | ||||
-rw-r--r-- | source/CommandOutput.h | 82 | ||||
-rw-r--r-- | source/Plugin.cpp | 4 | ||||
-rw-r--r-- | source/Plugin.h | 6 | ||||
-rw-r--r-- | source/PluginManager.cpp | 7 | ||||
-rw-r--r-- | source/PluginManager.h | 7 | ||||
-rw-r--r-- | source/Plugin_NewLua.cpp | 21 | ||||
-rw-r--r-- | source/Plugin_NewLua.h | 2 | ||||
-rw-r--r-- | source/RCONServer.cpp | 45 | ||||
-rw-r--r-- | source/RCONServer.h | 3 | ||||
-rw-r--r-- | source/Root.cpp | 94 | ||||
-rw-r--r-- | source/Root.h | 52 | ||||
-rw-r--r-- | source/Server.cpp | 12 | ||||
-rw-r--r-- | source/Server.h | 6 | ||||
-rw-r--r-- | source/StringUtils.cpp | 23 |
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); -} - - - - |