1
0

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
This commit is contained in:
madmaxoft@gmail.com 2013-06-29 15:30:05 +00:00
parent cff6ff2223
commit 7b75aaea7c
22 changed files with 599 additions and 258 deletions

View File

@ -18,23 +18,27 @@ function HandleBanCommand( Split, Player )
return true return true
end 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" Reason = "You have been banned"
end end
local Success = KickPlayer(PlayerName, Reason)
local Success, RealName = KickPlayer( PlayerName, Reason ) if (not(Success)) then
if( Success == false ) then return false;
return false
end end
LOGINFO( "'" .. RealName .. "' is being banned for ( "..Reason..") " ) LOGINFO("'" .. PlayerName .. "' has been banned (\"" .. Reason .. "\") ");
local Server = cRoot:Get():GetServer();
local Server = cRoot:Get():GetServer() Server:SendMessage("Banned " .. PlayerName);
Server:SendMessage( "Banning " .. RealName )
BannedPlayersIni:SetValueB("Banned", RealName, true)
BannedPlayersIni:WriteFile()
return true return true
end end

View File

@ -9,16 +9,60 @@
function InitConsoleCommands() function InitConsoleCommands()
local PluginMgr = cPluginManager:Get(); 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("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("numchunks", HandleConsoleNumChunks, "Shows number of chunks currently loaded");
PluginMgr:BindConsoleCommand("players", HandleConsolePlayers, "Lists all connected players"); PluginMgr:BindConsoleCommand("players", HandleConsolePlayers, "Lists all connected players");
PluginMgr:BindConsoleCommand("primaryserverversion", HandleConsolePrimaryServerVersion, "Gets or sets server version reported to 1.4+ clients"); 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("reload", HandleConsoleReload, "Reloads all plugins");
PluginMgr:BindConsoleCommand("save-all", HandleConsoleSaveAll, "Saves all chunks"); PluginMgr:BindConsoleCommand("save-all", HandleConsoleSaveAll, "Saves all chunks");
PluginMgr:BindConsoleCommand("say", HandleConsoleSay, "Sends a chat message to all players"); PluginMgr:BindConsoleCommand("say", HandleConsoleSay, "Sends a chat message to all players");
PluginMgr:BindConsoleCommand("unload", HandleConsoleUnload, "Unloads all unused chunks"); PluginMgr:BindConsoleCommand("unload", HandleConsoleUnload, "Unloads all unused chunks");
PluginMgr:BindConsoleCommand("rank", HandleConsoleRank, " [Player] [Rank] - to add someone to a group"); end
PluginMgr:BindConsoleCommand("listgroups", HandleConsoleListGroups, "Shows a list of all the groups");
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 end
@ -44,11 +88,55 @@ function HandleConsoleHelp(Split)
end end
table.sort(Commands, CompareCommands); table.sort(Commands, CompareCommands);
local Out = "";
for i, Command in ipairs(Commands) do for i, Command in ipairs(Commands) do
local Cmd = Command[1] .. string.rep(" ", MaxLength - Command[1]:len()); -- Align to a table Out = Out .. Command[1] .. string.rep(" ", MaxLength - Command[1]:len()); -- Align to a table
LOG(Cmd .. " - " .. Command[2]); Out = Out .. " - " .. Command[2] .. "\n";
end 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 end
@ -64,13 +152,14 @@ function HandleConsoleNumChunks(Split)
cRoot:Get():ForEachWorld(AddNumChunks); cRoot:Get():ForEachWorld(AddNumChunks);
local Total = 0; local Total = 0;
local Out = "";
for name, num in pairs(Output) do for name, num in pairs(Output) do
LOG(" " .. name .. ": " .. num .. " chunks"); Out = Out .. " " .. name .. ": " .. num .. " chunks\n";
Total = Total + num; Total = Total + num;
end end
LOG("Total: " .. Total .. " chunks"); Out = Out .. "Total: " .. Total .. " chunks\n";
return true; return true, Out;
end end
@ -89,14 +178,15 @@ function HandleConsolePlayers(Split)
cRoot:Get():ForEachPlayer(AddToTable); cRoot:Get():ForEachPlayer(AddToTable);
local Out = "";
for WorldName, Players in pairs(PlayersInWorlds) do for WorldName, Players in pairs(PlayersInWorlds) do
LOG("World " .. WorldName .. ":"); Out = Out .. "World " .. WorldName .. ":\n";
for i, PlayerName in ipairs(Players) do for i, PlayerName in ipairs(Players) do
LOG(" " .. PlayerName); Out = Out .. " " .. PlayerName .. "\n";
end end
end end
return true; return true, Out;
end end
@ -107,15 +197,62 @@ function HandleConsolePrimaryServerVersion(Split)
if (#Split == 1) then if (#Split == 1) then
-- Display current version: -- Display current version:
local Version = cRoot:Get():GetPrimaryServerVersion(); local Version = cRoot:Get():GetPrimaryServerVersion();
LOG("Primary server version: #" .. Version .. ", " .. cRoot:GetProtocolVersionTextFromInt(Version)); return true, "Primary server version: #" .. Version .. ", " .. cRoot:GetProtocolVersionTextFromInt(Version);
return true;
end end
-- Set new value as the version: -- Set new value as the version:
cRoot:Get():SetPrimaryServerVersion(tonumber(Split[2])); cRoot:Get():SetPrimaryServerVersion(tonumber(Split[2]));
local Version = cRoot:Get():GetPrimaryServerVersion(); local Version = cRoot:Get():GetPrimaryServerVersion();
LOG("Primary server version is now #" .. Version .. ", " .. cRoot:GetProtocolVersionTextFromInt(Version)); return true, "Primary server version is now #" .. Version .. ", " .. cRoot:GetProtocolVersionTextFromInt(Version);
return true; 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 end
@ -162,10 +299,10 @@ function HandleConsoleUnload(Split)
World:UnloadUnusedChunks(); World:UnloadUnusedChunks();
end end
LOGINFO("Num loaded chunks before: " .. cRoot:Get():GetTotalChunkCount()); local Out = "Num loaded chunks before: " .. cRoot:Get():GetTotalChunkCount() .. "\n";
cRoot:Get():ForEachWorld(UnloadChunks); cRoot:Get():ForEachWorld(UnloadChunks);
LOGINFO("Num loaded chunks after: " .. cRoot:Get():GetTotalChunkCount()); Out = Out .. "Num loaded chunks after: " .. cRoot:Get():GetTotalChunkCount();
return true; return true, Out;
end end
@ -173,75 +310,34 @@ end
function HandleConsoleRank(Split) -------------------------------------------------------------------------------------------
if Split[2] == nil or Split[3] == nil then -- Helper functions:
LOG("Usage: /rank [Player] [Group]")
return true --- Returns the list of players banned by name, separated by ", "
end function BanListByName()
local GroupsIni = cIniFile("groups.ini") local NumValues = BannedPlayersIni:NumValues("Banned");
if( GroupsIni:ReadFile() == false ) then local Banned = {};
LOG("Could not read groups.ini!") local KeyID = BannedPlayersIni:FindKey("Banned");
end for i = 1, NumValues do
if GroupsIni:FindKey(Split[3]) == -1 then local PlayerName = BannedPlayersIni:ValueName(KeyID, i - 1);
LOG("Group does not exist") if (BannedPlayersIni:GetValueB("Banned", PlayerName)) then
return true -- Player listed AND banned
end table.insert(Banned, PlayerName);
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
end end
local loopWorlds = function ( World ) return table.concat(Banned, ", ");
World:ForEachPlayer( loopPlayers )
end
cRoot:Get():ForEachWorld( loopWorlds )
LOG("Player " .. Split[2] .. " Was moved to " .. Split[3])
return true
end end
--- Returns the list of players banned by IP, separated by ", "
function HandleConsoleListGroups(Split) function BanListByIPs()
local GroupsIni = cIniFile("groups.ini") -- TODO: No IP ban implemented yet
if GroupsIni:ReadFile() == false then return "";
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;
end end
function HandleConsole(Split)
return true;
end

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
/* /*
** Lua binding: AllToLua ** 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 #ifndef __cplusplus
@ -195,7 +195,7 @@ static void tolua_reg_types (lua_State* tolua_S)
{ {
tolua_usertype(tolua_S,"TakeDamageInfo"); tolua_usertype(tolua_S,"TakeDamageInfo");
tolua_usertype(tolua_S,"cCraftingRecipe"); 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,"cStringMap");
tolua_usertype(tolua_S,"cItemGrid"); tolua_usertype(tolua_S,"cItemGrid");
tolua_usertype(tolua_S,"cBlockArea"); 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,"cRoot");
tolua_usertype(tolua_S,"cWindow"); tolua_usertype(tolua_S,"cWindow");
tolua_usertype(tolua_S,"cCraftingGrid"); tolua_usertype(tolua_S,"cCraftingGrid");
tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cPickup"); tolua_usertype(tolua_S,"cPickup");
tolua_usertype(tolua_S,"cItems"); tolua_usertype(tolua_S,"cItems");
tolua_usertype(tolua_S,"cGroup"); tolua_usertype(tolua_S,"cGroup");
tolua_usertype(tolua_S,"cClientHandle"); tolua_usertype(tolua_S,"cClientHandle");
tolua_usertype(tolua_S,"cChunkDesc"); tolua_usertype(tolua_S,"cChunkDesc");
tolua_usertype(tolua_S,"cFurnaceRecipe"); tolua_usertype(tolua_S,"cFurnaceRecipe");
tolua_usertype(tolua_S,"cCuboid"); tolua_usertype(tolua_S,"cTracer");
tolua_usertype(tolua_S,"cChatColor"); tolua_usertype(tolua_S,"cChatColor");
tolua_usertype(tolua_S,"cCuboid");
tolua_usertype(tolua_S,"Vector3i"); tolua_usertype(tolua_S,"Vector3i");
tolua_usertype(tolua_S,"cPlugin_NewLua");
tolua_usertype(tolua_S,"Lua__cWebPlugin"); tolua_usertype(tolua_S,"Lua__cWebPlugin");
tolua_usertype(tolua_S,"Lua__cPawn"); 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,"cItem");
tolua_usertype(tolua_S,"Vector3f"); tolua_usertype(tolua_S,"Vector3f");
tolua_usertype(tolua_S,"cCraftingRecipes"); tolua_usertype(tolua_S,"cWebAdmin");
tolua_usertype(tolua_S,"cDropSpenserEntity"); tolua_usertype(tolua_S,"cDropSpenserEntity");
tolua_usertype(tolua_S,"Lua__cPlayer"); 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,"cChestEntity");
tolua_usertype(tolua_S,"cDispenserEntity"); 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,"cBlockEntity");
tolua_usertype(tolua_S,"cCriticalSection"); tolua_usertype(tolua_S,"cCriticalSection");
tolua_usertype(tolua_S,"Lua__cPickup");
tolua_usertype(tolua_S,"cWebPlugin"); tolua_usertype(tolua_S,"cWebPlugin");
tolua_usertype(tolua_S,"HTTPRequest"); tolua_usertype(tolua_S,"HTTPRequest");
tolua_usertype(tolua_S,"HTTPFormData"); tolua_usertype(tolua_S,"HTTPFormData");
tolua_usertype(tolua_S,"cFurnaceEntity"); tolua_usertype(tolua_S,"cFurnaceEntity");
tolua_usertype(tolua_S,"cDropperEntity");
tolua_usertype(tolua_S,"cPluginManager"); tolua_usertype(tolua_S,"cPluginManager");
tolua_usertype(tolua_S,"cWorld"); tolua_usertype(tolua_S,"cDropperEntity");
tolua_usertype(tolua_S,"cIniFile"); tolua_usertype(tolua_S,"cIniFile");
tolua_usertype(tolua_S,"cPlugin"); tolua_usertype(tolua_S,"cWorld");
tolua_usertype(tolua_S,"AStringVector"); tolua_usertype(tolua_S,"cListeners");
tolua_usertype(tolua_S,"cPawn"); tolua_usertype(tolua_S,"cPawn");
tolua_usertype(tolua_S,"cPlayer"); tolua_usertype(tolua_S,"cPlayer");
tolua_usertype(tolua_S,"cGroupManager"); tolua_usertype(tolua_S,"cGroupManager");
tolua_usertype(tolua_S,"cBlockEntityWindowOwner"); 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,"cServer");
tolua_usertype(tolua_S,"cListeners"); tolua_usertype(tolua_S,"cBlockEntityWithItems");
tolua_usertype(tolua_S,"Lua__cEntity"); tolua_usertype(tolua_S,"Lua__cEntity");
tolua_usertype(tolua_S,"Vector3d"); tolua_usertype(tolua_S,"Vector3d");
} }
@ -5878,18 +5877,20 @@ static int tolua_AllToLua_cEntity_Destroy00(lua_State* tolua_S)
tolua_Error tolua_err; tolua_Error tolua_err;
if ( if (
!tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || !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; goto tolua_lerror;
else else
#endif #endif
{ {
cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0);
bool a_ShouldBroadcast = ((bool) tolua_toboolean(tolua_S,2,true));
#ifndef TOLUA_RELEASE #ifndef TOLUA_RELEASE
if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Destroy'", NULL); if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Destroy'", NULL);
#endif #endif
{ {
self->Destroy(); self->Destroy(a_ShouldBroadcast);
} }
} }
return 0; return 0;
@ -10169,40 +10170,6 @@ static int tolua_AllToLua_cPluginManager_IsConsoleCommandBound00(lua_State* tolu
} }
#endif //#ifndef TOLUA_DISABLE #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 */ /* method: GetName of class cPlugin */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetName00 #ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetName00
static int tolua_AllToLua_cPlugin_GetName00(lua_State* tolua_S) 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 #endif //#ifndef TOLUA_DISABLE
/* method: ExecuteConsoleCommand of class cRoot */ /* method: QueueExecuteConsoleCommand of class cRoot */
#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_ExecuteConsoleCommand00 #ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00
static int tolua_AllToLua_cRoot_ExecuteConsoleCommand00(lua_State* tolua_S) static int tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00(lua_State* tolua_S)
{ {
#ifndef TOLUA_RELEASE #ifndef TOLUA_RELEASE
tolua_Error tolua_err; 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); cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0);
const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0)); const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0));
#ifndef TOLUA_RELEASE #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 #endif
{ {
self->ExecuteConsoleCommand(a_Cmd); self->QueueExecuteConsoleCommand(a_Cmd);
tolua_pushcppstring(tolua_S,(const char*)a_Cmd); tolua_pushcppstring(tolua_S,(const char*)a_Cmd);
} }
} }
return 1; return 1;
#ifndef TOLUA_RELEASE #ifndef TOLUA_RELEASE
tolua_lerror: tolua_lerror:
tolua_error(tolua_S,"#ferror in function 'ExecuteConsoleCommand'.",&tolua_err); tolua_error(tolua_S,"#ferror in function 'QueueExecuteConsoleCommand'.",&tolua_err);
return 0; return 0;
#endif #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,"ExecuteCommand",tolua_AllToLua_cPluginManager_ExecuteCommand00);
tolua_function(tolua_S,"ForceExecuteCommand",tolua_AllToLua_cPluginManager_ForceExecuteCommand00); tolua_function(tolua_S,"ForceExecuteCommand",tolua_AllToLua_cPluginManager_ForceExecuteCommand00);
tolua_function(tolua_S,"IsConsoleCommandBound",tolua_AllToLua_cPluginManager_IsConsoleCommandBound00); tolua_function(tolua_S,"IsConsoleCommandBound",tolua_AllToLua_cPluginManager_IsConsoleCommandBound00);
tolua_function(tolua_S,"ExecuteConsoleCommand",tolua_AllToLua_cPluginManager_ExecuteConsoleCommand00);
tolua_endmodule(tolua_S); tolua_endmodule(tolua_S);
tolua_cclass(tolua_S,"cPlugin","cPlugin","",NULL); tolua_cclass(tolua_S,"cPlugin","cPlugin","",NULL);
tolua_beginmodule(tolua_S,"cPlugin"); 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,"GetFurnaceRecipe",tolua_AllToLua_cRoot_GetFurnaceRecipe00);
tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00); tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00);
tolua_function(tolua_S,"GetPluginManager",tolua_AllToLua_cRoot_GetPluginManager00); 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,"GetTotalChunkCount",tolua_AllToLua_cRoot_GetTotalChunkCount00);
tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00); tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00);
tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00); tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00);

