From 34b3c13404c466c8f64d198dce914a1e3fa094c2 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Fri, 15 Feb 2013 13:00:59 +0000 Subject: [PATCH] Plugins can now bind console commands FS #300 Most console commands are now implemented in the Core plugin. git-svn-id: http://mc-server.googlecode.com/svn/trunk@1214 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- MCServer/Plugins/Core/console.lua | 188 ++++++++++++++++++++++ MCServer/Plugins/Core/main.lua | 14 +- VC2008/MCServer.vcproj | 4 + source/Bindings.cpp | 209 ++++++++++++++++++++----- source/Bindings.h | 2 +- source/ManualBindings.cpp | 251 +++++++++++++++++++----------- source/Plugin.cpp | 10 ++ source/Plugin.h | 11 +- source/PluginManager.cpp | 100 ++++++++++++ source/PluginManager.h | 24 ++- source/Plugin_NewLua.cpp | 83 +++++++++- source/Plugin_NewLua.h | 8 + source/Root.cpp | 27 +++- source/Root.h | 17 +- source/Server.cpp | 104 +++---------- source/Server.h | 8 +- 16 files changed, 825 insertions(+), 235 deletions(-) create mode 100644 MCServer/Plugins/Core/console.lua diff --git a/MCServer/Plugins/Core/console.lua b/MCServer/Plugins/Core/console.lua new file mode 100644 index 000000000..398b70eb4 --- /dev/null +++ b/MCServer/Plugins/Core/console.lua @@ -0,0 +1,188 @@ + +-- console.lua + +-- Implements things related to console commands + + + + + +function InitConsoleCommands() + local PluginMgr = cPluginManager:Get(); + PluginMgr:BindConsoleCommand("help", HandleConsoleHelp, "Lists all commands"); + 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("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"); +end + + + + + +function HandleConsoleHelp(Split) + local Commands = {}; -- {index => {"Command", "HelpString"} } + local MaxLength = 0; + local AddToTable = function(Command, HelpString) + table.insert(Commands, { Command, HelpString }); + local CmdLen = Command:len(); + if (CmdLen > MaxLength) then + MaxLength = CmdLen; + end + end + + cPluginManager:Get():ForEachConsoleCommand(AddToTable); + + -- Sort the table: + local CompareCommands = function(a, b) + return a[1] < b[1]; -- compare command strings + end + table.sort(Commands, CompareCommands); + + 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]); + end + return true; +end + + + + + +function HandleConsoleNumChunks(Split) + local Output = {}; + local AddNumChunks = function(World) + Output[World:GetName()] = World:GetNumChunks(); + end; + + cRoot:Get():ForEachWorld(AddNumChunks); + + local Total = 0; + for name, num in pairs(Output) do + LOG(" " .. name .. ": " .. num .. " chunks"); + Total = Total + num; + end + LOG("Total: " .. Total .. " chunks"); + + return true; +end + + + + + +function HandleConsolePlayers(Split) + local PlayersInWorlds = {}; -- "WorldName" => [players array] + local AddToTable = function(Player) + local WorldName = Player:GetWorld():GetName(); + if (PlayersInWorlds[WorldName] == nil) then + PlayersInWorlds[WorldName] = {}; + end + table.insert(PlayersInWorlds[WorldName], Player:GetName()); + end + + cRoot:Get():ForEachPlayer(AddToTable); + + for WorldName, Players in pairs(PlayersInWorlds) do + LOG("World " .. WorldName .. ":"); + for i, PlayerName in ipairs(Players) do + LOG(" " .. PlayerName); + end + end + + return true; +end + + + + + +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; + 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; +end + + + + + +function HandleConsoleReload(Split) + Server = cRoot:Get():GetServer(); + Server:SendMessage(cChatColor.Green .. "Reloading all plugins."); + cPluginManager:Get():ReloadPlugins(); + return true; +end + + + + + +function HandleConsoleSaveAll(Split) + cRoot:Get():SaveAllChunks(); + return true; +end + + + + + +function HandleConsoleSay(Split) + table.remove(Split, 1); + local Message = ""; + for i, Text in ipairs(Split) do + Message = Message .. " " .. Text; + end + Message = Message:sub(2); -- Cut off the first space + cRoot:Get():GetServer():BroadcastChat(cChatColor.Purple .. "[SERVER] " .. Message); + return true; +end + + + + + +function HandleConsoleUnload(Split) + local UnloadChunks = function(World) + World:UnloadUnusedChunks(); + end + + LOGINFO("Num loaded chunks before: " .. cRoot:Get():GetTotalChunkCount()); + cRoot:Get():ForEachWorld(UnloadChunks); + LOGINFO("Num loaded chunks after: " .. cRoot:Get():GetTotalChunkCount()); + return true; +end + + + + + +function HandleConsole(Split) + return true; +end + + + + + +function HandleConsole(Split) + return true; +end + + + + + diff --git a/MCServer/Plugins/Core/main.lua b/MCServer/Plugins/Core/main.lua index 4682333fd..d9049bfda 100644 --- a/MCServer/Plugins/Core/main.lua +++ b/MCServer/Plugins/Core/main.lua @@ -49,6 +49,8 @@ function Initialize(Plugin) PluginManager:BindCommand("/regeneratechunk", "core.regeneratechunk", HandleRegenerateChunkCommand, " <[X] [Z]> - Regenerates a chunk, current or specified"); PluginManager:BindCommand("/viewdistance", "core.viewdistance", HandleViewDistanceCommand, " [".. cClientHandle.MIN_VIEW_DISTANCE .."-".. cClientHandle.MAX_VIEW_DISTANCE .."] - Change your view distance") + InitConsoleCommands(); + local IniFile = cIniFile("settings.ini") if ( IniFile:ReadFile() == true ) then SHOW_PLUGIN_NAMES = IniFile:GetValueB("HelpPlugin", "ShowPluginNames", true ) @@ -89,12 +91,12 @@ function Initialize(Plugin) end end - Plugin:AddWebTab( "Server Settings", HandleRequest_ServerSettings ) - Plugin:AddWebTab( "Chat", HandleRequest_Chat ) - Plugin:AddWebTab( "Playerlist", HandleRequest_PlayerList ) - Plugin:AddWebTab( "Whitelist", HandleRequest_WhiteList ) - Plugin:AddWebTab( "Permissions", HandleRequest_Permissions ) - Plugin:AddWebTab( "Manage Plugins", HandleRequest_ManagePlugins ) + Plugin:AddWebTab("Server Settings", HandleRequest_ServerSettings); + Plugin:AddWebTab("Chat", HandleRequest_Chat); + Plugin:AddWebTab("Playerlist", HandleRequest_PlayerList); + Plugin:AddWebTab("Whitelist", HandleRequest_WhiteList); + Plugin:AddWebTab("Permissions", HandleRequest_Permissions); + Plugin:AddWebTab("Manage Plugins", HandleRequest_ManagePlugins); LOG( "Initialized " .. Plugin:GetName() .. " v." .. Plugin:GetVersion() ) return true diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index 98883b991..12978e4a2 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -2198,6 +2198,10 @@ RelativePath="..\MCServer\Plugins\Core\ban.lua" > + + diff --git a/source/Bindings.cpp b/source/Bindings.cpp index 09dd7d35c..084e58de7 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 02/13/13 21:05:08. +** Generated automatically by tolua++-1.0.92 on 02/15/13 12:20:59. */ #ifndef __cplusplus @@ -148,54 +148,55 @@ static void tolua_reg_types (lua_State* tolua_S) { tolua_usertype(tolua_S,"TakeDamageInfo"); tolua_usertype(tolua_S,"cCraftingRecipe"); - tolua_usertype(tolua_S,"cPlugin_NewLua"); + tolua_usertype(tolua_S,"cPlugin"); tolua_usertype(tolua_S,"cStringMap"); tolua_usertype(tolua_S,"cBlockArea"); tolua_usertype(tolua_S,"cInventory"); tolua_usertype(tolua_S,"cRoot"); 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,"cTracer"); - tolua_usertype(tolua_S,"cChatColor"); tolua_usertype(tolua_S,"cCuboid"); + tolua_usertype(tolua_S,"cChatColor"); tolua_usertype(tolua_S,"Vector3i"); + tolua_usertype(tolua_S,"cStairs"); tolua_usertype(tolua_S,"Lua__cWebPlugin"); tolua_usertype(tolua_S,"Lua__cPawn"); - tolua_usertype(tolua_S,"cPawn"); + tolua_usertype(tolua_S,"cTCPLink"); tolua_usertype(tolua_S,"cItem"); tolua_usertype(tolua_S,"Vector3f"); tolua_usertype(tolua_S,"Lua__cTCPLink"); tolua_usertype(tolua_S,"cCraftingRecipes"); tolua_usertype(tolua_S,"Lua__cPlayer"); - tolua_usertype(tolua_S,"cTCPLink"); + tolua_usertype(tolua_S,"cGroupManager"); tolua_usertype(tolua_S,"cChestEntity"); tolua_usertype(tolua_S,"cWebAdmin"); - tolua_usertype(tolua_S,"cGroupManager"); - tolua_usertype(tolua_S,"cBlockEntity"); tolua_usertype(tolua_S,"Lua__cPickup"); - tolua_usertype(tolua_S,"Lua__cEntity"); - tolua_usertype(tolua_S,"cPluginManager"); + tolua_usertype(tolua_S,"cBlockEntity"); tolua_usertype(tolua_S,"cWebPlugin"); - tolua_usertype(tolua_S,"cServer"); - tolua_usertype(tolua_S,"cLadder"); - tolua_usertype(tolua_S,"MTRand"); - tolua_usertype(tolua_S,"HTTPFormData"); - tolua_usertype(tolua_S,"cIniFile"); - tolua_usertype(tolua_S,"cEntity"); + tolua_usertype(tolua_S,"cPluginManager"); tolua_usertype(tolua_S,"HTTPRequest"); + tolua_usertype(tolua_S,"HTTPFormData"); + tolua_usertype(tolua_S,"MTRand"); + tolua_usertype(tolua_S,"cLadder"); + tolua_usertype(tolua_S,"cPlugin_NewLua"); + tolua_usertype(tolua_S,"cEntity"); + tolua_usertype(tolua_S,"cIniFile"); + tolua_usertype(tolua_S,"cServer"); + tolua_usertype(tolua_S,"AStringVector"); tolua_usertype(tolua_S,"cVine"); tolua_usertype(tolua_S,"cPlayer"); tolua_usertype(tolua_S,"cTorch"); tolua_usertype(tolua_S,"cBlockEntityWindowOwner"); - tolua_usertype(tolua_S,"cPlugin"); - tolua_usertype(tolua_S,"Lua__cChestEntity"); tolua_usertype(tolua_S,"cWorld"); - tolua_usertype(tolua_S,"cStairs"); + tolua_usertype(tolua_S,"Lua__cChestEntity"); + tolua_usertype(tolua_S,"cPawn"); + tolua_usertype(tolua_S,"Lua__cEntity"); tolua_usertype(tolua_S,"Vector3d"); } @@ -9215,6 +9216,75 @@ static int tolua_AllToLua_cPluginManager_ForceExecuteCommand00(lua_State* tolua_ } #endif //#ifndef TOLUA_DISABLE +/* method: IsConsoleCommandBound of class cPluginManager */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cPluginManager_IsConsoleCommandBound00 +static int tolua_AllToLua_cPluginManager_IsConsoleCommandBound00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cPluginManager",0,&tolua_err) || + !tolua_iscppstring(tolua_S,2,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 AString a_Command = ((const AString) tolua_tocppstring(tolua_S,2,0)); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'IsConsoleCommandBound'", NULL); +#endif + { + bool tolua_ret = (bool) self->IsConsoleCommandBound(a_Command); + tolua_pushboolean(tolua_S,(bool)tolua_ret); + tolua_pushcppstring(tolua_S,(const char*)a_Command); + } + } + return 2; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'IsConsoleCommandBound'.",&tolua_err); + return 0; +#endif +} +#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) @@ -9427,35 +9497,37 @@ static int tolua_get_cPlugin_NewLua___cWebPlugin__(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: ServerCommand of class cServer */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_ServerCommand00 -static int tolua_AllToLua_cServer_ServerCommand00(lua_State* tolua_S) +/* method: BroadcastChat of class cServer */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cServer_BroadcastChat00 +static int tolua_AllToLua_cServer_BroadcastChat00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) || !tolua_iscppstring(tolua_S,2,0,&tolua_err) || - !tolua_isnoobj(tolua_S,3,&tolua_err) + !tolua_isusertype(tolua_S,3,"const cClientHandle",1,&tolua_err) || + !tolua_isnoobj(tolua_S,4,&tolua_err) ) goto tolua_lerror; else #endif { cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0); - const AString a_Cmd = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const AString a_Message = ((const AString) tolua_tocppstring(tolua_S,2,0)); + const cClientHandle* a_Exclude = ((const cClientHandle*) tolua_tousertype(tolua_S,3,NULL)); #ifndef TOLUA_RELEASE - if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ServerCommand'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'BroadcastChat'", NULL); #endif { - self->ServerCommand(a_Cmd); - tolua_pushcppstring(tolua_S,(const char*)a_Cmd); + self->BroadcastChat(a_Message,a_Exclude); + tolua_pushcppstring(tolua_S,(const char*)a_Message); } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ServerCommand'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'BroadcastChat'.",&tolua_err); return 0; #endif } @@ -14129,9 +14201,9 @@ static int tolua_AllToLua_cRoot_GetPluginManager00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE -/* method: ServerCommand of class cRoot */ -#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_ServerCommand00 -static int tolua_AllToLua_cRoot_ServerCommand00(lua_State* tolua_S) +/* method: ExecuteConsoleCommand of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_ExecuteConsoleCommand00 +static int tolua_AllToLua_cRoot_ExecuteConsoleCommand00(lua_State* tolua_S) { #ifndef TOLUA_RELEASE tolua_Error tolua_err; @@ -14147,17 +14219,17 @@ static int tolua_AllToLua_cRoot_ServerCommand00(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 'ServerCommand'", NULL); + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'ExecuteConsoleCommand'", NULL); #endif { - self->ServerCommand(a_Cmd); + self->ExecuteConsoleCommand(a_Cmd); tolua_pushcppstring(tolua_S,(const char*)a_Cmd); } } return 1; #ifndef TOLUA_RELEASE tolua_lerror: - tolua_error(tolua_S,"#ferror in function 'ServerCommand'.",&tolua_err); + tolua_error(tolua_S,"#ferror in function 'ExecuteConsoleCommand'.",&tolua_err); return 0; #endif } @@ -14195,6 +14267,67 @@ static int tolua_AllToLua_cRoot_GetTotalChunkCount00(lua_State* tolua_S) } #endif //#ifndef TOLUA_DISABLE +/* method: SaveAllChunks of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_SaveAllChunks00 +static int tolua_AllToLua_cRoot_SaveAllChunks00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertype(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnoobj(tolua_S,2,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + cRoot* self = (cRoot*) tolua_tousertype(tolua_S,1,0); +#ifndef TOLUA_RELEASE + if (!self) tolua_error(tolua_S,"invalid 'self' in function 'SaveAllChunks'", NULL); +#endif + { + self->SaveAllChunks(); + } + } + return 0; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'SaveAllChunks'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + +/* method: GetProtocolVersionTextFromInt of class cRoot */ +#ifndef TOLUA_DISABLE_tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00 +static int tolua_AllToLua_cRoot_GetProtocolVersionTextFromInt00(lua_State* tolua_S) +{ +#ifndef TOLUA_RELEASE + tolua_Error tolua_err; + if ( + !tolua_isusertable(tolua_S,1,"cRoot",0,&tolua_err) || + !tolua_isnumber(tolua_S,2,0,&tolua_err) || + !tolua_isnoobj(tolua_S,3,&tolua_err) + ) + goto tolua_lerror; + else +#endif + { + int a_ProtocolVersionNum = ((int) tolua_tonumber(tolua_S,2,0)); + { + AString tolua_ret = (AString) cRoot::GetProtocolVersionTextFromInt(a_ProtocolVersionNum); + tolua_pushcppstring(tolua_S,(const char*)tolua_ret); + } + } + return 1; +#ifndef TOLUA_RELEASE + tolua_lerror: + tolua_error(tolua_S,"#ferror in function 'GetProtocolVersionTextFromInt'.",&tolua_err); + return 0; +#endif +} +#endif //#ifndef TOLUA_DISABLE + /* method: delete of class cTCPLink */ #ifndef TOLUA_DISABLE_tolua_AllToLua_cTCPLink_delete00 static int tolua_AllToLua_cTCPLink_delete00(lua_State* tolua_S) @@ -22176,6 +22309,8 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_function(tolua_S,"GetCommandPermission",tolua_AllToLua_cPluginManager_GetCommandPermission00); 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"); @@ -22192,7 +22327,7 @@ TOLUA_API int tolua_AllToLua_open (lua_State* tolua_S) tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cServer","cServer","",NULL); tolua_beginmodule(tolua_S,"cServer"); - tolua_function(tolua_S,"ServerCommand",tolua_AllToLua_cServer_ServerCommand00); + tolua_function(tolua_S,"BroadcastChat",tolua_AllToLua_cServer_BroadcastChat00); tolua_function(tolua_S,"SendMessage",tolua_AllToLua_cServer_SendMessage00); tolua_endmodule(tolua_S); tolua_cclass(tolua_S,"cWorld","cWorld","",NULL); @@ -22380,8 +22515,10 @@ 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,"ServerCommand",tolua_AllToLua_cRoot_ServerCommand00); + tolua_function(tolua_S,"ExecuteConsoleCommand",tolua_AllToLua_cRoot_ExecuteConsoleCommand00); 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); tolua_endmodule(tolua_S); #ifdef __cplusplus tolua_cclass(tolua_S,"cTCPLink","cTCPLink","",tolua_collect_cTCPLink); diff --git a/source/Bindings.h b/source/Bindings.h index 8c562096a..fcaeb2242 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 02/13/13 21:05:08. +** Generated automatically by tolua++-1.0.92 on 02/15/13 12:21:00. */ /* Exported function */ diff --git a/source/ManualBindings.cpp b/source/ManualBindings.cpp index 63697f8dd..358e582ee 100644 --- a/source/ManualBindings.cpp +++ b/source/ManualBindings.cpp @@ -507,6 +507,41 @@ static int tolua_ForEach(lua_State * tolua_S) +static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) +{ + cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); + + const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); + + lua_newtable(tolua_S); + //lua_createtable(tolua_S, AllPlugins.size(), 0); + int newTable = lua_gettop(tolua_S); + int index = 1; + cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin(); + while(iter != AllPlugins.end()) + { + const cPlugin* Plugin = iter->second; + tolua_pushstring( tolua_S, iter->first.c_str() ); + if( Plugin != NULL ) + { + tolua_pushusertype( tolua_S, (void*)Plugin, "const cPlugin" ); + } + else + { + tolua_pushboolean(tolua_S, 0); + } + //lua_rawseti(tolua_S, newTable, index); + lua_rawset(tolua_S, -3); + ++iter; + ++index; + } + return 1; +} + + + + + static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) { int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ @@ -582,40 +617,80 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) -static int tolua_cPluginManager_GetAllPlugins(lua_State* tolua_S) +static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) { - cPluginManager* self = (cPluginManager*) tolua_tousertype(tolua_S,1,0); - - const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); - - lua_newtable(tolua_S); - //lua_createtable(tolua_S, AllPlugins.size(), 0); - int newTable = lua_gettop(tolua_S); - int index = 1; - cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin(); - while(iter != AllPlugins.end()) + int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ + if( NumArgs != 1) { - const cPlugin* Plugin = iter->second; - tolua_pushstring( tolua_S, iter->first.c_str() ); - if( Plugin != NULL ) - { - tolua_pushusertype( tolua_S, (void*)Plugin, "const cPlugin" ); - } - else - { - tolua_pushboolean(tolua_S, 0); - } - //lua_rawseti(tolua_S, newTable, index); - lua_rawset(tolua_S, -3); - ++iter; - ++index; + LOGWARN("Error in function call 'ForEachConsoleCommand': Requires 1 argument, got %i", NumArgs); + return 0; } + + cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, 0); + if (self == NULL) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Not called on an object instance"); + return 0; + } + + if (!lua_isfunction(tolua_S, 2)) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Expected a function for parameter #1"); + return 0; + } + + int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); + if (FuncRef == LUA_REFNIL) + { + LOGWARN("Error in function call 'ForEachConsoleCommand': Could not get function reference of parameter #1"); + return 0; + } + + class cLuaCallback : public cPluginManager::cCommandEnumCallback + { + public: + cLuaCallback(lua_State * a_LuaState, int a_FuncRef) + : LuaState( a_LuaState ) + , FuncRef( a_FuncRef ) + {} + + private: + virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override + { + lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + tolua_pushcppstring(LuaState, a_Command); + tolua_pushcppstring(LuaState, a_HelpString); + + int s = lua_pcall(LuaState, 2, 1, 0); + if (report_errors(LuaState, s)) + { + return true; /* Abort enumeration */ + } + + if (lua_isboolean(LuaState, -1)) + { + return (tolua_toboolean( LuaState, -1, 0) > 0); + } + return false; /* Continue enumeration */ + } + lua_State * LuaState; + int FuncRef; + } Callback(tolua_S, FuncRef); + + bool bRetVal = self->ForEachConsoleCommand(Callback); + + /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ + luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + + /* Push return value on stack */ + tolua_pushboolean(tolua_S, bRetVal); return 1; } + static int tolua_cPluginManager_BindCommand(lua_State * L) { // Function signature: cPluginManager:BindCommand(Command, Permission, Function, HelpString) @@ -675,6 +750,63 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) +static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) +{ + // Function signature: cPluginManager:BindConsoleCommand(Command, Function, HelpString) + + // Get the plugin identification out of LuaState: + lua_getglobal(L, LUA_PLUGIN_INSTANCE_VAR_NAME); + if (!lua_islightuserdata(L, -1)) + { + LOGERROR("cPluginManager:BindConsoleCommand() cannot get plugin instance, what have you done to my Lua state? Command-binding aborted."); + } + cPlugin_NewLua * Plugin = (cPlugin_NewLua *)lua_topointer(L, -1); + lua_pop(L, 1); + + // Read the arguments to this API call: + tolua_Error tolua_err; + if ( + !tolua_isusertype (L, 1, "cPluginManager", 0, &tolua_err) || // self + !tolua_iscppstring(L, 2, 0, &tolua_err) || // Command + !tolua_iscppstring(L, 4, 0, &tolua_err) || // HelpString + !tolua_isnoobj (L, 5, &tolua_err) + ) + { + tolua_error(L, "#ferror in function 'BindConsoleCommand'.", &tolua_err); + return 0; + } + if (!lua_isfunction(L, 3)) + { + luaL_error(L, "\"BindConsoleCommand\" function expects a function as its 2nd parameter. Command-binding aborted."); + return 0; + } + cPluginManager * self = (cPluginManager *)tolua_tousertype(L, 1, 0); + AString Command (tolua_tocppstring(L, 2, "")); + AString HelpString(tolua_tocppstring(L, 4, "")); + + // Store the function reference: + lua_pop(L, 1); // Pop the help string off the stack + int FnRef = luaL_ref(L, LUA_REGISTRYINDEX); // Store function reference + if (FnRef == LUA_REFNIL) + { + LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console Command \"%s\" not bound.", Command.c_str()); + return 0; + } + + if (!self->BindConsoleCommand(Command, Plugin, HelpString)) + { + // Refused. Possibly already bound. Error message has been given, bail out silently. + return 0; + } + + Plugin->BindConsoleCommand(Command, FnRef); + return 0; +} + + + + + static int tolua_cPlayer_GetGroups(lua_State* tolua_S) { cPlayer* self = (cPlayer*) tolua_tousertype(tolua_S,1,0); @@ -725,67 +857,6 @@ static int tolua_cPlayer_GetResolvedPermissions(lua_State* tolua_S) -/* -// TODO: rewrite this for the new API -static int tolua_cPlugin_BindCommand(lua_State* tolua_S) -{ - cPlugin* self = (cPlugin*) tolua_tousertype(tolua_S,1,0); - cPluginManager* PluginManager = cRoot::Get()->GetPluginManager(); - cLuaCommandBinder* CommandBinder = PluginManager->GetLuaCommandBinder(); - - tolua_Error tolua_err; - tolua_err.array = 0; - tolua_err.index = 0; - tolua_err.type = 0; - - std::string Permission = ""; - std::string Command = ""; - int Reference = LUA_REFNIL; - - if( tolua_isstring( tolua_S, 2, 0, &tolua_err ) && - lua_isfunction( tolua_S, 3 ) ) - { - Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - Command = ((std::string) tolua_tocppstring(tolua_S,2,0)); - } - else if( tolua_isstring( tolua_S, 2, 0, &tolua_err ) && - tolua_isstring( tolua_S, 3, 0, &tolua_err ) && - lua_isfunction( tolua_S, 4 ) ) - { - Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - Command = ((std::string) tolua_tocppstring(tolua_S,2,0)); - Permission = ((std::string) tolua_tocppstring(tolua_S,3,0)); - } - else - { - if( tolua_err.type == 0 ) - { - tolua_err.type = "function"; - } - tolua_error(tolua_S,"#ferror in function 'BindCommand'.",&tolua_err); - return 0; - } - - if( Reference != LUA_REFNIL ) - { - if( !CommandBinder->BindCommand( Command, Permission, self, tolua_S, Reference ) ) - { - luaL_unref( tolua_S, LUA_REGISTRYINDEX, Reference ); - } - } - else - { - LOGERROR("ERROR: cPlugin:BindCommand invalid function reference in 2nd argument (Command: \"%s\")", Command.c_str() ); - } - - return 0; -} -*/ - - - - - static int tolua_cPlugin_NewLua_AddWebTab(lua_State * tolua_S) { cPlugin_NewLua * self = (cPlugin_NewLua*)tolua_tousertype(tolua_S,1,0); @@ -1056,9 +1127,11 @@ void ManualBindings::Bind( lua_State* tolua_S ) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPluginManager"); - tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); - tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); - tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); + tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); + tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); + tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand); + tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); + tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPlayer"); diff --git a/source/Plugin.cpp b/source/Plugin.cpp index 968f8c1f6..3c2502617 100644 --- a/source/Plugin.cpp +++ b/source/Plugin.cpp @@ -535,6 +535,16 @@ bool cPlugin::HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) +bool cPlugin::HandleConsoleCommand(const AStringVector & a_Split) +{ + UNUSED(a_Split); + return false; +} + + + + + AString cPlugin::GetLocalDirectory(void) const { return std::string("Plugins/") + m_Directory; diff --git a/source/Plugin.h b/source/Plugin.h index e787c3239..de7113c22 100644 --- a/source/Plugin.h +++ b/source/Plugin.h @@ -85,12 +85,21 @@ public: virtual bool OnWeatherChanged (cWorld & a_World); virtual bool OnWeatherChanging (cWorld & a_World, eWeather & a_NewWeather); - /// Handles the command split into a_Split, issued by player a_Player. Command permissions have already been checked. + /** Handles the command split into a_Split, issued by player a_Player. + Command permissions have already been checked. + Returns true if command handled successfully + */ 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); + /// All bound commands are to be removed, do any language-dependent cleanup here virtual void ClearCommands(void) {} ; + /// All bound console commands are to be removed, do any language-dependent cleanup here + virtual void ClearConsoleCommands(void) {} ; + /** Called from cPluginManager::AddHook() to check if the hook can be added. Plugin API providers may check if the plugin is written correctly (has the hook handler function) Returns true if the hook can be added (handler exists) diff --git a/source/PluginManager.cpp b/source/PluginManager.cpp index c80c512a0..014cf719f 100644 --- a/source/PluginManager.cpp +++ b/source/PluginManager.cpp @@ -7,6 +7,7 @@ #include "WebAdmin.h" #include "Item.h" #include "Root.h" +#include "Server.h" #include "../iniFile/iniFile.h" #include "tolua++.h" @@ -137,6 +138,7 @@ void cPluginManager::ReloadPluginsNow(void) { LOG("Loaded %i plugin(s)", GetNumPlugins()); } + cServer::BindBuiltInConsoleCommands(); } @@ -1079,6 +1081,7 @@ void cPluginManager::RemovePlugin(cPlugin * a_Plugin) } RemovePluginCommands(a_Plugin); + RemovePluginConsoleCommands(a_Plugin); RemoveHooks(a_Plugin); if (a_Plugin != NULL) { @@ -1189,6 +1192,103 @@ bool cPluginManager::ForceExecuteCommand(cPlayer * a_Player, const AString & a_C +void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin) +{ + if (a_Plugin != NULL) + { + a_Plugin->ClearConsoleCommands(); + } + + for (CommandMap::iterator itr = m_ConsoleCommands.begin(); itr != m_ConsoleCommands.end();) + { + if (itr->second.m_Plugin == a_Plugin) + { + CommandMap::iterator EraseMe = itr; // Stupid GCC doesn't have a std::map::erase() that would return the next iterator + ++itr; + m_ConsoleCommands.erase(EraseMe); + } + else + { + ++itr; + } + } // for itr - m_Commands[] +} + + + + + +bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString) +{ + CommandMap::iterator cmd = m_ConsoleCommands.find(a_Command); + if (cmd != m_ConsoleCommands.end()) + { + LOGWARNING("Console command \"%s\" is already bound to plugin \"%s\".", a_Command.c_str(), cmd->second.m_Plugin->GetName().c_str()); + return false; + } + + m_ConsoleCommands[a_Command].m_Plugin = a_Plugin; + m_ConsoleCommands[a_Command].m_Permission = ""; + m_ConsoleCommands[a_Command].m_HelpString = a_HelpString; + return true; +} + + + + + +bool cPluginManager::ForEachConsoleCommand(cCommandEnumCallback & a_Callback) +{ + for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) + { + if (a_Callback.Command(itr->first, itr->second.m_Plugin, "", itr->second.m_HelpString)) + { + return false; + } + } // for itr - m_Commands[] + return true; +} + + + + + +bool cPluginManager::IsConsoleCommandBound(const AString & a_Command) +{ + return (m_ConsoleCommands.find(a_Command) != m_ConsoleCommands.end()); +} + + + + + +bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split) +{ + if (a_Split.empty()) + { + return false; + } + + CommandMap::iterator cmd = m_ConsoleCommands.find(a_Split[0]); + if (cmd == m_ConsoleCommands.end()) + { + // Command not found + return false; + } + + if (cmd->second.m_Plugin == NULL) + { + // This is a built-in command + return false; + } + + return cmd->second.m_Plugin->HandleConsoleCommand(a_Split); +} + + + + + bool cPluginManager::AddPlugin(cPlugin * a_Plugin) { m_Plugins[a_Plugin->GetDirectory()] = a_Plugin; diff --git a/source/PluginManager.h b/source/PluginManager.h index 86e357e91..d070bfabd 100644 --- a/source/PluginManager.h +++ b/source/PluginManager.h @@ -86,7 +86,9 @@ public: // tolua_export class cCommandEnumCallback { public: - /// Called for each command; return true to abort enumeration + /** Called for each command; return true to abort enumeration + For console commands, a_Permission is not used (set to empty string) + */ virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0; } ; @@ -170,7 +172,22 @@ public: // tolua_export /// Executes the command, as if it was requested by a_Player. Permisssions are not checked. Returns true if executed (false if not found) bool ForceExecuteCommand(cPlayer * a_Player, const AString & a_Command); // tolua_export - + + /// Removes all console command bindings that the specified plugin has made + void RemovePluginConsoleCommands(cPlugin * a_Plugin); + + /// Binds a console command to the specified plugin. Returns true if successful, false if command already bound. + bool BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param + + /// Calls a_Callback for each bound console command, returns true if all commands were enumerated + bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp + + /// 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 + private: friend class cRoot; @@ -178,7 +195,7 @@ private: { public: cPlugin * m_Plugin; - AString m_Permission; + AString m_Permission; // Not used for console commands AString m_HelpString; } ; @@ -189,6 +206,7 @@ private: PluginMap m_Plugins; HookMap m_Hooks; CommandMap m_Commands; + CommandMap m_ConsoleCommands; bool m_bReloadPlugins; diff --git a/source/Plugin_NewLua.cpp b/source/Plugin_NewLua.cpp index 249c9fafd..2b233579a 100644 --- a/source/Plugin_NewLua.cpp +++ b/source/Plugin_NewLua.cpp @@ -1273,7 +1273,7 @@ bool cPlugin_NewLua::HandleCommand(const AStringVector & a_Split, cPlayer * a_Pl CommandMap::iterator cmd = m_Commands.find(a_Split[0]); if (cmd == m_Commands.end()) { - LOGWARNING("Command handler registered in cPluginManager but not in cPlugin, wtf? Command \"%s\".", a_Split[0].c_str()); + LOGWARNING("Command handler is registered in cPluginManager but not in cPlugin, wtf? Command \"%s\".", a_Split[0].c_str()); return false; } @@ -1322,6 +1322,58 @@ bool cPlugin_NewLua::HandleCommand(const AStringVector & a_Split, cPlayer * a_Pl +bool cPlugin_NewLua::HandleConsoleCommand(const AStringVector & a_Split) +{ + 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()); + return false; + } + + cCSLock Lock(m_CriticalSection); + + // Push the function to be called: + LOGD("1. Stack size: %i", lua_gettop(m_LuaState)); + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, cmd->second); // same as lua_getref() + + // Push the split: + LOGD("2. Stack size: %i", lua_gettop(m_LuaState)); + lua_createtable(m_LuaState, a_Split.size(), 0); + int newTable = lua_gettop(m_LuaState); + int index = 1; + std::vector::const_iterator iter = a_Split.begin(), end = a_Split.end(); + while(iter != end) + { + tolua_pushstring(m_LuaState, (*iter).c_str()); + lua_rawseti(m_LuaState, newTable, index); + ++iter; + ++index; + } + LOGD("3. Stack size: %i", lua_gettop(m_LuaState)); + + // Call function: + LOGD("Calling bound function! :D"); + int s = lua_pcall(m_LuaState, 1, 1, 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 + LOGD("ok. Stack size: %i", lua_gettop(m_LuaState)); + + return RetVal; +} + + + + + void cPlugin_NewLua::ClearCommands(void) { cCSLock Lock(m_CriticalSection); @@ -1341,6 +1393,25 @@ void cPlugin_NewLua::ClearCommands(void) +void cPlugin_NewLua::ClearConsoleCommands(void) +{ + cCSLock Lock(m_CriticalSection); + + // Unreference the bound functions so that Lua can GC them + if (m_LuaState != NULL) + { + for (CommandMap::iterator itr = m_ConsoleCommands.begin(), end = m_ConsoleCommands.end(); itr != end; ++itr) + { + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); + } + } + m_ConsoleCommands.clear(); +} + + + + + bool cPlugin_NewLua::CanAddHook(cPluginManager::PluginHook a_Hook) { const char * FnName = GetHookFnName(a_Hook); @@ -1526,6 +1597,16 @@ void cPlugin_NewLua::BindCommand(const AString & a_Command, int a_FnRef) +void cPlugin_NewLua::BindConsoleCommand(const AString & a_Command, int a_FnRef) +{ + ASSERT(m_ConsoleCommands.find(a_Command) == m_ConsoleCommands.end()); + m_ConsoleCommands[a_Command] = a_FnRef; +} + + + + + // Helper functions bool cPlugin_NewLua::PushFunction(const char * a_FunctionName, bool a_bLogError /* = true */) { diff --git a/source/Plugin_NewLua.h b/source/Plugin_NewLua.h index 529f0e0eb..ab1907a41 100644 --- a/source/Plugin_NewLua.h +++ b/source/Plugin_NewLua.h @@ -72,8 +72,12 @@ public: virtual bool HandleCommand(const AStringVector & a_Split, cPlayer * a_Player) override; + virtual bool HandleConsoleCommand(const AStringVector & a_Split) override; + virtual void ClearCommands(void) override; + virtual void ClearConsoleCommands(void) override; + virtual bool CanAddHook(cPluginManager::PluginHook a_Hook) override; // cWebPlugin override @@ -86,6 +90,9 @@ public: /// Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. void BindCommand(const AString & a_Command, int a_FnRef); + /// Binds the console command to call the function specified by a Lua function reference. Simply adds to CommandMap. + void BindConsoleCommand(const AString & a_Command, int a_FnRef); + lua_State* GetLuaState() { return m_LuaState; } cCriticalSection & GetCriticalSection() { return m_CriticalSection; } @@ -98,6 +105,7 @@ protected: typedef std::map CommandMap; CommandMap m_Commands; + CommandMap m_ConsoleCommands; bool PushFunction(const char * a_FunctionName, bool a_bLogError = true); bool CallFunction(int a_NumArgs, int a_NumResults, const char * a_FunctionName ); // a_FunctionName is only used for error messages, nothing else diff --git a/source/Root.cpp b/source/Root.cpp index 72003ca72..41788ff8d 100644 --- a/source/Root.cpp +++ b/source/Root.cpp @@ -73,7 +73,7 @@ void cRoot::InputThread(void * a_Params) { std::string Command; std::getline(std::cin, Command); - self.ServerCommand(Command); + self.ExecuteConsoleCommand(Command); } } @@ -149,7 +149,7 @@ void cRoot::Start(void) LoadWorlds(); LOG("Loading plugin manager..."); - m_PluginManager = new cPluginManager(); // This should be last + m_PluginManager = new cPluginManager(); m_PluginManager->ReloadPluginsNow(); LOG("Loading MonsterConfig..."); @@ -348,11 +348,11 @@ bool cRoot::ForEachWorld(cWorldListCallback & a_Callback) -void cRoot::TickWorlds( float a_Dt ) +void cRoot::TickWorlds(float a_Dt) { - for( WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr ) + for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr) { - itr->second->Tick( a_Dt ); + itr->second->Tick(a_Dt); } } @@ -360,10 +360,12 @@ void cRoot::TickWorlds( float a_Dt ) -void cRoot::ServerCommand(const AString & a_Cmd) +void cRoot::ExecuteConsoleCommand(const AString & a_Cmd) { - LOG("Server console command: \"%s\"", a_Cmd.c_str()); - m_Server->ServerCommand(a_Cmd); + LOG("Executing console command: \"%s\"", a_Cmd.c_str()); + m_Server->ExecuteConsoleCommand(a_Cmd); + + // Some commands are built-in: if (a_Cmd == "stop") { m_bStop = true; @@ -491,6 +493,15 @@ bool cRoot::FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallbac +AString cRoot::GetProtocolVersionTextFromInt(int a_ProtocolVersion) +{ + return cProtocolRecognizer::GetVersionTextFromInt(a_ProtocolVersion); +} + + + + + void cRoot::LogChunkStats(void) { int SumNumValid = 0; diff --git a/source/Root.h b/source/Root.h index 7cc33d6c1..d3a06354f 100644 --- a/source/Root.h +++ b/source/Root.h @@ -62,18 +62,22 @@ public: cPluginManager * GetPluginManager (void) { return m_PluginManager; } // tolua_export cAuthenticator & GetAuthenticator (void) { return m_Authenticator; } - void ServerCommand(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); // tolua_export - void KickUser(int a_ClientID, const AString & a_Reason); // Kicks the user, no matter in what world they are. Used from cAuthenticator - void AuthenticateUser(int a_ClientID); // Called by cAuthenticator to auth the specified user + /// Kicks the user, no matter in what world they are. Used from cAuthenticator + void KickUser(int a_ClientID, const AString & a_Reason); + + /// Called by cAuthenticator to auth the specified user + void AuthenticateUser(int a_ClientID); - void TickWorlds( float a_Dt ); + void TickWorlds(float a_Dt); /// Returns the number of chunks loaded int GetTotalChunkCount(void); // tolua_export /// Saves all chunks in all worlds - void SaveAllChunks(void); + void SaveAllChunks(void); // tolua_export /// Calls the callback for each player in all worlds bool ForEachPlayer(cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << @@ -81,6 +85,9 @@ public: /// Finds a player from a partial or complete player name and calls the callback - case-insensitive bool FindAndDoWithPlayer(const AString & a_PlayerName, cPlayerListCallback & a_Callback); // >> EXPORTED IN MANUALBINDINGS << + /// Returns the textual description of the protocol version: 49 -> "1.4.4". Provided specifically for Lua API + static AString GetProtocolVersionTextFromInt(int a_ProtocolVersionNum); // tolua_export + private: void LoadGlobalSettings(); diff --git a/source/Server.cpp b/source/Server.cpp index 39d790aa1..7a41352ab 100644 --- a/source/Server.cpp +++ b/source/Server.cpp @@ -460,7 +460,7 @@ bool cServer::Command(cClientHandle & a_Client, const AString & a_Cmd) -void cServer::ServerCommand(const AString & a_Cmd) +void cServer::ExecuteConsoleCommand(const AString & a_Cmd) { AStringVector split = StringSplit(a_Cmd, " "); if (split.empty()) @@ -468,69 +468,18 @@ void cServer::ServerCommand(const AString & a_Cmd) return; } - if (split[0].compare( "help" ) == 0) - { - printf("================== ALL COMMANDS ===================\n"); - printf("help - Shows this message\n"); - printf("save-all - Saves all loaded chunks to disk\n"); - printf("list - Lists all players currently in server\n"); - printf("unload - Unloads all unused chunks\n"); - printf("numchunks - Shows number of chunks currently loaded\n"); - printf("chunkstats - Shows chunks statistics\n"); - printf("say - Sends a chat message to all players\n"); - printf("restart - Kicks all clients, and saves everything\n"); - printf(" and clears memory\n"); - printf("stop - Saves everything and closes server\n"); - printf("primaryserverversion - Gets or sets server version reported to 1.4+ clients\n"); - printf("===================================================\n"); - return; - } + // Special handling: "stop" and "restart" are built in if ((split[0].compare("stop") == 0) || (split[0].compare("restart") == 0)) { return; } - if (split[0].compare("save-all") == 0) - { - cRoot::Get()->SaveAllChunks(); - return; - } - if (split[0].compare("unload") == 0) - { - LOG("Num loaded chunks before: %i", cRoot::Get()->GetTotalChunkCount() ); - cRoot::Get()->GetDefaultWorld()->UnloadUnusedChunks(); // TODO: Iterate through ALL worlds - LOG("Num loaded chunks after: %i", cRoot::Get()->GetTotalChunkCount() ); - return; - } - if (split[0].compare("list") == 0) - { - class cPlayerLogger : public cPlayerListCallback - { - virtual bool Item(cPlayer * a_Player) override - { - LOG("\t%s @ %s", a_Player->GetName().c_str(), a_Player->GetClientHandle()->GetIPString().c_str()); - return false; - } - } Logger; - cRoot::Get()->ForEachPlayer(Logger); - return; - } - if (split[0].compare("numchunks") == 0) - { - LOG("Num loaded chunks: %i", cRoot::Get()->GetTotalChunkCount() ); - return; - } + + // 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(); return; } - - if (split[0].compare("monsters") == 0) - { - // TODO: cWorld::ListMonsters(); - return; - } - #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) if (split[0].compare("dumpmem") == 0) { @@ -540,38 +489,27 @@ void cServer::ServerCommand(const AString & a_Cmd) } #endif - if (split[0].compare("primaryserverversion") == 0) + if (cPluginManager::Get()->ExecuteConsoleCommand(split)) { - if (split.size() > 1) - { - int Version = atol(split[1].c_str()); - if (Version == 0) - { - LOGWARNING("Cannot parse version \"%s\". Not setting anything."); - return; - } - cRoot::Get()->SetPrimaryServerVersion(Version); - } - LOGINFO("Primary server version: %d (%s)", - cRoot::Get()->m_PrimaryServerVersion, - cProtocolRecognizer::GetVersionTextFromInt(cRoot::Get()->m_PrimaryServerVersion).c_str() - ); return; } - if (split.size() > 1) - { - if (split[0].compare("say") == 0) - { - AString ToSay = a_Cmd.substr(a_Cmd.find_first_of("say") + 4); - AString Message = cChatColor::Purple + "[SERVER] " + ToSay; - LOG("[SERVER]: %s", ToSay.c_str()); - BroadcastChat(Message); - return; - } - } - printf("Unknown command, type 'help' for all commands.\n"); - // LOG("You didn't enter anything? (%s)", a_Cmd.c_str() ); + LOG("Unknown command, type 'help' for all commands.\n"); +} + + + + + +void cServer::BindBuiltInConsoleCommands(void) +{ + cPluginManager * PlgMgr = cPluginManager::Get(); + PlgMgr->BindConsoleCommand("restart", NULL, "Restarts the server cleanly"); + PlgMgr->BindConsoleCommand("stop", NULL, "Stops the server cleanly"); + PlgMgr->BindConsoleCommand("chunkstats", NULL, "Displays detailed chunk memory statistics"); + #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) + PlgMgr->BindConsoleCommand("dumpmem", NULL, "Dumps all used memory blocks together with their callstacks into memdump.xml"); + #endif } diff --git a/source/Server.h b/source/Server.h index 0910e7c28..707f91261 100644 --- a/source/Server.h +++ b/source/Server.h @@ -38,14 +38,18 @@ public: // tolua_export bool IsConnected(){return m_bIsConnected;} // returns connection status void StartListenClient(); // Listen to client - void BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude = NULL); + void BroadcastChat(const AString & a_Message, const cClientHandle * a_Exclude = NULL); // tolua_export bool Tick(float a_Dt); void StartListenThread(); bool Command(cClientHandle & a_Client, const AString & a_Cmd); - void ServerCommand(const AString & a_Cmd); // tolua_export + void ExecuteConsoleCommand(const AString & a_Cmd); + + /// Binds the built-in console commands with the plugin manager + static void BindBuiltInConsoleCommands(void); + void Shutdown(); void SendMessage(const AString & a_Message, cPlayer * a_Player = NULL, bool a_bExclude = false ); // tolua_export