From 7b75aaea7c538f61518a60fe4af363383020e0bc Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Sat, 29 Jun 2013 15:30:05 +0000 Subject: 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 --- source/Bindings.cpp | 84 +++++++++++++------------------------------ source/Bindings.h | 2 +- source/CommandOutput.cpp | 71 ++++++++++++++++++++++++++++++++++++ source/CommandOutput.h | 82 ++++++++++++++++++++++++++++++++++++++++++ source/Plugin.cpp | 4 ++- source/Plugin.h | 6 ++-- source/PluginManager.cpp | 7 ++-- source/PluginManager.h | 7 ++-- source/Plugin_NewLua.cpp | 21 +++++++---- source/Plugin_NewLua.h | 2 +- source/RCONServer.cpp | 45 ++++++++++++++++++++++- source/RCONServer.h | 3 ++ source/Root.cpp | 94 ++++++++++++++++++++++++++++++++---------------- source/Root.h | 52 +++++++++++++++++++++------ source/Server.cpp | 12 ++++--- source/Server.h | 6 +++- source/StringUtils.cpp | 23 ------------ 17 files changed, 375 insertions(+), 146 deletions(-) create mode 100644 source/CommandOutput.cpp create mode 100644 source/CommandOutput.h (limited to 'source') diff --git a/source/Bindings.cpp b/source/Bindings.cpp index 386ec2e41..ec164b764 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 06/22/13 19:31:23. +** Generated automatically by tolua++-1.0.92 on 06/29/13 17:21:35. */ #ifndef __cplusplus @@ -195,7 +195,7 @@ static void tolua_reg_types (lua_State* tolua_S) { tolua_usertype(tolua_S,"TakeDamageInfo"); tolua_usertype(tolua_S,"cCraftingRecipe"); - tolua_usertype(tolua_S,"cEntity"); + tolua_usertype(tolua_S,"cPlugin_NewLua"); tolua_usertype(tolua_S,"cStringMap"); tolua_usertype(tolua_S,"cItemGrid"); tolua_usertype(tolua_S,"cBlockArea"); @@ -205,48 +205,47 @@ static void tolua_reg_types (lua_State* tolua_S) tolua_usertype(tolua_S,"cRoot"); tolua_usertype(tolua_S,"cWindow"); tolua_usertype(tolua_S,"cCraftingGrid"); - tolua_usertype(tolua_S,"cTracer"); tolua_usertype(tolua_S,"cPickup"); tolua_usertype(tolua_S,"cItems"); tolua_usertype(tolua_S,"cGroup"); tolua_usertype(tolua_S,"cClientHandle"); tolua_usertype(tolua_S,"cChunkDesc"); tolua_usertype(tolua_S,"cFurnaceRecipe"); - tolua_usertype(tolua_S,"cCuboid"); + tolua_usertype(tolua_S,"cTracer"); tolua_usertype(tolua_S,"cChatColor"); + tolua_usertype(tolua_S,"cCuboid"); tolua_usertype(tolua_S,"Vector3i"); - tolua_usertype(tolua_S,"cPlugin_NewLua"); tolua_usertype(tolua_S,"Lua__cWebPlugin"); tolua_usertype(tolua_S,"Lua__cPawn"); - tolua_usertype(tolua_S,"cWebAdmin"); + tolua_usertype(tolua_S,"cEntity"); tolua_usertype(tolua_S,"cItem"); tolua_usertype(tolua_S,"Vector3f"); - tolua_usertype(tolua_S,"cCraftingRecipes"); + tolua_usertype(tolua_S,"cWebAdmin"); tolua_usertype(tolua_S,"cDropSpenserEntity"); tolua_usertype(tolua_S,"Lua__cPlayer"); - tolua_usertype(tolua_S,"cItemGrid::cListener"); + tolua_usertype(tolua_S,"cCraftingRecipes"); tolua_usertype(tolua_S,"cChestEntity"); tolua_usertype(tolua_S,"cDispenserEntity"); - tolua_usertype(tolua_S,"Lua__cPickup"); + tolua_usertype(tolua_S,"cPlugin"); tolua_usertype(tolua_S,"cBlockEntity"); tolua_usertype(tolua_S,"cCriticalSection"); + tolua_usertype(tolua_S,"Lua__cPickup"); tolua_usertype(tolua_S,"cWebPlugin"); tolua_usertype(tolua_S,"HTTPRequest"); tolua_usertype(tolua_S,"HTTPFormData"); tolua_usertype(tolua_S,"cFurnaceEntity"); - tolua_usertype(tolua_S,"cDropperEntity"); tolua_usertype(tolua_S,"cPluginManager"); - tolua_usertype(tolua_S,"cWorld"); + tolua_usertype(tolua_S,"cDropperEntity"); tolua_usertype(tolua_S,"cIniFile"); - tolua_usertype(tolua_S,"cPlugin"); - tolua_usertype(tolua_S,"AStringVector"); + tolua_usertype(tolua_S,"cWorld"); + tolua_usertype(tolua_S,"cListeners"); tolua_usertype(tolua_S,"cPawn"); tolua_usertype(tolua_S,"cPlayer"); tolua_usertype(tolua_S,"cGroupManager"); tolua_usertype(tolua_S,"cBlockEntityWindowOwner"); - tolua_usertype(tolua_S,"cBlockEntityWithItems"); + tolua_usertype(tolua_S,"cItemGrid::cListener"); tolua_usertype(tolua_S,"cServer"); - tolua_usertype(tolua_S,"cListeners"); + tolua_usertype(tolua_S,"cBlockEntityWithItems"); tolua_usertype(tolua_S,"Lua__cEntity"); tolua_usertype(tolua_S,"Vector3d"); } @@ -5878,18 +5877,20 @@ static int tolua_AllToLua_cEntity_Destroy00(lua_State* tolua_S) tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"cEntity",0,&tolua_err) || - !tolua_isnoobj(tolua_S,2,&tolua_err) + !tolua_isboolean(tolua_S,2,1,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) ) goto tolua_lerror; else #endif { cEntity* self = (cEntity*) tolua_tousertype(tolua_S,1,0); + bool a_ShouldBroadcast = ((bool) tolua_toboolean(tolua_S,2,true)); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'Destroy'", NULL); #endif { - self->Destroy(); + self->Destroy(a_ShouldBroadcast); } } return 0; @@ -10169,40 +10170,6 @@ static int tolua_AllToLua_cPluginManager_IsConsoleCommandBound00(lua_State* tolu } #endif //#ifndef TOLUA_DISABLE -/* method: ExecuteConsoleCommand of class cPluginManager */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_ExecuteConsoleCommand00 -static int tolua_AllToLua_cPluginManager_ExecuteConsoleCommand00(lua_State* tolua_S) -{ -#ifndef TOLUA_RELEASE - tolua_Error tolua_err; - if ( - !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || - (tolua_isvaluenil(tolua_S,2,&tolua_err) || !tolua_isusertype(tolua_S,2,"const AStringVector",0,&tolua_err)) || - !tolua_isnoobj(tolua_S,3,&tolua_err) - ) - goto tolua_lerror; - else -#endif - { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - const AStringVector* a_Split = ((const AStringVector*) tolua_tousertype(tolua_S,2,0)); -#ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteConsoleCommand'", NULL); -#endif - { - bool tolua_ret = (bool) self->ExecuteConsoleCommand(*a_Split); - tolua_pushboolean(tolua_S,(bool)tolua_ret); - } - } - return 1; -#ifndef TOLUA_RELEASE - tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ExecuteConsoleCommand'.",&tolua_err); - return 0; -#endif -} -#endif //#ifndef TOLUA_DISABLE - /* method: GetName of class cPlugin */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cPlugin_GetName00 static int tolua_AllToLua_cPlugin_GetName00(lua_State* tolua_S) @@ -18771,9 +18738,9 @@ static int tolua_AllToLua_cRoot_GetPluginManager00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: ExecuteConsoleCommand of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_ExecuteConsoleCommand00 -static int tolua_AllToLua_cRoot_ExecuteConsoleCommand00(lua_State* tolua_S) +/* method: QueueExecuteConsoleCommand of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00 +static int tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; @@ -18789,17 +18756,17 @@ static int tolua_AllToLua_cRoot_ExecuteConsoleCommand00(lua_State* tolua_S) cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0)); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteConsoleCommand'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'QueueExecuteConsoleCommand'", NULL); #endif { - self->ExecuteConsoleCommand(a_Cmd); + self->QueueExecuteConsoleCommand(a_Cmd); tolua_pushcppstring(tolua_S,(const char*)a_Cmd); } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ExecuteConsoleCommand'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'QueueExecuteConsoleCommand'.",&tolua_err); return 0; #endif } @@ -28187,7 +28154,6 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"ExecuteCommand",tolua_AllToLua_cPluginManager_ExecuteCommand00); tolua_function(tolua_S,"ForceExecuteCommand",tolua_AllToLua_cPluginManager_ForceExecuteCommand00); tolua_function(tolua_S,"IsConsoleCommandBound",tolua_AllToLua_cPluginManager_IsConsoleCommandBound00); - tolua_function(tolua_S,"ExecuteConsoleCommand",tolua_AllToLua_cPluginManager_ExecuteConsoleCommand00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cPlugin","cPlugin","",NULL); tolua_beginmodule(tolua_S,"cPlugin"); @@ -28599,7 +28565,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"GetFurnaceRecipe",tolua_AllToLua_cRoot_GetFurnaceRecipe00); tolua_function(tolua_S,"GetWebAdmin",tolua_AllToLua_cRoot_GetWebAdmin00); tolua_function(tolua_S,"GetPluginManager",tolua_AllToLua_cRoot_GetPluginManager00); - tolua_function(tolua_S,"ExecuteConsoleCommand",tolua_AllToLua_cRoot_ExecuteConsoleCommand00); + tolua_function(tolua_S,"QueueExecuteConsoleCommand",tolua_AllToLua_cRoot_QueueExecuteConsoleCommand00); tolua_function(tolua_S,"GetTotalChunkCount",tolua_AllToLua_cRoot_GetTotalChunkCount00); tolua_function(tolua_S,"SaveAllChunks",tolua_AllToLua_cRoot_SaveAllChunks00); tolua_function(tolua_S,"GetProtocolVersionTextFromInt",tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00); diff --git a/source/Bindings.h b/source/Bindings.h index df29c6201..c13cf32bd 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 06/22/13 19:31:24. +** Generated automatically by tolua++-1.0.92 on 06/29/13 17:21:36. */ /* Exported function */ diff --git a/source/CommandOutput.cpp b/source/CommandOutput.cpp new file mode 100644 index 000000000..49102b38c --- /dev/null +++ b/source/CommandOutput.cpp @@ -0,0 +1,71 @@ + +// CommandOutput.cpp + +// Implements the various classes that process command output + +#include "Globals.h" +#include "CommandOutput.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cCommandOutputCallback: + +void cCommandOutputCallback::Out(const char * a_Fmt, ...) +{ + AString Output; + va_list args; + va_start(args, a_Fmt); + AppendVPrintf(Output, a_Fmt, args); + va_end(args); + Output.append("\n"); + Out(Output); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cLogCommandOutputCallback: + +void cLogCommandOutputCallback::Out(const AString & a_Text) +{ + m_Buffer.append(a_Text); +} + + + + + +void cLogCommandOutputCallback::Finished(void) +{ + // Log each line separately: + size_t len = m_Buffer.length(); + size_t last = 0; + for (size_t i = 0; i < len; i++) + { + switch (m_Buffer[i]) + { + case '\n': + { + LOG(m_Buffer.substr(last, i - last).c_str()); + last = i + 1; + break; + } + } + } // for i - m_Buffer[] + if (last < len) + { + LOG(m_Buffer.substr(last).c_str()); + } + + // Clear the buffer for the next command output: + m_Buffer.clear(); +} + + + + diff --git a/source/CommandOutput.h b/source/CommandOutput.h new file mode 100644 index 000000000..68217dbb4 --- /dev/null +++ b/source/CommandOutput.h @@ -0,0 +1,82 @@ + +// CommandOutput.h + +// Declares various classes that process command output + + + + + +/** Interface for a callback that receives command output +The Out() function is called for any output the command has produced. +Descendants override that function to provide specific processing of the output. +*/ +class cCommandOutputCallback +{ +public: + virtual ~cCommandOutputCallback() {}; // Force a virtual destructor in subclasses + + /// Syntax sugar function, calls Out() with Printf()-ed parameters; appends a "\n" + void Out(const char * a_Fmt, ...); + + /// Called when the command wants to output anything; may be called multiple times + virtual void Out(const AString & a_Text) = 0; + + /// Called when the command processing has been finished + virtual void Finished(void) {}; +} ; + + + + + +/// Class that discards all command output +class cNullCommandOutputCallback : + public cCommandOutputCallback +{ + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override + { + // Do nothing + } +} ; + + + + + + +/// Sends all command output to a log, line by line, when the command finishes processing +class cLogCommandOutputCallback : + public cCommandOutputCallback +{ +public: + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override; + virtual void Finished(void) override; + +protected: + /// Output is stored here until the command finishes processing + AString m_Buffer; +} ; + + + + + +/// Sends all command output to a log, line by line; deletes self when command finishes processing +class cLogCommandDeleteSelfOutputCallback : + public cLogCommandOutputCallback +{ + typedef cLogCommandOutputCallback super; + + virtual void Finished(void) override + { + super::Finished(); + delete this; + } +} ; + + + + diff --git a/source/Plugin.cpp b/source/Plugin.cpp index 389ef15e4..daf1d33c8 100644 --- a/source/Plugin.cpp +++ b/source/Plugin.cpp @@ -5,6 +5,7 @@ #include "Pawn.h" #include "Player.h" #include "World.h" +#include "CommandOutput.h" @@ -546,9 +547,10 @@ bool cPlugin::HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) -bool cPlugin::HandleConsoleCommand(const AStringVector & a_Split) +bool cPlugin::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) { UNUSED(a_Split); + UNUSED(a_Output); return false; } diff --git a/source/Plugin.h b/source/Plugin.h index 9107cce6f..91f5f5286 100644 --- a/source/Plugin.h +++ b/source/Plugin.h @@ -92,8 +92,10 @@ public: */ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player); - /// Handles the console command split into a_Split. Returns true if command handled successfully. - virtual bool HandleConsoleCommand(const AStringVector & a_Split); + /** Handles the console command split into a_Split. + Returns true if command handled successfully. Output is to be sent to the a_Output callback. + */ + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output); /// All bound commands are to be removed, do any language-dependent cleanup here virtual void ClearCommands(void) {} ; diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp index a180f5618..32f2a2a99 100644 --- a/source/PluginManager.cpp +++ b/source/PluginManager.cpp @@ -8,6 +8,7 @@ #include "Item.h" #include "Root.h" #include "Server.h" +#include "CommandOutput.h" #include "../iniFile/iniFile.h" #include "tolua++.h" @@ -1300,7 +1301,7 @@ bool cPluginManager::IsConsoleCommandBound(const AString & a_Command) -bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split) +bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) { if (a_Split.empty()) { @@ -1323,11 +1324,11 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split) // Ask plugins first if a command is okay to execute the console command: if (CallHookExecuteCommand(NULL, a_Split)) { - LOGINFO("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str()); + a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str()); return false; } - return cmd->second.m_Plugin->HandleConsoleCommand(a_Split); + return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output); } diff --git a/source/PluginManager.h b/source/PluginManager.h index 655081568..9268fe66d 100644 --- a/source/PluginManager.h +++ b/source/PluginManager.h @@ -29,6 +29,9 @@ class cPickup; struct TakeDamageInfo; class cPawn; +// fwd: CommandOutput.h +class cCommandOutputCallback; + @@ -191,8 +194,8 @@ public: // tolua_export /// Returns true if the console command is in the command map bool IsConsoleCommandBound(const AString & a_Command); // tolua_export - /// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. - bool ExecuteConsoleCommand(const AStringVector & a_Split); // tolua_export + /// Executes the command split into a_Split, as if it was given on the console. Returns true if executed. Output is sent to the a_Output callback + bool ExecuteConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output); private: friend class cRoot; diff --git a/source/Plugin_NewLua.cpp b/source/Plugin_NewLua.cpp index 335ac000a..1e4a6a94a 100644 --- a/source/Plugin_NewLua.cpp +++ b/source/Plugin_NewLua.cpp @@ -3,7 +3,7 @@ #define LUA_USE_POSIX #include "Plugin_NewLua.h" -#include "MCLogger.h" +#include "CommandOutput.h" extern "C" { @@ -1378,13 +1378,15 @@ bool cPlugin_NewLua::HandleCommand(const AStringVector & a_Split, cPlayer * a_Pl -bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split) +bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) { ASSERT(!a_Split.empty()); CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); if (cmd == m_ConsoleCommands.end()) { - LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\".", a_Split[0].c_str()); + LOGWARNING("Console command handler is registered in cPluginManager but not in cPlugin, wtf? Console command \"%s\", plugin \"%s\".", + a_Split[0].c_str(), GetName().c_str() + ); return false; } @@ -1407,16 +1409,21 @@ bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split) } // Call function: - int s = lua_pcall(m_LuaState, 1, 1, 0); + int s = lua_pcall(m_LuaState, 1, 2, 0); if (report_errors(m_LuaState, s)) { LOGERROR("Lua error. Stack size: %i", lua_gettop(m_LuaState)); return false; } - // Handle return value: - bool RetVal = (tolua_toboolean(m_LuaState, -1, 0) > 0); - lua_pop(m_LuaState, 1); // Pop return value + // Handle return values: + if (lua_isstring(m_LuaState, -1)) + { + AString str = tolua_tocppstring(m_LuaState, -1, ""); + a_Output.Out(str); + } + bool RetVal = (tolua_toboolean(m_LuaState, -2, 0) > 0); + lua_pop(m_LuaState, 2); // Pop return values return RetVal; } diff --git a/source/Plugin_NewLua.h b/source/Plugin_NewLua.h index 086568f76..7abd0f9fe 100644 --- a/source/Plugin_NewLua.h +++ b/source/Plugin_NewLua.h @@ -77,7 +77,7 @@ public: virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override; - virtual bool HandleConsoleCommand(const AStringVector & a_Split) override; + virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output) override; virtual void ClearCommands(void) override; diff --git a/source/RCONServer.cpp b/source/RCONServer.cpp index 9558512eb..cf6ed8b0c 100644 --- a/source/RCONServer.cpp +++ b/source/RCONServer.cpp @@ -8,6 +8,7 @@ #include "RCONServer.h" #include "Server.h" #include "Root.h" +#include "CommandOutput.h" @@ -37,6 +38,41 @@ enum +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cRCONCommandOutput: + +class cRCONCommandOutput : + public cCommandOutputCallback +{ +public: + cRCONCommandOutput(cRCONServer::cConnection & a_Connection, int a_RequestID) : + m_Connection(a_Connection), + m_RequestID(a_RequestID) + { + } + + // cCommandOutputCallback overrides: + virtual void Out(const AString & a_Text) override + { + m_Buffer.append(a_Text); + } + + virtual void Finished(void) override + { + m_Connection.SendResponse(m_RequestID, RCON_PACKET_RESPONSE, m_Buffer.size(), m_Buffer.c_str()); + delete this; + } + +protected: + cRCONServer::cConnection & m_Connection; + int m_RequestID; + AString m_Buffer; +} ; + + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cRCONServer: @@ -218,9 +254,16 @@ bool cRCONServer::cConnection::ProcessPacket(int a_RequestID, int a_PacketType, case RCON_PACKET_COMMAND: { + if (!m_IsAuthenticated) + { + char AuthNeeded[] = "You need to authenticate first!"; + SendResponse(a_RequestID, RCON_PACKET_RESPONSE, sizeof(AuthNeeded), AuthNeeded); + return false; + } + AString cmd(a_Payload, a_PayloadLength); LOGD("RCON command from %s: \"%s\"", m_IPAddress.c_str(), cmd.c_str()); - cRoot::Get()->ExecuteConsoleCommand(cmd); + cRoot::Get()->ExecuteConsoleCommand(cmd, *(new cRCONCommandOutput(*this, a_RequestID))); // Send an empty response: SendResponse(a_RequestID, RCON_PACKET_RESPONSE, 0, NULL); diff --git a/source/RCONServer.h b/source/RCONServer.h index 95122ba5f..a4a0d20fe 100644 --- a/source/RCONServer.h +++ b/source/RCONServer.h @@ -34,6 +34,8 @@ public: void Initialize(cIniFile & a_IniFile); protected: + friend class cRCONCommandOutput; + class cConnection : public cSocketThreads::cCallback { @@ -41,6 +43,7 @@ protected: cConnection(cRCONServer & a_RCONServer, cSocket & a_Socket); protected: + friend class cRCONCommandOutput; /// Set to true if the client has successfully authenticated bool m_IsAuthenticated; diff --git a/source/Root.cpp b/source/Root.cpp index c7be00c20..771986003 100644 --- a/source/Root.cpp +++ b/source/Root.cpp @@ -15,6 +15,7 @@ #include "Items/ItemHandler.h" #include "Chunk.h" #include "Protocol/ProtocolRecognizer.h" // for protocol version constants +#include "CommandOutput.h" #ifdef USE_SQUIRREL #include "squirrelbindings/SquirrelFunctions.h" @@ -69,11 +70,13 @@ void cRoot::InputThread(void * a_Params) { cRoot & self = *(cRoot*)a_Params; + cLogCommandOutputCallback Output; + while (!(self.m_bStop || self.m_bRestart)) { std::string Command; std::getline(std::cin, Command); - self.ExecuteConsoleCommand(Command); + self.ExecuteConsoleCommand(Command, Output); } } @@ -350,14 +353,14 @@ bool cRoot::ForEachWorld(cWorldListCallback & a_Callback) void cRoot::TickWorlds(float a_Dt) { // Execute any pending commands: - AStringVector PendingCommands; + cCommandQueue PendingCommands; { cCSLock Lock(m_CSPendingCommands); std::swap(PendingCommands, m_PendingCommands); } - for (AStringVector::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr) + for (cCommandQueue::iterator itr = PendingCommands.begin(), end = PendingCommands.end(); itr != end; ++itr) { - DoExecuteConsoleCommand(*itr); + ExecuteConsoleCommand(itr->m_Command, *(itr->m_Output)); } // Tick the worlds: @@ -371,7 +374,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 cPlayerListCallback; typedef cItemCallback cWorldListCallback; @@ -27,6 +27,7 @@ typedef cItemCallback cWorldListCallback; +/// The root of the object hierarchy class cRoot // tolua_export { // tolua_export public: @@ -47,8 +48,8 @@ public: /// Calls the callback for each world; returns true if the callback didn't abort (return true) bool ForEachWorld(cWorldListCallback & a_Callback); // >> Exported in ManualBindings << - /// Logs chunkstats for each world and totals - void LogChunkStats(void); + /// Writes chunkstats, for each world and totals, to the output callback + void LogChunkStats(cCommandOutputCallback & a_Output); int GetPrimaryServerVersion(void) const { return m_PrimaryServerVersion; } // tolua_export void SetPrimaryServerVersion(int a_Version) { m_PrimaryServerVersion = a_Version; } // tolua_export @@ -62,8 +63,22 @@ public: cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } - /// Queues a console command for execution through the cServer class; does special handling for "stop" and "restart". - void ExecuteConsoleCommand(const AString & a_Cmd); // tolua_export + /** Queues a console command for execution through the cServer class. + The command will be executed in the tick thread + The command's output will be written to the a_Output callback + "stop" and "restart" commands have special handling. + */ + void QueueExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); + + /** Queues a console command for execution through the cServer class. + The command will be executed in the tick thread + The command's output will be sent to console + "stop" and "restart" commands have special handling. + */ + void QueueExecuteConsoleCommand(const AString & a_Cmd); // tolua_export + + /// Executes a console command through the cServer class; does special handling for "stop" and "restart". + void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); /// Kicks the user, no matter in what world they are. Used from cAuthenticator void KickUser(int a_ClientID, const AString & a_Reason); @@ -89,12 +104,27 @@ public: static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); // tolua_export private: - typedef std::map< AString, cWorld* > WorldMap; - cWorld* m_pDefaultWorld; + class cCommand + { + public: + cCommand(const AString & a_Command, cCommandOutputCallback * a_Output) : + m_Command(a_Command), + m_Output(a_Output) + { + } + + AString m_Command; + cCommandOutputCallback * m_Output; + } ; + + typedef std::map WorldMap; + typedef std::vector cCommandQueue; + + cWorld * m_pDefaultWorld; WorldMap m_WorldsByName; cCriticalSection m_CSPendingCommands; - AStringVector m_PendingCommands; + cCommandQueue m_PendingCommands; cThread * m_InputThread; @@ -131,7 +161,7 @@ private: void DoExecuteConsoleCommand(const AString & a_Cmd); static void InputThread(void* a_Params); - + static cRoot* s_Root; }; // tolua_export diff --git a/source/Server.cpp b/source/Server.cpp index 7dac3ea18..67a02f626 100644 --- a/source/Server.cpp +++ b/source/Server.cpp @@ -21,6 +21,7 @@ #include "Tracer.h" #include "WebAdmin.h" #include "Protocol/ProtocolRecognizer.h" +#include "CommandOutput.h" #include "MersenneTwister.h" @@ -425,7 +426,7 @@ bool cServer::Command(cClientHandle & a_Client, AString & a_Cmd) -void cServer::ExecuteConsoleCommand(const AString & a_Cmd) +void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output) { AStringVector split = StringSplit(a_Cmd, " "); if (split.empty()) @@ -442,7 +443,8 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd) // There is currently no way a plugin can do these (and probably won't ever be): if (split[0].compare("chunkstats") == 0) { - cRoot::Get()->LogChunkStats(); + cRoot::Get()->LogChunkStats(a_Output); + a_Output.Finished(); return; } #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) @@ -462,12 +464,14 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd) } #endif - if (cPluginManager::Get()->ExecuteConsoleCommand(split)) + if (cPluginManager::Get()->ExecuteConsoleCommand(split, a_Output)) { + a_Output.Finished(); return; } - LOG("Unknown command, type 'help' for all commands.\n"); + a_Output.Out("Unknown command, type 'help' for all commands."); + a_Output.Finished(); } diff --git a/source/Server.h b/source/Server.h index 3f7a24699..542673b49 100644 --- a/source/Server.h +++ b/source/Server.h @@ -19,9 +19,11 @@ +// fwd: class cPlayer; class cClientHandle; class cIniFile; +class cCommandOutputCallback ; typedef std::list cClientHandleList; @@ -44,7 +46,9 @@ public: // tolua_export bool Start(void); bool Command(cClientHandle & a_Client, AString & a_Cmd); - void ExecuteConsoleCommand(const AString & a_Cmd); + + /// Executes the console command, sends output through the specified callback + void ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallback & a_Output); /// Binds the built-in console commands with the plugin manager static void BindBuiltInConsoleCommands(void); diff --git a/source/StringUtils.cpp b/source/StringUtils.cpp index da809c5a4..b55ce51e6 100644 --- a/source/StringUtils.cpp +++ b/source/StringUtils.cpp @@ -569,26 +569,3 @@ AString & CreateHexDump(AString & a_Out, const void * a_Data, int a_Size, int a_ - -AString Trim(const AString & a_Text) -{ - if (a_Text.empty()) - { - return ""; - } - size_t Beginning = a_Text.find_first_not_of(" \r\n\t"); - if (Beginning == AString::npos) - { - Beginning = 0; - } - size_t End = a_Text.find_last_not_of(" \r\n\t"); - if (End == AString::npos) - { - End = a_Text.length(); - } - return a_Text.substr(Beginning, End - Beginning + 1); -} - - - - -- cgit v1.2.3