View File

@ -1,6 +1,6 @@
/* /*
** Lua binding: AllToLua ** 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 */ /* Exported function */

71
source/CommandOutput.cpp Normal file
View File

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

82
source/CommandOutput.h Normal file
View File

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

View File

@ -5,6 +5,7 @@
#include "Pawn.h" #include "Pawn.h"
#include "Player.h" #include "Player.h"
#include "World.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_Split);
UNUSED(a_Output);
return false; return false;
} }

View File

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

View File

@ -8,6 +8,7 @@
#include "Item.h" #include "Item.h"
#include "Root.h" #include "Root.h"
#include "Server.h" #include "Server.h"
#include "CommandOutput.h"
#include "../iniFile/iniFile.h" #include "../iniFile/iniFile.h"
#include "tolua++.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()) 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: // Ask plugins first if a command is okay to execute the console command:
if (CallHookExecuteCommand(NULL, a_Split)) 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 false;
} }
return cmd->second.m_Plugin->HandleConsoleCommand(a_Split); return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output);
} }

View File

@ -29,6 +29,9 @@ class cPickup;
struct TakeDamageInfo; struct TakeDamageInfo;
class cPawn; 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 /// Returns true if the console command is in the command map
bool IsConsoleCommandBound(const AString & a_Command); // tolua_export 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. /// 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); // tolua_export bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output);
private: private:
friend class cRoot; friend class cRoot;

View File

@ -3,7 +3,7 @@
#define LUA_USE_POSIX #define LUA_USE_POSIX
#include "Plugin_NewLua.h" #include "Plugin_NewLua.h"
#include "MCLogger.h" #include "CommandOutput.h"
extern "C" 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()); ASSERT(!a_Split.empty());
CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]);
if (cmd == m_ConsoleCommands.end()) 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; return false;
} }
@ -1407,16 +1409,21 @@ bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split)
} }
// Call function: // 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)) if (report_errors(m_LuaState, s))
{ {
LOGERROR("Lua error. Stack size: %i", lua_gettop(m_LuaState)); LOGERROR("Lua error. Stack size: %i", lua_gettop(m_LuaState));
return false; return false;
} }
// Handle return value: // Handle return values:
bool RetVal = (tolua_toboolean(m_LuaState, -1, 0) > 0); if (lua_isstring(m_LuaState, -1))
lua_pop(m_LuaState, 1); // Pop return value {
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; return RetVal;
} }

View File

@ -77,7 +77,7 @@ public:
virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override; 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; virtual void ClearCommands(void) override;

View File

@ -8,6 +8,7 @@
#include "RCONServer.h" #include "RCONServer.h"
#include "Server.h" #include "Server.h"
#include "Root.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: // cRCONServer:
@ -218,9 +254,16 @@ bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType,
case RCON_PACKET_COMMAND: 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); AString cmd(a_Payload, a_PayloadLength);
LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str()); 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: // Send an empty response:
SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL);

View File

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

View File

@ -15,6 +15,7 @@
#include "Items/ItemHandler.h" #include "Items/ItemHandler.h"
#include "Chunk.h" #include "Chunk.h"
#include "Protocol/ProtocolRecognizer.h" // for protocol version constants #include "Protocol/ProtocolRecognizer.h" // for protocol version constants
#include "CommandOutput.h"
#ifdef USE_SQUIRREL #ifdef USE_SQUIRREL
#include "squirrelbindings/SquirrelFunctions.h" #include "squirrelbindings/SquirrelFunctions.h"
@ -69,11 +70,13 @@ void cRoot::InputThread(void * a_Params)
{ {
cRoot & self = *(cRoot*)a_Params; cRoot & self = *(cRoot*)a_Params;
cLogCommandOutputCallback Output;
while (!(self.m_bStop || self.m_bRestart)) while (!(self.m_bStop || self.m_bRestart))
{ {
std::string Command; std::string Command;
std::getline(std::cin, 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) void cRoot::TickWorlds(float a_Dt)
{ {
// Execute any pending commands: // Execute any pending commands:
AStringVector PendingCommands; cCommandQueue PendingCommands;
{ {
cCSLock Lock(m_CSPendingCommands); cCSLock Lock(m_CSPendingCommands);
std::swap(PendingCommands, m_PendingCommands); 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: // 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: // Some commands are built-in:
if (a_Cmd == "stop") if (a_Cmd == "stop")
@ -385,17 +388,48 @@ void cRoot::ExecuteConsoleCommand(const AString & a_Cmd)
// Put the command into a queue (Alleviates FS #363): // Put the command into a queue (Alleviates FS #363):
cCSLock Lock(m_CSPendingCommands); 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()); 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 SumNumValid = 0;
int SumNumDirty = 0; int SumNumDirty = 0;
@ -541,35 +575,35 @@ void cRoot::LogChunkStats(void)
int NumDirty = 0; int NumDirty = 0;
int NumInLighting = 0; int NumInLighting = 0;
World->GetChunkStats(NumValid, NumDirty, NumInLighting); World->GetChunkStats(NumValid, NumDirty, NumInLighting);
LOG("World %s:", World->GetName().c_str()); a_Output.Out("World %s:", World->GetName().c_str());
LOG(" Num loaded chunks: %d", NumValid); a_Output.Out(" Num loaded chunks: %d", NumValid);
LOG(" Num dirty chunks: %d", NumDirty); a_Output.Out(" Num dirty chunks: %d", NumDirty);
LOG(" Num chunks in lighting queue: %d", NumInLighting); a_Output.Out(" Num chunks in lighting queue: %d", NumInLighting);
LOG(" Num chunks in generator queue: %d", NumInGenerator); a_Output.Out(" Num chunks in generator queue: %d", NumInGenerator);
LOG(" Num chunks in storage load queue: %d", NumInLoadQueue); a_Output.Out(" Num chunks in storage load queue: %d", NumInLoadQueue);
LOG(" Num chunks in storage save queue: %d", NumInSaveQueue); a_Output.Out(" Num chunks in storage save queue: %d", NumInSaveQueue);
int Mem = NumValid * sizeof(cChunk); int Mem = NumValid * sizeof(cChunk);
LOG(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024)); a_Output.Out(" Memory used by chunks: %d KiB (%d MiB)", (Mem + 1023) / 1024, (Mem + 1024 * 1024 - 1) / (1024 * 1024));
LOG(" Per-chunk memory size breakdown:"); a_Output.Out(" Per-chunk memory size breakdown:");
LOG(" block types: %6d bytes (%3d KiB)", sizeof(cChunkDef::BlockTypes), (sizeof(cChunkDef::BlockTypes) + 1023) / 1024); a_Output.Out(" 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); a_Output.Out(" 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); a_Output.Out(" 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); a_Output.Out(" 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(" 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); 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; SumNumValid += NumValid;
SumNumDirty += NumDirty; SumNumDirty += NumDirty;
SumNumInLighting += NumInLighting; SumNumInLighting += NumInLighting;
SumNumInGenerator += NumInGenerator; SumNumInGenerator += NumInGenerator;
SumMem += Mem; SumMem += Mem;
} }
LOG("Totals:"); a_Output.Out("Totals:");
LOG(" Num loaded chunks: %d", SumNumValid); a_Output.Out(" Num loaded chunks: %d", SumNumValid);
LOG(" Num dirty chunks: %d", SumNumDirty); a_Output.Out(" Num dirty chunks: %d", SumNumDirty);
LOG(" Num chunks in lighting queue: %d", SumNumInLighting); a_Output.Out(" Num chunks in lighting queue: %d", SumNumInLighting);
LOG(" Num chunks in generator queue: %d", SumNumInGenerator); a_Output.Out(" 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(" Memory used by chunks: %d KiB (%d MiB)", (SumMem + 1023) / 1024, (SumMem + 1024 * 1024 - 1) / (1024 * 1024));
} }

View File

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

View File

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

View File

@ -19,9 +19,11 @@
// fwd:
class cPlayer; class cPlayer;
class cClientHandle; class cClientHandle;
class cIniFile; class cIniFile;
class cCommandOutputCallback ;
typedef std::list<cClientHandle *> cClientHandleList; typedef std::list<cClientHandle *> cClientHandleList;
@ -44,7 +46,9 @@ public: // tolua_export
bool Start(void); bool Start(void);
bool Command(cClientHandle & a_Client, AString & a_Cmd); 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 /// Binds the built-in console commands with the plugin manager
static void BindBuiltInConsoleCommands(void); static void BindBuiltInConsoleCommands(void);

View File

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