diff options
author | Mattes D <github@xoft.cz> | 2016-06-30 10:42:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2016-06-30 10:42:58 +0200 |
commit | 9af664dc739405726dd52f4fe7dce7b42f5ce18e (patch) | |
tree | 59bae2de637b5f3c629db8e55fb44a3106266709 | |
parent | Self tests (#3242) (diff) | |
parent | Converted cLuaState::cCallbackPtr into a UniquePtr. (diff) | |
download | cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.gz cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.bz2 cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.lz cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.xz cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.tar.zst cuberite-9af664dc739405726dd52f4fe7dce7b42f5ce18e.zip |
30 files changed, 1614 insertions, 2282 deletions
diff --git a/Server/Plugins/APIDump/Classes/Plugins.lua b/Server/Plugins/APIDump/Classes/Plugins.lua index d5ac5fdbd..c86adc087 100644 --- a/Server/Plugins/APIDump/Classes/Plugins.lua +++ b/Server/Plugins/APIDump/Classes/Plugins.lua @@ -2,7 +2,7 @@ return { cPlugin = { - Desc = [[cPlugin describes a Lua plugin. This page is dedicated to new-style plugins and contain their functions. Each plugin has its own Plugin object. + Desc = [[cPlugin describes a Lua plugin. This page is dedicated to new-style plugins and contain their functions. Each plugin has its own cPlugin object. ]], Functions = { @@ -22,10 +22,10 @@ return cPluginLua = { - Desc = "", + Desc = "(<b>OBSOLETE</b>) This class is no longer useful in the API and will be removed as soon as all core plugins are migrated away from it. The {{cPlugin}} class serves as the main plugin instance's interface.", Functions = { - AddWebTab = { Params = "", Return = "", Notes = "Adds a new webadmin tab" }, + AddWebTab = { Params = "Title, HandlerFn", Return = "", Notes = "<b>OBSOLETE</b> - Use {{cWebAdmin}}:AddWebTab() instead." }, }, Inherits = "cPlugin", }, -- cPluginLua @@ -81,6 +81,7 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); GetNumLoadedPlugins = { Params = "", Return = "number", Notes = "Returns the number of loaded plugins (psLoaded only)" }, GetNumPlugins = { Params = "", Return = "number", Notes = "Returns the number of plugins, including the disabled, errored, unloaded and not-found ones" }, GetPlugin = { Params = "PluginName", Return = "{{cPlugin}}", Notes = "(<b>DEPRECATED, UNSAFE</b>) Returns a plugin handle of the specified plugin, or nil if such plugin is not loaded. Note thatdue to multithreading the handle is not guaranteed to be safe for use when stored - a single-plugin reload may have been triggered in the mean time for the requested plugin." }, + GetPluginFolderName = { Params = "PluginName", Return = "string", Notes = "Returns the name of the folder from which the plugin was loaded (without the \"Plugins\" part). Used as a plugin's display name." }, GetPluginsPath = { Params = "", Return = "string", Notes = "Returns the path where the individual plugin folders are located. Doesn't include the path separator at the end of the returned string." }, IsCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if in-game Command is already bound (by any plugin)" }, IsConsoleCommandBound = { Params = "Command", Return = "bool", Notes = "Returns true if console Command is already bound (by any plugin)" }, diff --git a/Server/Plugins/APIDump/Classes/WebAdmin.lua b/Server/Plugins/APIDump/Classes/WebAdmin.lua index 808335aea..a9d92b069 100644 --- a/Server/Plugins/APIDump/Classes/WebAdmin.lua +++ b/Server/Plugins/APIDump/Classes/WebAdmin.lua @@ -5,7 +5,15 @@ return Desc = "", Functions = { + AddWebTab = { Params = "Title, UrlPath, HandlerFn", Return = "", Notes = "(STATIC) Adds a new web tab to webadmin. The tab uses \"Title\" as its display string and is identified in the URL using the UrlPath (https://server.domain.com/webadmin/{PluginName}/{UrlPath}). The HandlerFn is the callback function that is called when the admin accesses the page, it has the following signature:<br/><pre class=\"prettyprint lang-lua\">function ({{a_Request|HTTPRequest}}, a_UrlPath)<br/> return Content, ContentType<br/>end</pre> URLPath must not contain a '/', the recommendation is to use only 7-bit-clean ASCII character set." }, + GetAllWebTabs = { Params = "", Return = "array-table", Notes = "(STATIC) Returns an array-table with each item describing a web tab, for all web tabs registered in the WebAdmin, for all plugins. The returned table has the following format:<br/><pre class=\"prettyprint lang-lua\">{<br/> {<br/> PluginName = \"Plugin's API name\",<br/> UrlPath = \"UrlPath given to AddWebTab\",<br/> Title = \"Title given to AddWebTab\",<br/> },<br/> ...<br/>}"}, + GetBaseURL = { Params = "URL", Return = "string", Notes = "(STATIC) Returns the string that is the path of the base webadmin (\"../../../webadmin\") relative to the given URL." }, + GetContentTypeFromFileExt = { Params = "FileExt", Return = "string", Notes = "(STATIC) Returns the content-type that should be used for files with the specified extension (without the dot), such as \"text/plain\" for the \"txt\" extension. If the extension is not known, returns an empty string." }, GetHTMLEscapedString = { Params = "string", Return = "string", Notes = "(STATIC) Gets the HTML-escaped representation of a requested string. This is useful for user input and game data that is not guaranteed to be escaped already." }, + GetPage = { Params = "{{Request|HTTPRequest}}", Return = "table", Notes = "(STATIC) Returns the (inner HTML) page contents for the specified request. Calls the appropriate WebTab handler registered via AddWebTab() and returns the information from that plugin wrapped in a table with the following structure:<br/><pre class=\"prettyprint lang-lua\">{<br/> Content = \"\", -- Content returned by the plugin<br/> ContentType = \"\", -- Content type returned by the plugin, or \"text/html\" if none returned<br/> UrlPath = \"\", -- UrlPath decoded from the request<br/> TabTitle = \"\", -- Title of the tab that handled the request, as given to AddWebTab()<br/> PluginName = \"\", -- API name of the plugin that handled the request<br/> PluginFolder = \"\", -- Folder name (= display name) of the plugin that handled the request<br/>}</pre>This function is mainly used in the webadmin template file." }, + GetPorts = { Params = "", Return = "string", Notes = "Returns a comma-separated list of ports on which the webadmin is configured to listen. Note that this list does include ports that may currently be unavailable (another server was already listening on them prior to launching Cuberite)." }, + GetURLEncodedString = { Params = "string", Return = "string", Notes = "(STATIC) Returns the string given to it escaped by URL encoding, which makes the string suitable for transmission in an URL. Invalid characters are turned into \"%xy\" values." }, + Reload = { Params = "", Return = "", Notes = "Reloads the webadmin's config - the allowed logins, the template script and the login page. Note that reloading will not change the \"enabled\" state of the server, and it will not update listening ports. Existing WebTabs will be kept registered even after the reload." }, }, }, -- cWebAdmin diff --git a/Server/Plugins/Debuggers/Debuggers.lua b/Server/Plugins/Debuggers/Debuggers.lua index 362dc4dd9..02510c866 100644 --- a/Server/Plugins/Debuggers/Debuggers.lua +++ b/Server/Plugins/Debuggers/Debuggers.lua @@ -1876,8 +1876,8 @@ end function HandleConsoleSchedule(a_Split) local prev = os.clock() - LOG("Scheduling a task for 2 seconds in the future (current os.clock is " .. prev .. ")") - cRoot:Get():GetDefaultWorld():ScheduleTask(40, + LOG("Scheduling a task for 5 seconds in the future (current os.clock is " .. prev .. ")") + cRoot:Get():GetDefaultWorld():ScheduleTask(5 * 20, function () local current = os.clock() local diff = current - prev diff --git a/Server/webadmin/template.lua b/Server/webadmin/template.lua index d80ca5f74..168f87993 100644 --- a/Server/webadmin/template.lua +++ b/Server/webadmin/template.lua @@ -20,7 +20,7 @@ end -function GetDefaultPage() +local function GetDefaultPage() local PM = cRoot:Get():GetPluginManager() local SubTitle = "Current Game" @@ -55,30 +55,31 @@ end function ShowPage(WebAdmin, TemplateRequest) SiteContent = {} - local BaseURL = WebAdmin:GetBaseURL(TemplateRequest.Request.Path) + local BaseURL = cWebAdmin:GetBaseURL(TemplateRequest.Request.Path) local Title = "Cuberite WebAdmin" local NumPlayers = cRoot:Get():GetServer():GetNumPlayers() local MemoryUsageKiB = cRoot:GetPhysicalRAMUsage() local NumChunks = cRoot:Get():GetTotalChunkCount() - local PluginPage = WebAdmin:GetPage(TemplateRequest.Request) + local PluginPage = cWebAdmin:GetPage(TemplateRequest.Request) local PageContent = PluginPage.Content - local SubTitle = PluginPage.PluginName - if (PluginPage.TabName ~= "") then - SubTitle = PluginPage.PluginName .. " - " .. PluginPage.TabName + local SubTitle = PluginPage.PluginFolder + if (PluginPage.UrlPath ~= "") then + SubTitle = PluginPage.PluginFolder .. " - " .. PluginPage.TabTitle end if (PageContent == "") then PageContent, SubTitle = GetDefaultPage() end + --[[ + -- 2016-01-15 Mattes: This wasn't used anywhere in the code, no idea what it was supposed to do local reqParamsClass = "" - - for key,value in pairs(TemplateRequest.Request.Params) do + for key, value in pairs(TemplateRequest.Request.Params) do reqParamsClass = reqParamsClass .. " param-" .. string.lower(string.gsub(key, "[^a-zA-Z0-9]+", "-") .. "-" .. string.gsub(value, "[^a-zA-Z0-9]+", "-")) end - if (string.gsub(reqParamsClass, "%s", "") == "") then reqParamsClass = " no-param" end + --]] Output([[ <!-- Copyright Justin S and Cuberite Team, licensed under CC-BY-SA 3.0 --> @@ -133,20 +134,39 @@ function ShowPage(WebAdmin, TemplateRequest) <td class="trow1 smalltext"> ]]) - - local AllPlugins = WebAdmin:GetPlugins() - for key,value in pairs(AllPlugins) do - local PluginWebTitle = value:GetWebTitle() - local TabNames = value:GetTabNames() - if (GetTableSize(TabNames) > 0) then - Output("<div><a class='usercp_nav_item usercp_nav_pmfolder' style='text-decoration:none;'><b>"..PluginWebTitle.."</b></a></div>\n"); - - for webname,prettyname in pairs(TabNames) do - Output("<div><a href='" .. BaseURL .. PluginWebTitle .. "/" .. webname .. "' class='usercp_nav_item usercp_nav_sub_pmfolder'>" .. prettyname .. "</a></div>\n") + -- Get all tabs: + local perPluginTabs = {} + for _, tab in ipairs(cWebAdmin:GetAllWebTabs()) do + local pluginTabs = perPluginTabs[tab.PluginName] or {}; + perPluginTabs[tab.PluginName] = pluginTabs + table.insert(pluginTabs, tab) + end + + -- Sort by plugin: + local pluginNames = {} + for pluginName, pluginTabs in pairs(perPluginTabs) do + table.insert(pluginNames, pluginName) + end + table.sort(pluginNames) + + -- Output by plugin, then alphabetically: + for _, pluginName in ipairs(pluginNames) do + local pluginTabs = perPluginTabs[pluginName] + table.sort(pluginTabs, + function(a_Tab1, a_Tab2) + return ((a_Tab1.Title or "") < (a_Tab2.Title or "")) end - - Output("<br>\n"); + ) + + -- Translate the plugin name into the folder name (-> title) + local pluginWebTitle = cPluginManager:Get():GetPluginFolderName(pluginName) or pluginName + Output("<div><a class='usercp_nav_item usercp_nav_pmfolder' style='text-decoration:none;'><b>" .. pluginWebTitle .. "</b></a></div>\n"); + + -- Output each tab: + for _, tab in pairs(pluginTabs) do + Output("<div><a href='" .. BaseURL .. pluginName .. "/" .. tab.UrlPath .. "' class='usercp_nav_item usercp_nav_sub_pmfolder'>" .. tab.Title .. "</a></div>\n") end + Output("<br>\n"); end diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index 991ed0ddd..6ca9c8658 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -38,7 +38,6 @@ $cfile "LuaFunctions.h" $cfile "PluginManager.h" $cfile "Plugin.h" $cfile "PluginLua.h" -$cfile "WebPlugin.h" $cfile "LuaWindow.h" $cfile "../BlockID.h" diff --git a/src/Bindings/CMakeLists.txt b/src/Bindings/CMakeLists.txt index 10cda1efb..4f25f2cf4 100644 --- a/src/Bindings/CMakeLists.txt +++ b/src/Bindings/CMakeLists.txt @@ -23,7 +23,6 @@ SET (SRCS Plugin.cpp PluginLua.cpp PluginManager.cpp - WebPlugin.cpp ) SET (HDRS @@ -44,7 +43,6 @@ SET (HDRS Plugin.h PluginLua.h PluginManager.h - WebPlugin.h tolua++.h ) @@ -66,7 +64,6 @@ set(BINDING_DEPENDENCIES ../Bindings/Plugin.h ../Bindings/PluginLua.h ../Bindings/PluginManager.h - ../Bindings/WebPlugin.h ../BiomeDef.h ../BlockArea.h ../BlockEntities/BeaconEntity.h diff --git a/src/Bindings/LuaChunkStay.cpp b/src/Bindings/LuaChunkStay.cpp index d8d7da5d0..145a9ee1b 100644 --- a/src/Bindings/LuaChunkStay.cpp +++ b/src/Bindings/LuaChunkStay.cpp @@ -113,12 +113,10 @@ void cLuaChunkStay::AddChunkCoord(cLuaState & L, int a_Index) -void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos) +void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, cLuaState::cCallbackPtr a_OnChunkAvailable, cLuaState::cCallbackPtr a_OnAllChunksAvailable) { - // Get the references to the callback functions: - m_LuaState = &m_Plugin.GetLuaState(); - m_OnChunkAvailable.RefStack(*m_LuaState, a_OnChunkAvailableStackPos); - m_OnAllChunksAvailable.RefStack(*m_LuaState, a_OnAllChunksAvailableStackPos); + m_OnChunkAvailable = std::move(a_OnChunkAvailable); + m_OnAllChunksAvailable = std::move(a_OnAllChunksAvailable); // Enable the ChunkStay: super::Enable(a_ChunkMap); @@ -130,10 +128,9 @@ void cLuaChunkStay::Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPo void cLuaChunkStay::OnChunkAvailable(int a_ChunkX, int a_ChunkZ) { - if (m_OnChunkAvailable.IsValid()) + if (m_OnChunkAvailable != nullptr) { - cPluginLua::cOperation Op(m_Plugin); - Op().Call(static_cast<int>(m_OnChunkAvailable), a_ChunkX, a_ChunkZ); + m_OnChunkAvailable->Call(a_ChunkX, a_ChunkZ); } } @@ -143,15 +140,14 @@ void cLuaChunkStay::OnChunkAvailable(int a_ChunkX, int a_ChunkZ) bool cLuaChunkStay::OnAllChunksAvailable(void) { - if (m_OnAllChunksAvailable.IsValid()) + if (m_OnAllChunksAvailable != nullptr) { // Call the callback: - cPluginLua::cOperation Op(m_Plugin); - Op().Call(static_cast<int>(m_OnAllChunksAvailable)); + m_OnAllChunksAvailable->Call(); // Remove the callback references - they won't be needed anymore - m_OnChunkAvailable.UnRef(); - m_OnAllChunksAvailable.UnRef(); + m_OnChunkAvailable.reset(); + m_OnAllChunksAvailable.reset(); } // Disable the ChunkStay by returning true diff --git a/src/Bindings/LuaChunkStay.h b/src/Bindings/LuaChunkStay.h index a455d2502..51356d5b7 100644 --- a/src/Bindings/LuaChunkStay.h +++ b/src/Bindings/LuaChunkStay.h @@ -39,7 +39,7 @@ public: bool AddChunks(int a_ChunkCoordTableStackPos); /** Enables the ChunkStay for the specified chunkmap, with the specified Lua callbacks. */ - void Enable(cChunkMap & a_ChunkMap, int a_OnChunkAvailableStackPos, int a_OnAllChunksAvailableStackPos); + void Enable(cChunkMap & a_ChunkMap, cLuaState::cCallbackPtr a_OnChunkAvailable, cLuaState::cCallbackPtr a_OnAllChunksAvailable); protected: /** The plugin which has created the ChunkStay, via cWorld:ChunkStay() binding method. */ @@ -49,10 +49,10 @@ protected: cLuaState * m_LuaState; /** The Lua function to call in OnChunkAvailable. Only valid when enabled. */ - cLuaState::cRef m_OnChunkAvailable; + cLuaState::cCallbackPtr m_OnChunkAvailable; /** The Lua function to call in OnAllChunksAvailable. Only valid when enabled. */ - cLuaState::cRef m_OnAllChunksAvailable; + cLuaState::cCallbackPtr m_OnAllChunksAvailable; // cChunkStay overrides: diff --git a/src/Bindings/LuaNameLookup.cpp b/src/Bindings/LuaNameLookup.cpp index e52d8dbdc..3cbdbb5cf 100644 --- a/src/Bindings/LuaNameLookup.cpp +++ b/src/Bindings/LuaNameLookup.cpp @@ -12,7 +12,7 @@ cLuaNameLookup::cLuaNameLookup(const AString & a_Query, cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), - m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos), + m_Callbacks(cPluginLua::cOperation(a_Plugin)(), a_CallbacksTableStackPos), m_Query(a_Query) { } diff --git a/src/Bindings/LuaServerHandle.cpp b/src/Bindings/LuaServerHandle.cpp index a84f894b5..9cc8ad350 100644 --- a/src/Bindings/LuaServerHandle.cpp +++ b/src/Bindings/LuaServerHandle.cpp @@ -14,7 +14,7 @@ cLuaServerHandle::cLuaServerHandle(UInt16 a_Port, cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), - m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos), + m_Callbacks(cPluginLua::cOperation(a_Plugin)(), a_CallbacksTableStackPos), m_Port(a_Port) { } diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index f11e74e75..5e6c24365 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -20,6 +20,10 @@ extern "C" #include "../Entities/Entity.h" #include "../BlockEntities/BlockEntity.h" + + + + // fwd: "SQLite/lsqlite3.c" extern "C" { @@ -39,6 +43,10 @@ extern "C" const cLuaState::cRet cLuaState::Return = {}; +/** Each Lua state stores a pointer to its creating cLuaState in Lua globals, under this name. +This way any cLuaState can reference the main cLuaState's TrackedCallbacks, mutex etc. */ +static const char * g_CanonLuaStateGlobalName = "_CuberiteInternal_CanonLuaState"; + @@ -114,6 +122,135 @@ cLuaStateTracker & cLuaStateTracker::Get(void) //////////////////////////////////////////////////////////////////////////////// +// cLuaState::cCallback: + +cLuaState::cCallback::cCallback(void): + m_CS(nullptr) +{ +} + + + + + +bool cLuaState::cCallback::RefStack(cLuaState & a_LuaState, int a_StackPos) +{ + // Check if the stack contains a function: + if (!lua_isfunction(a_LuaState, a_StackPos)) + { + return false; + } + + // Clear any previous callback: + Clear(); + + // Add self to LuaState's callback-tracking: + auto canonState = a_LuaState.QueryCanonLuaState(); + canonState->TrackCallback(*this); + + // Store the new callback: + m_CS = &(canonState->m_CS); + m_Ref.RefStack(*canonState, a_StackPos); + return true; +} + + + + + +void cLuaState::cCallback::Clear(void) +{ + // Free the callback reference: + lua_State * luaState = nullptr; + { + auto cs = m_CS; + if (cs != nullptr) + { + cCSLock Lock(*cs); + if (!m_Ref.IsValid()) + { + return; + } + luaState = m_Ref.GetLuaState(); + m_Ref.UnRef(); + } + } + + // Remove from LuaState's callback-tracking: + if (luaState == nullptr) + { + return; + } + cLuaState(luaState).UntrackCallback(*this); +} + + + + + +bool cLuaState::cCallback::IsValid(void) +{ + auto cs = m_CS; + if (cs == nullptr) + { + return false; + } + cCSLock lock(*cs); + return m_Ref.IsValid(); +} + + + + + +bool cLuaState::cCallback::IsSameLuaState(cLuaState & a_LuaState) +{ + auto cs = m_CS; + if (cs == nullptr) + { + return false; + } + cCSLock lock(*cs); + if (!m_Ref.IsValid()) + { + return false; + } + auto canonState = a_LuaState.QueryCanonLuaState(); + if (canonState == nullptr) + { + return false; + } + return (m_Ref.GetLuaState() == static_cast<lua_State *>(*canonState)); +} + + + + + +void cLuaState::cCallback::Invalidate(void) +{ + auto cs = m_CS; + if (cs == nullptr) + { + // Already invalid + return; + } + cCSLock Lock(*cs); + if (!m_Ref.IsValid()) + { + LOGD("%s: Inconsistent callback at %p, has a CS but an invalid Ref. This should not happen", + __FUNCTION__, reinterpret_cast<void *>(this) + ); + return; + } + m_Ref.UnRef(); +} + + + + + +//////////////////////////////////////////////////////////////////////////////// // cLuaState: cLuaState::cLuaState(const AString & a_SubsystemName) : @@ -170,6 +307,10 @@ void cLuaState::Create(void) luaL_openlibs(m_LuaState); m_IsOwned = true; cLuaStateTracker::Add(*this); + + // Add the CanonLuaState value into the Lua state, so that we can get it from anywhere: + lua_pushlightuserdata(m_LuaState, reinterpret_cast<void *>(this)); + lua_setglobal(m_LuaState, g_CanonLuaStateGlobalName); } @@ -206,6 +347,16 @@ void cLuaState::Close(void) Detach(); return; } + + // Invalidate all callbacks: + { + cCSLock Lock(m_CSTrackedCallbacks); + for (auto & c: m_TrackedCallbacks) + { + c->Invalidate(); + } + } + cLuaStateTracker::Del(*this); lua_close(m_LuaState); m_LuaState = nullptr; @@ -402,7 +553,7 @@ bool cLuaState::PushFunction(const char * a_FunctionName) -bool cLuaState::PushFunction(int a_FnRef) +bool cLuaState::PushFunction(const cRef & a_FnRef) { ASSERT(IsValid()); ASSERT(m_NumCurrentFunctionArgs == -1); // If not, there's already something pushed onto the stack @@ -410,7 +561,7 @@ bool cLuaState::PushFunction(int a_FnRef) // Push the error handler for lua_pcall() lua_pushcfunction(m_LuaState, &ReportFnCallErrors); - lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, a_FnRef); // same as lua_getref() + lua_rawgeti(m_LuaState, LUA_REGISTRYINDEX, static_cast<int>(a_FnRef)); // same as lua_getref() if (!lua_isfunction(m_LuaState, -1)) { lua_pop(m_LuaState, 2); @@ -821,6 +972,18 @@ void cLuaState::Push(std::chrono::milliseconds a_Value) +void cLuaState::Pop(int a_NumValuesToPop) +{ + ASSERT(IsValid()); + + lua_pop(m_LuaState, a_NumValuesToPop); + m_NumCurrentFunctionArgs -= a_NumValuesToPop; +} + + + + + bool cLuaState::GetStackValue(int a_StackPos, AString & a_Value) { size_t len = 0; @@ -847,6 +1010,41 @@ bool cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal) +bool cLuaState::GetStackValue(int a_StackPos, cCallback & a_Callback) +{ + return a_Callback.RefStack(*this, a_StackPos); +} + + + + + +bool cLuaState::GetStackValue(int a_StackPos, cCallbackPtr & a_Callback) +{ + if (a_Callback == nullptr) + { + a_Callback = cpp14::make_unique<cCallback>(); + } + return a_Callback->RefStack(*this, a_StackPos); +} + + + + + +bool cLuaState::GetStackValue(int a_StackPos, cCallbackSharedPtr & a_Callback) +{ + if (a_Callback == nullptr) + { + a_Callback = std::make_shared<cCallback>(); + } + return a_Callback->RefStack(*this, a_StackPos); +} + + + + + bool cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result) { if (lua_isnumber(m_LuaState, a_StackPos)) @@ -1632,16 +1830,16 @@ void cLuaState::ToString(int a_StackPos, AString & a_String) -void cLuaState::LogStack(const char * a_Header) +void cLuaState::LogStackValues(const char * a_Header) { - LogStack(m_LuaState, a_Header); + LogStackValues(m_LuaState, a_Header); } -void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) +void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header) { // Format string consisting only of %s is used to appease the compiler LOG("%s", (a_Header != nullptr) ? a_Header : "Lua C API Stack contents:"); @@ -1667,6 +1865,21 @@ void cLuaState::LogStack(lua_State * a_LuaState, const char * a_Header) +cLuaState * cLuaState::QueryCanonLuaState(void) +{ + // Get the CanonLuaState global from Lua: + auto cb = WalkToNamedGlobal(g_CanonLuaStateGlobalName); + if (!cb.IsValid()) + { + return nullptr; + } + return reinterpret_cast<cLuaState *>(lua_touserdata(m_LuaState, -1)); +} + + + + + int cLuaState::ReportFnCallErrors(lua_State * a_LuaState) { LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1)); @@ -1701,6 +1914,50 @@ int cLuaState::BreakIntoDebugger(lua_State * a_LuaState) +void cLuaState::TrackCallback(cCallback & a_Callback) +{ + // Get the CanonLuaState global from Lua: + auto canonState = QueryCanonLuaState(); + if (canonState == nullptr) + { + LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState)); + return; + } + + // Add the callback: + cCSLock Lock(canonState->m_CSTrackedCallbacks); + canonState->m_TrackedCallbacks.push_back(&a_Callback); +} + + + + + +void cLuaState::UntrackCallback(cCallback & a_Callback) +{ + // Get the CanonLuaState global from Lua: + auto canonState = QueryCanonLuaState(); + if (canonState == nullptr) + { + LOGWARNING("%s: Lua state %p has invalid CanonLuaState!", __FUNCTION__, reinterpret_cast<void *>(m_LuaState)); + return; + } + + // Remove the callback: + cCSLock Lock(canonState->m_CSTrackedCallbacks); + auto & trackedCallbacks = canonState->m_TrackedCallbacks; + trackedCallbacks.erase(std::remove_if(trackedCallbacks.begin(), trackedCallbacks.end(), + [&a_Callback](cCallback * a_StoredCallback) + { + return (a_StoredCallback == &a_Callback); + } + )); +} + + + + + //////////////////////////////////////////////////////////////////////////////// // cLuaState::cRef: @@ -1756,7 +2013,7 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) { UnRef(); } - m_LuaState = &a_LuaState; + m_LuaState = a_LuaState; lua_pushvalue(a_LuaState, a_StackPos); // Push a copy of the value at a_StackPos onto the stack m_Ref = luaL_ref(a_LuaState, LUA_REGISTRYINDEX); } @@ -1767,11 +2024,9 @@ void cLuaState::cRef::RefStack(cLuaState & a_LuaState, int a_StackPos) void cLuaState::cRef::UnRef(void) { - ASSERT(m_LuaState->IsValid()); // The reference should be destroyed before destroying the LuaState - if (IsValid()) { - luaL_unref(*m_LuaState, LUA_REGISTRYINDEX, m_Ref); + luaL_unref(m_LuaState, LUA_REGISTRYINDEX, m_Ref); } m_LuaState = nullptr; m_Ref = LUA_REFNIL; diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index b795a80d4..106d8a783 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -7,7 +7,7 @@ The contained lua_State can be either owned or attached. Owned lua_State is created by calling Create() and the cLuaState automatically closes the state Or, lua_State can be attached by calling Attach(), the cLuaState doesn't close such a state -Attaching a state will automatically close an owned state. +If owning a state, trying to attach a state will automatically close the previously owned state. Calling a Lua function is done internally by pushing the function using PushFunction(), then pushing the arguments and finally executing CallFunction(). cLuaState automatically keeps track of the number of @@ -16,8 +16,12 @@ the stack using GetStackValue(). All of this is wrapped in a templated function which is generated automatically by gen_LuaState_Call.lua script file into the LuaState_Call.inc file. Reference management is provided by the cLuaState::cRef class. This is used when you need to hold a reference to -any Lua object across several function calls; usually this is used for callbacks. The class is RAII-like, with -automatic resource management. +any Lua object across several function calls. The class is RAII-like, with automatic resource management. + +Callbacks management is provided by the cLuaState::cCallback class. Use a GetStackValue() with cCallbackPtr +parameter to store the callback, and then at any time you can use the cCallback's Call() templated function +to call the callback. The callback management takes care of cLuaState being destroyed - the cCallback object +stays valid but doesn't call into Lua code anymore, returning false for "failure" instead. */ @@ -39,6 +43,7 @@ extern "C" class cLuaServerHandle; class cLuaTCPLink; class cLuaUDPEndpoint; +class cPluginLua; @@ -49,6 +54,20 @@ class cLuaState { public: + /** Provides a RAII-style locking for the LuaState. + Used mainly by the cPluginLua internals to provide the actual locking for interface operations, such as callbacks. */ + class cLock + { + public: + cLock(cLuaState & a_LuaState): + m_Lock(a_LuaState.m_CS) + { + } + protected: + cCSLock m_Lock; + }; + + /** Used for storing references to object in the global registry. Can be bound (contains a reference) or unbound (doesn't contain reference). The reference can also be reset by calling RefStack(). */ @@ -80,8 +99,20 @@ public: /** Allows to use this class wherever an int (i. e. ref) is to be used */ explicit operator int(void) const { return m_Ref; } + /** Returns the Lua state associated with the value. */ + lua_State * GetLuaState(void) { return m_LuaState; } + + /** Creates a Lua reference to the specified object instance in the specified Lua state. + This is useful to make anti-GC references for objects that were created by Lua and need to stay alive longer than Lua GC would normally guarantee. */ + template <typename T> void CreateFromObject(cLuaState & a_LuaState, T && a_Object) + { + a_LuaState.Push(std::forward<T>(a_Object)); + RefStack(a_LuaState, -1); + a_LuaState.Pop(); + } + protected: - cLuaState * m_LuaState; + lua_State * m_LuaState; int m_Ref; // Remove the copy-constructor: @@ -112,6 +143,83 @@ public: } ; + /** Represents a callback to Lua that C++ code can call. + Is thread-safe and unload-safe. + When the Lua state is unloaded, the callback returns an error instead of calling into non-existent code. + To receive the callback instance from the Lua side, use RefStack() or (better) cLuaState::GetStackValue() + with a cCallbackPtr. Note that instances of this class are tracked in the canon LuaState instance, so that + they can be invalidated when the LuaState is unloaded; due to multithreading issues they can only be tracked + by-ptr, which has an unfortunate effect of disabling the copy and move constructors. */ + class cCallback + { + public: + /** Creates an unbound callback instance. */ + cCallback(void); + + ~cCallback() + { + Clear(); + } + + /** Calls the Lua callback, if still available. + Returns true if callback has been called. + Returns false if the Lua state isn't valid anymore. */ + template <typename... Args> + bool Call(Args &&... args) + { + auto cs = m_CS; + if (cs == nullptr) + { + return false; + } + cCSLock Lock(*cs); + if (!m_Ref.IsValid()) + { + return false; + } + return cLuaState(m_Ref.GetLuaState()).Call(m_Ref, std::forward<Args>(args)...); + } + + /** Set the contained callback to the function in the specified Lua state's stack position. + If a callback has been previously contained, it is freed first. */ + bool RefStack(cLuaState & a_LuaState, int a_StackPos); + + /** Frees the contained callback, if any. */ + void Clear(void); + + /** Returns true if the contained callback is valid. */ + bool IsValid(void); + + /** Returns true if the callback resides in the specified Lua state. + Internally, compares the callback's canon Lua state. */ + bool IsSameLuaState(cLuaState & a_LuaState); + + protected: + friend class cLuaState; + + /** The mutex protecting m_Ref against multithreaded access */ + cCriticalSection * m_CS; + + /** Reference to the Lua callback */ + cRef m_Ref; + + + /** Invalidates the callback, without untracking it from the cLuaState. + Called only from cLuaState when closing the Lua state. */ + void Invalidate(void); + + /** This class cannot be copied, because it is tracked in the LuaState by-ptr. + Use cCallbackPtr for a copyable object. */ + cCallback(const cCallback &) = delete; + + /** This class cannot be moved, because it is tracked in the LuaState by-ptr. + Use cCallbackPtr for a copyable object. */ + cCallback(cCallback &&) = delete; + }; + typedef UniquePtr<cCallback> cCallbackPtr; + typedef SharedPtr<cCallback> cCallbackSharedPtr; + + /** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */ class cRet { @@ -168,6 +276,8 @@ public: protected: lua_State * m_LuaState; + /** Used for debugging, Lua state's stack size is checked against this number to make sure + it is the same as when the value was pushed. */ int m_StackLen; // Remove the copy-constructor: @@ -261,11 +371,17 @@ public: void Push(const UInt32 a_Value); void Push(std::chrono::milliseconds a_time); + /** Pops the specified number of values off the top of the Lua stack. */ + void Pop(int a_NumValuesToPop = 1); + // GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged. // Returns whether value was changed // Enum values are checked for their allowed values and fail if the value is not assigned. bool GetStackValue(int a_StackPos, AString & a_Value); bool GetStackValue(int a_StackPos, bool & a_Value); + bool GetStackValue(int a_StackPos, cCallback & a_Callback); + bool GetStackValue(int a_StackPos, cCallbackPtr & a_Callback); + bool GetStackValue(int a_StackPos, cCallbackSharedPtr & a_Callback); bool GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result); bool GetStackValue(int a_StackPos, cRef & a_Ref); bool GetStackValue(int a_StackPos, double & a_Value); @@ -440,21 +556,26 @@ public: void ToString(int a_StackPos, AString & a_String); /** Logs all the elements' types on the API stack, with an optional header for the listing. */ - void LogStack(const char * a_Header = nullptr); + void LogStackValues(const char * a_Header = nullptr); /** Logs all the elements' types on the API stack, with an optional header for the listing. */ - static void LogStack(lua_State * a_LuaState, const char * a_Header = nullptr); + static void LogStackValues(lua_State * a_LuaState, const char * a_Header = nullptr); + + /** Returns the canon Lua state (the primary cLuaState instance that was used to create, rather than attach, the lua_State structure). + Returns nullptr if the canon Lua state cannot be queried. */ + cLuaState * QueryCanonLuaState(void); protected: + cCriticalSection m_CS; + lua_State * m_LuaState; /** If true, the state is owned by this object and will be auto-Closed. False => attached state */ bool m_IsOwned; /** The subsystem name is used for reporting errors to the console, it is either "plugin %s" or "LuaScript" - whatever is given to the constructor - */ + whatever is given to the constructor. */ AString m_SubsystemName; /** Name of the currently pushed function (for the Push / Call chain) */ @@ -463,6 +584,15 @@ protected: /** Number of arguments currently pushed (for the Push / Call chain) */ int m_NumCurrentFunctionArgs; + /** The tracked callbacks. + This object will invalidate all of these when it is about to be closed. + Protected against multithreaded access by m_CSTrackedCallbacks. */ + std::vector<cCallback *> m_TrackedCallbacks; + + /** Protects m_TrackedTallbacks against multithreaded access. */ + cCriticalSection m_CSTrackedCallbacks; + + /** Variadic template terminator: If there's nothing more to push / pop, just call the function. Note that there are no return values either, because those are prefixed by a cRet value, so the arg list is never empty. */ bool PushCallPop(void) @@ -512,18 +642,10 @@ protected: */ bool PushFunction(const char * a_FunctionName); - /** Pushes a function that has been saved into the global registry, identified by a_FnRef. - Returns true if successful. Logs a warning on failure - */ - bool PushFunction(int a_FnRef); - /** Pushes a function that has been saved as a reference. Returns true if successful. Logs a warning on failure */ - bool PushFunction(const cRef & a_FnRef) - { - return PushFunction(static_cast<int>(a_FnRef)); - } + bool PushFunction(const cRef & a_FnRef); /** Pushes a function that is stored in a referenced table by name Returns true if successful. Logs a warning on failure @@ -545,6 +667,14 @@ protected: /** Tries to break into the MobDebug debugger, if it is installed. */ static int BreakIntoDebugger(lua_State * a_LuaState); + + /** Adds the specified callback to tracking. + The callback will be invalidated when this Lua state is about to be closed. */ + void TrackCallback(cCallback & a_Callback); + + /** Removes the specified callback from tracking. + The callback will no longer be invalidated when this Lua state is about to be closed. */ + void UntrackCallback(cCallback & a_Callback); } ; diff --git a/src/Bindings/LuaTCPLink.cpp b/src/Bindings/LuaTCPLink.cpp index deaba9d86..4b04a1c02 100644 --- a/src/Bindings/LuaTCPLink.cpp +++ b/src/Bindings/LuaTCPLink.cpp @@ -13,7 +13,7 @@ cLuaTCPLink::cLuaTCPLink(cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), - m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos) + m_Callbacks(cPluginLua::cOperation(a_Plugin)(), a_CallbacksTableStackPos) { // Warn if the callbacks aren't valid: if (!m_Callbacks.IsValid()) diff --git a/src/Bindings/LuaUDPEndpoint.cpp b/src/Bindings/LuaUDPEndpoint.cpp index 8637eeb4e..ed8f4e87f 100644 --- a/src/Bindings/LuaUDPEndpoint.cpp +++ b/src/Bindings/LuaUDPEndpoint.cpp @@ -12,7 +12,7 @@ cLuaUDPEndpoint::cLuaUDPEndpoint(cPluginLua & a_Plugin, int a_CallbacksTableStackPos): m_Plugin(a_Plugin), - m_Callbacks(a_Plugin.GetLuaState(), a_CallbacksTableStackPos) + m_Callbacks(cPluginLua::cOperation(a_Plugin)(), a_CallbacksTableStackPos) { // Warn if the callbacks aren't valid: if (!m_Callbacks.IsValid()) diff --git a/src/Bindings/LuaWindow.cpp b/src/Bindings/LuaWindow.cpp index 706397a27..6fffa7aac 100644 --- a/src/Bindings/LuaWindow.cpp +++ b/src/Bindings/LuaWindow.cpp @@ -15,14 +15,13 @@ //////////////////////////////////////////////////////////////////////////////// // cLuaWindow: -cLuaWindow::cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : - super(a_WindowType, a_Title), +cLuaWindow::cLuaWindow(cLuaState & a_LuaState, cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) : + Super(a_WindowType, a_Title), m_Contents(a_SlotsX, a_SlotsY), - m_Plugin(nullptr), - m_LuaRef(LUA_REFNIL), - m_OnClosingFnRef(LUA_REFNIL), - m_OnSlotChangedFnRef(LUA_REFNIL) + m_LuaState(a_LuaState.QueryCanonLuaState()) { + ASSERT(m_LuaState != nullptr); // We must have a valid Lua state; this assert fails only if there was no Canon Lua state + m_Contents.AddListener(*this); m_SlotAreas.push_back(new cSlotAreaItemGrid(m_Contents, *this)); @@ -67,62 +66,42 @@ cLuaWindow::~cLuaWindow() -void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef) +void cLuaWindow::SetOnClosing(cLuaState::cCallbackPtr && a_OnClosing) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); - ASSERT(m_LuaRef == LUA_REFNIL); - m_Plugin = a_Plugin; - m_LuaRef = a_LuaRef; -} - - - + // Only one Lua state can be a cLuaWindow object callback: + ASSERT(a_OnClosing->IsSameLuaState(*m_LuaState)); - -bool cLuaWindow::IsLuaReferenced(void) const -{ - return ((m_Plugin != nullptr) && (m_LuaRef != LUA_REFNIL)); + // Store the new reference, releasing the old one if appropriate: + m_OnClosing = std::move(a_OnClosing); } -void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef) +void cLuaWindow::SetOnSlotChanged(cLuaState::cCallbackPtr && a_OnSlotChanged) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); + // Only one Lua state can be a cLuaWindow object callback: + ASSERT(a_OnSlotChanged->IsSameLuaState(*m_LuaState)); - // If there already was a function, unreference it first - if (m_OnClosingFnRef != LUA_REFNIL) - { - m_Plugin->Unreference(m_OnClosingFnRef); - } - - // Store the new reference - m_Plugin = a_Plugin; - m_OnClosingFnRef = a_FnRef; + // Store the new reference, releasing the old one if appropriate: + m_OnSlotChanged = std::move(a_OnSlotChanged); } -void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) +void cLuaWindow::OpenedByPlayer(cPlayer & a_Player) { - // Either m_Plugin is not set or equal to the passed plugin; only one plugin can use one cLuaWindow object - ASSERT((m_Plugin == nullptr) || (m_Plugin == a_Plugin)); - - // If there already was a function, unreference it first - if (m_OnSlotChangedFnRef != LUA_REFNIL) + // If the first player is opening the window, create a Lua Reference to the window object so that Lua will not GC it until the last player closes the window: + if (m_PlayerCount == 0) { - m_Plugin->Unreference(m_OnSlotChangedFnRef); + m_LuaRef.CreateFromObject(*m_LuaState, this); } - // Store the new reference - m_Plugin = a_Plugin; - m_OnSlotChangedFnRef = a_FnRef; + ++m_PlayerCount; + Super::OpenedByPlayer(a_Player); } @@ -132,17 +111,27 @@ void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef) bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) { // First notify the plugin through the registered callback: - if (m_OnClosingFnRef != LUA_REFNIL) + if (m_OnClosing != nullptr) { - ASSERT(m_Plugin != nullptr); - if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse)) + bool res; + if ( + m_OnClosing->Call(this, &a_Player, a_CanRefuse, cLuaState::Return, res) && // The callback succeeded + res // The callback says not to close the window + ) { // The callback disagrees (the higher levels check the CanRefuse flag compliance) return false; } } - return super::ClosedByPlayer(a_Player, a_CanRefuse); + // If the last player has closed the window, release the Lua reference, so that Lua may GC the object: + --m_PlayerCount; + if (m_PlayerCount == 0) + { + m_LuaRef.UnRef(); + } + + return Super::ClosedByPlayer(a_Player, a_CanRefuse); } @@ -151,13 +140,7 @@ bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) void cLuaWindow::Destroy(void) { - super::Destroy(); - - if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != nullptr)) - { - // The object is referenced by Lua, un-reference it - m_Plugin->Unreference(m_LuaRef); - } + Super::Destroy(); // Lua will take care of this object, it will garbage-collect it, so we must not delete it! m_IsDestroyed = false; @@ -178,7 +161,7 @@ void cLuaWindow::DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Pl } } - super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false); + Super::DistributeStackToAreas(a_ItemStack, a_Player, Areas, a_ShouldApply, false); } @@ -194,9 +177,9 @@ void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) } // If an OnSlotChanged callback has been registered, call it: - if (m_OnSlotChangedFnRef != LUA_REFNIL) + if (m_OnSlotChanged != nullptr) { - m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum); + m_OnSlotChanged->Call(this, a_SlotNum); } } diff --git a/src/Bindings/LuaWindow.h b/src/Bindings/LuaWindow.h index f292a5154..5f987d4c6 100644 --- a/src/Bindings/LuaWindow.h +++ b/src/Bindings/LuaWindow.h @@ -9,6 +9,8 @@ #pragma once +#include <atomic> +#include "LuaState.h" #include "../UI/Window.h" #include "../ItemGrid.h" @@ -16,35 +18,30 @@ -// fwd: PluginLua.h -class cPluginLua; - - - - - /** A window that has been created by a Lua plugin and is handled entirely by that plugin This object needs extra care with its lifetime management: - It is created by Lua, so Lua expects to garbage-collect it later -- normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them -To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() -delete the window, but rather leaves it dangling, with only Lua having the reference to it. -Additionally, to forbid Lua from deleting this object while it is used by players, the manual bindings for -cPlayer:OpenWindow check if the window is of this class, and if so, make a global Lua reference for this object. -This reference needs to be unreferenced in the Destroy() function. */ +- Normal cWindow objects are deleted in their ClosedByPlayer() function if the last player closes them + To overcome this, this object overloads the Destroy functions, which doesn't let the ClosedByPlayer() + delete the window, but rather leaves it dangling, with only Lua having the reference to it. +- Lua could GC the window while a player is still using it + The object creates a Lua reference to itself when opened by a player and + removes the reference when the last player closes the window. +*/ // tolua_begin class cLuaWindow : public cWindow // tolua_end , public cItemGrid::cListener - // tolua_begin -{ - typedef cWindow super; +{ // tolua_export + typedef cWindow Super; public: - /** Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size */ - cLuaWindow(cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + /** Create a window of the specified type, with a slot grid of a_SlotsX * a_SlotsY size. + Exported in ManualBindings.cpp */ + cLuaWindow(cLuaState & a_LuaState, cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title); + // tolua_begin virtual ~cLuaWindow(); /** Returns the internal representation of the contents that are manipulated by Lua */ @@ -52,36 +49,37 @@ public: // tolua_end - /** Sets the plugin reference and the internal Lua object reference index - used for preventing Lua's GC to collect this class while the window is open. */ - void SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef); - - /** Returns true if SetLuaRef() has been called */ - bool IsLuaReferenced(void) const; - - /** Sets the callback function (Lua reference) to call when the window is about to close */ - void SetOnClosing(cPluginLua * a_Plugin, int a_FnRef); + /** Sets the Lua callback function to call when the window is about to close */ + void SetOnClosing(cLuaState::cCallbackPtr && a_OnClosing); - /** Sets the callback function (Lua reference) to call when a slot is changed */ - void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef); + /** Sets the Lua callback function to call when a slot is changed */ + void SetOnSlotChanged(cLuaState::cCallbackPtr && a_OnSlotChanged); protected: + /** Contents of the non-inventory part */ cItemGrid m_Contents; - /** The plugin that has opened the window and owns the m_LuaRef */ - cPluginLua * m_Plugin; + /** The Lua state that has opened the window and owns the m_LuaRef */ + cLuaState * m_LuaState; + + /** The Lua callback to call when the window is closing for any player */ + cLuaState::cCallbackPtr m_OnClosing; + + /** The Lua callback to call when a slot has changed */ + cLuaState::cCallbackPtr m_OnSlotChanged; - /** The Lua object reference, used for keeping the object alive as long as any player has the window open */ - int m_LuaRef; + /** Number of players that are currently using the window. + Used to manager the m_LuaRef lifetime. */ + std::atomic<int> m_PlayerCount; - /** The Lua reference for the callback to call when the window is closing for any player */ - int m_OnClosingFnRef; + /** Reference to self, to keep Lua from GCing the object while a player is still using it. + Created when the first player opens the window, destroyed when the last player closes the window. */ + cLuaState::cRef m_LuaRef; - /** The Lua reference for the callback to call when a slot has changed */ - int m_OnSlotChangedFnRef; // cWindow overrides: + virtual void OpenedByPlayer(cPlayer & a_Player) override; virtual bool ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) override; virtual void Destroy(void) override; virtual void DistributeStack(cItem & a_ItemStack, int a_Slot, cPlayer & a_Player, cSlotArea * a_ClickedArea, bool a_ShouldApply) override; diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 9945929d0..1d735ac83 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -43,6 +43,50 @@ +//////////////////////////////////////////////////////////////////////////////// +// LuaCommandHandler: + +/** Defines a bridge between cPluginManager::cCommandHandler and cLuaState::cCallback. */ +class LuaCommandHandler: + public cPluginManager::cCommandHandler +{ +public: + LuaCommandHandler(cLuaState::cCallbackPtr && a_Callback): + m_Callback(std::move(a_Callback)) + { + } + + virtual bool ExecuteCommand( + const AStringVector & a_Split, + cPlayer * a_Player, + const AString & a_Command, + cCommandOutputCallback * a_Output + ) override + { + bool res = false; + AString s; + if (!m_Callback->Call(a_Split, a_Player, a_Command, cLuaState::Return, res, s)) + { + return false; + } + if (res && (a_Output != nullptr) && !s.empty()) + { + a_Output->Out(s); + } + return res; + } + +protected: + cLuaState::cCallbackPtr m_Callback; +}; + + + + + +//////////////////////////////////////////////////////////////////////////////// +// cManualBindings: + // Better error reporting for Lua int cManualBindings::tolua_do_error(lua_State * L, const char * a_pMsg, tolua_Error * a_pToLuaError) { @@ -993,7 +1037,13 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, } // Retrieve and check the hook type - int HookType = static_cast<int>(tolua_tonumber(S, a_ParamIdx, -1)); + int HookType; + if (!S.GetStackValue(a_ParamIdx, HookType)) + { + LOGWARNING("cPluginManager.AddHook(): Cannot read the hook type."); + S.LogStackTrace(); + return 0; + } if (!a_PluginManager->IsValidHookType(HookType)) { LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType); @@ -1002,7 +1052,14 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager, } // Add the hook to the plugin - if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1)) + cLuaState::cCallbackPtr callback; + if (!S.GetStackValue(a_ParamIdx + 1, callback)) + { + LOGWARNING("cPluginManager.AddHook(): Cannot read the callback parameter"); + S.LogStackTrace(); + return 0; + } + if (!Plugin->AddHookCallback(HookType, std::move(callback))) { LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType); S.LogStackTrace(); @@ -1059,10 +1116,11 @@ static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager, } // Retrieve the function to call and add it to the plugin: - lua_pushstring(S, FnName); - bool res = Plugin->AddHookRef(HookType, 1); - lua_pop(S, 1); // Pop the function off the stack - if (!res) + cLuaState::cCallbackPtr callback; + lua_getglobal(S, FnName); + bool res = S.GetStackValue(-1, callback); + lua_pop(S, 1); + if (!res || !callback->IsValid()) { LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName); S.LogStackTrace(); @@ -1254,12 +1312,13 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) -static int tolua_cPluginManager_BindCommand(lua_State * L) +static int tolua_cPluginManager_BindCommand(lua_State * a_LuaState) { /* Function signatures: cPluginManager:BindCommand(Command, Permission, Function, HelpString) cPluginManager.BindCommand(Command, Permission, Function, HelpString) -- without the "self" param */ + cLuaState L(a_LuaState); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { @@ -1292,29 +1351,24 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) return 0; } cPluginManager * self = cPluginManager::Get(); - AString Command (tolua_tocppstring(L, idx, "")); - AString Permission(tolua_tocppstring(L, idx + 1, "")); - AString HelpString(tolua_tocppstring(L, idx + 3, "")); - - // 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) + AString Command, Permission, HelpString; + cLuaState::cCallbackPtr Handler; + L.GetStackValues(idx, Command, Permission, Handler, HelpString); + if (!Handler->IsValid()) { LOGERROR("\"BindCommand\": Cannot create a function reference. Command \"%s\" not bound.", Command.c_str()); return 0; } - if (!self->BindCommand(Command, Plugin, Permission, HelpString)) + auto CommandHandler = std::make_shared<LuaCommandHandler>(std::move(Handler)); + if (!self->BindCommand(Command, Plugin, CommandHandler, Permission, HelpString)) { // Refused. Possibly already bound. Error message has been given, display the callstack: - cLuaState LS(L); - LS.LogStackTrace(); + L.LogStackTrace(); return 0; } - Plugin->BindCommand(Command, FnRef); - lua_pushboolean(L, true); + L.Push(true); return 1; } @@ -1322,7 +1376,7 @@ static int tolua_cPluginManager_BindCommand(lua_State * L) -static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) +static int tolua_cPluginManager_BindConsoleCommand(lua_State * a_LuaState) { /* Function signatures: cPluginManager:BindConsoleCommand(Command, Function, HelpString) @@ -1330,6 +1384,7 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) */ // Get the plugin identification out of LuaState: + cLuaState L(a_LuaState); cPluginLua * Plugin = cManualBindings::GetLuaPlugin(L); if (Plugin == nullptr) { @@ -1361,28 +1416,23 @@ static int tolua_cPluginManager_BindConsoleCommand(lua_State * L) return 0; } cPluginManager * self = cPluginManager::Get(); - AString Command (tolua_tocppstring(L, idx, "")); - AString HelpString(tolua_tocppstring(L, idx + 2, "")); - - // 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) + AString Command, HelpString; + cLuaState::cCallbackPtr Handler; + L.GetStackValues(idx, Command, Handler, HelpString); + if (!Handler->IsValid()) { - LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console Command \"%s\" not bound.", Command.c_str()); + LOGERROR("\"BindConsoleCommand\": Cannot create a function reference. Console command \"%s\" not bound.", Command.c_str()); return 0; } - if (!self->BindConsoleCommand(Command, Plugin, HelpString)) + auto CommandHandler = std::make_shared<LuaCommandHandler>(std::move(Handler)); + if (!self->BindConsoleCommand(Command, Plugin, CommandHandler, HelpString)) { // Refused. Possibly already bound. Error message has been given, display the callstack: - cLuaState LS(L); - LS.LogStackTrace(); + L.LogStackTrace(); return 0; } - - Plugin->BindConsoleCommand(Command, FnRef); - lua_pushboolean(L, true); + L.Push(true); return 1; } @@ -1586,55 +1636,6 @@ static int tolua_cPlayer_GetRestrictions(lua_State * tolua_S) -static int tolua_cPlayer_OpenWindow(lua_State * tolua_S) -{ - // Function signature: cPlayer:OpenWindow(Window) - - // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - return 0; - } - - // Get the parameters: - cPlayer * self = reinterpret_cast<cPlayer *>(tolua_tousertype(tolua_S, 1, nullptr)); - cWindow * wnd = reinterpret_cast<cWindow *>(tolua_tousertype(tolua_S, 2, nullptr)); - if ((self == nullptr) || (wnd == nullptr)) - { - LOGWARNING("%s: invalid self (%p) or wnd (%p)", __FUNCTION__, static_cast<void *>(self), static_cast<void *>(wnd)); - return 0; - } - - // If cLuaWindow, add a reference, so that Lua won't delete the cLuaWindow object mid-processing - tolua_Error err; - if (tolua_isusertype(tolua_S, 2, "cLuaWindow", 0, &err)) - { - cLuaWindow * LuaWnd = reinterpret_cast<cLuaWindow *>(wnd); - // Only if not already referenced - if (!LuaWnd->IsLuaReferenced()) - { - int LuaRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (LuaRef == LUA_REFNIL) - { - LOGWARNING("%s: Cannot create a window reference. Cannot open window \"%s\".", - __FUNCTION__, wnd->GetWindowTitle().c_str() - ); - return 0; - } - LuaWnd->SetLuaRef(Plugin, LuaRef); - } - } - - // Open the window - self->OpenWindow(wnd); - return 0; -} - - - - - static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S) { // Function signature: cPlayer:PermissionMatches(PermissionStr, TemplateStr) -> bool @@ -1665,36 +1666,25 @@ static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S) template < class OBJTYPE, - void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef) + void (OBJTYPE::*SetCallback)(cLuaState::cCallbackPtr && a_CallbackFn) > static int tolua_SetObjectCallback(lua_State * tolua_S) { // Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction) - // Retrieve the plugin instance from the Lua state - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - // Warning message has already been printed by GetLuaPlugin(), bail out silently - return 0; - } - // Get the parameters - self and the function reference: - OBJTYPE * self = reinterpret_cast<OBJTYPE *>(tolua_tousertype(tolua_S, 1, nullptr)); - if (self == nullptr) - { - LOGWARNING("%s: invalid self (%p)", __FUNCTION__, static_cast<void *>(self)); - return 0; - } - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); // Store function reference for later retrieval - if (FnRef == LUA_REFNIL) + cLuaState L(tolua_S); + OBJTYPE * self; + cLuaState::cCallbackPtr callback; + if (!L.GetStackValues(1, self, callback)) { - LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__); + LOGWARNING("%s: Cannot get parameters", __FUNCTION__); + L.LogStackTrace(); return 0; } // Set the callback - (self->*SetCallback)(Plugin, FnRef); + (self->*SetCallback)(std::move(callback)); return 0; } @@ -1702,47 +1692,71 @@ static int tolua_SetObjectCallback(lua_State * tolua_S) -static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) +// Callback class used for the WebTab: +class cWebTabCallback: + public cWebAdmin::cWebTabCallback { - cLuaState LuaState(tolua_S); - cPluginLua * self = nullptr; +public: + /** The Lua callback to call to generate the page contents. */ + cLuaState::cCallback m_Callback; - if (!LuaState.GetStackValue(1, self)) + virtual bool Call( + const HTTPRequest & a_Request, + const AString & a_UrlPath, + AString & a_Content, + AString & a_ContentType + ) override { - LOGWARNING("cPluginLua:AddWebTab: invalid self as first argument"); - return 0; + AString content, contentType; + return m_Callback.Call(&a_Request, a_UrlPath, cLuaState::Return, a_Content, a_ContentType); } +}; + + + - tolua_Error tolua_err; - tolua_err.array = 0; - tolua_err.index = 3; - tolua_err.type = "function"; - std::string Title; - int Reference = LUA_REFNIL; +static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) +{ + // OBSOLETE, use cWebAdmin:AddWebTab() instead! + // Function signature: + // cPluginLua:AddWebTab(Title, CallbackFn, [UrlPath]) - if (LuaState.CheckParamString(2) && LuaState.CheckParamFunction(3)) + // TODO: Warn about obsolete API usage + // Only implement after merging the new API change and letting some time for changes in the plugins + + // Check params: + cLuaState LuaState(tolua_S); + cPluginLua * self = cManualBindings::GetLuaPlugin(tolua_S); + if (self == nullptr) { - Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - LuaState.GetStackValue(2, Title); + return 0; } - else + if ( + !LuaState.CheckParamString(2) || + !LuaState.CheckParamFunction(3) || + // Optional string as param 4 + !LuaState.CheckParamEnd(5) + ) { - return cManualBindings::tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err); + return 0; } - if (Reference != LUA_REFNIL) + // Read the params: + AString title, urlPath; + auto callback = std::make_shared<cWebTabCallback>(); + if (!LuaState.GetStackValues(2, title, callback->m_Callback)) { - if (!self->AddWebTab(Title.c_str(), tolua_S, Reference)) - { - luaL_unref(tolua_S, LUA_REGISTRYINDEX, Reference); - } + LOGWARNING("cPlugin:AddWebTab(): Cannot read required parameters"); + return 0; } - else + if (!LuaState.GetStackValue(4, urlPath)) { - LOGWARNING("cPluginLua:AddWebTab: invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str()); + urlPath = cWebAdmin::GetURLEncodedString(title); } + cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback); + return 0; } @@ -2107,22 +2121,68 @@ static int tolua_cUrlParser_ParseAuthorityPart(lua_State * a_LuaState) -static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) +static int tolua_cWebAdmin_AddWebTab(lua_State * tolua_S) +{ + // Function signatures: + // cWebAdmin:AddWebTab(Title, UrlPath, CallbackFn) + + // Check params: + cLuaState LuaState(tolua_S); + cPluginLua * self = cManualBindings::GetLuaPlugin(tolua_S); + if (self == nullptr) + { + return 0; + } + if ( + // Don't care whether the first param is a cWebAdmin instance or class + !LuaState.CheckParamString(2, 3) || + !LuaState.CheckParamFunction(4) || + !LuaState.CheckParamEnd(5) + ) + { + return 0; + } + + // Read the params: + AString title, urlPath; + auto callback = std::make_shared<cWebTabCallback>(); + if (!LuaState.GetStackValues(2, title, urlPath, callback->m_Callback)) + { + LOGWARNING("cWebAdmin:AddWebTab(): Cannot read required parameters"); + return 0; + } + + cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback); + + return 0; +} + + + + + +static int tolua_cWebAdmin_GetAllWebTabs(lua_State * tolua_S) { - cWebAdmin * self = reinterpret_cast<cWebAdmin *>(tolua_tousertype(tolua_S, 1, nullptr)); + // Function signature: + // cWebAdmin:GetAllWebTabs() -> { {"PluginName", "UrlPath", "Title"}, {"PluginName", "UrlPath", "Title"}, ...} - const cWebAdmin::PluginList & AllPlugins = self->GetPlugins(); + // Don't care about params at all - lua_createtable(tolua_S, static_cast<int>(AllPlugins.size()), 0); + auto webTabs = cRoot::Get()->GetWebAdmin()->GetAllWebTabs(); + lua_createtable(tolua_S, static_cast<int>(webTabs.size()), 0); int newTable = lua_gettop(tolua_S); int index = 1; - cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin(); - while (iter != AllPlugins.end()) - { - const cWebPlugin * Plugin = *iter; - tolua_pushusertype(tolua_S, reinterpret_cast<void *>(const_cast<cWebPlugin*>(Plugin)), "const cWebPlugin"); + cLuaState L(tolua_S); + for (const auto & wt: webTabs) + { + lua_createtable(tolua_S, 0, 3); + L.Push(wt->m_PluginName); + lua_setfield(tolua_S, -2, "PluginName"); + L.Push(wt->m_UrlPath); + lua_setfield(tolua_S, -2, "UrlPath"); + L.Push(wt->m_Title); + lua_setfield(tolua_S, -2, "Title"); lua_rawseti(tolua_S, newTable, index); - ++iter; ++index; } return 1; @@ -2132,14 +2192,14 @@ static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S) -/** Binding for cWebAdmin::GetHTMLEscapedString. +/** Binding for cWebAdmin::GetBaseURL. Manual code required because ToLua generates an extra return value */ -static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) +static int tolua_cWebAdmin_GetBaseURL(lua_State * tolua_S) { // Check the param types: cLuaState S(tolua_S); if ( - !S.CheckParamUserTable(1, "cWebAdmin") || + // Don't care whether the first param is a cWebAdmin instance or class !S.CheckParamString(2) || !S.CheckParamEnd(3) ) @@ -2152,7 +2212,7 @@ static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) S.GetStackValue(2, Input); // Convert and return: - S.Push(cWebAdmin::GetHTMLEscapedString(Input)); + S.Push(cWebAdmin::GetBaseURL(Input)); return 1; } @@ -2160,14 +2220,14 @@ static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) -/** Binding for cWebAdmin::GetURLEncodedString. +/** Binding for cWebAdmin::GetContentTypeFromFileExt. Manual code required because ToLua generates an extra return value */ -static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) +static int tolua_cWebAdmin_GetContentTypeFromFileExt(lua_State * tolua_S) { // Check the param types: cLuaState S(tolua_S); if ( - !S.CheckParamUserTable(1, "cWebAdmin") || + // Don't care whether the first param is a cWebAdmin instance or class !S.CheckParamString(2) || !S.CheckParamEnd(3) ) @@ -2180,7 +2240,7 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) S.GetStackValue(2, Input); // Convert and return: - S.Push(cWebAdmin::GetURLEncodedString(Input)); + S.Push(cWebAdmin::GetContentTypeFromFileExt(Input)); return 1; } @@ -2188,20 +2248,112 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) -static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) +/** Binding for cWebAdmin::GetHTMLEscapedString. +Manual code required because ToLua generates an extra return value */ +static int tolua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S) { - // Returns a map of (SafeTitle -> Title) for the plugin's web tabs. - auto self = reinterpret_cast<cWebPlugin *>(tolua_tousertype(tolua_S, 1, nullptr)); - auto TabNames = self->GetTabNames(); - lua_newtable(tolua_S); - int index = 1; - for (auto itr = TabNames.cbegin(), end = TabNames.cend(); itr != end; ++itr) + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care whether the first param is a cWebAdmin instance or class + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) { - tolua_pushstring(tolua_S, itr->second.c_str()); // Because the SafeTitle is supposed to be unique, use it as key - tolua_pushstring(tolua_S, itr->first.c_str()); - lua_rawset(tolua_S, -3); - ++index; + return 0; + } + + // Get the parameters: + AString Input; + S.GetStackValue(2, Input); + + // Convert and return: + S.Push(cWebAdmin::GetHTMLEscapedString(Input)); + return 1; +} + + + + + +/** Binding for cWebAdmin::GetPage. */ +static int tolua_cWebAdmin_GetPage(lua_State * tolua_S) +{ + /* + Function signature: + cWebAdmin:GetPage(a_HTTPRequest) -> + { + Content = "", // Content generated by the plugin + ContentType = "", // Content type generated by the plugin (default: "text/html") + UrlPath = "", // URL path of the tab + TabTitle = "", // Tab's title, as register via cWebAdmin:AddWebTab() + PluginName = "", // Plugin's API name + PluginFolder = "", // Plugin's folder name (display name) + } + */ + + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care about first param, whether it's cWebAdmin instance or class + !S.CheckParamUserType(2, "HTTPRequest") || + !S.CheckParamEnd(3) + ) + { + return 0; + } + + // Get the parameters: + HTTPRequest * request = nullptr; + if (!S.GetStackValue(2, request)) + { + LOGWARNING("cWebAdmin:GetPage(): Cannot read the HTTPRequest parameter."); + return 0; + } + + // Generate the page and push the results as a dictionary-table: + auto page = cRoot::Get()->GetWebAdmin()->GetPage(*request); + lua_createtable(S, 0, 6); + S.Push(page.Content); + lua_setfield(S, -2, "Content"); + S.Push(page.ContentType); + lua_setfield(S, -2, "ContentType"); + S.Push(page.TabUrlPath); + lua_setfield(S, -2, "UrlPath"); + S.Push(page.TabTitle); + lua_setfield(S, -2, "TabTitle"); + S.Push(page.PluginName); + lua_setfield(S, -2, "PluginName"); + S.Push(cPluginManager::Get()->GetPluginFolderName(page.PluginName)); + lua_setfield(S, -2, "PluginFolder"); + return 1; +} + + + + + +/** Binding for cWebAdmin::GetURLEncodedString. +Manual code required because ToLua generates an extra return value */ +static int tolua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S) +{ + // Check the param types: + cLuaState S(tolua_S); + if ( + // Don't care whether the first param is a cWebAdmin instance or class + !S.CheckParamString(2) || + !S.CheckParamEnd(3) + ) + { + return 0; } + + // Get the parameters: + AString Input; + S.GetStackValue(2, Input); + + // Convert and return: + S.Push(cWebAdmin::GetURLEncodedString(Input)); return 1; } @@ -2624,6 +2776,79 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) +static int tolua_cLuaWindow_new(lua_State * tolua_S) +{ + // Function signature: + // cLuaWindow:new(type, slotsX, slotsY, title) + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cLuaWindow") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamString(5) || + !L.CheckParamEnd(6) + ) + { + return 0; + } + + // Read params: + int windowType, slotsX, slotsY; + AString title; + if (!L.GetStackValues(2, windowType, slotsX, slotsY, title)) + { + LOGWARNING("%s: Cannot read Lua parameters", __FUNCTION__); + L.LogStackValues(); + L.LogStackTrace(); + } + + // Create the window and return it: + L.Push(new cLuaWindow(L, static_cast<cLuaWindow::WindowType>(windowType), slotsX, slotsY, title)); + return 1; +} + + + + + +static int tolua_cLuaWindow_new_local(lua_State * tolua_S) +{ + // Function signature: + // cLuaWindow:new(type, slotsX, slotsY, title) + + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserTable(1, "cLuaWindow") || + !L.CheckParamNumber(2, 4) || + !L.CheckParamString(5) || + !L.CheckParamEnd(6) + ) + { + return 0; + } + + // Read params: + int windowType, slotsX, slotsY; + AString title; + if (!L.GetStackValues(2, windowType, slotsX, slotsY, title)) + { + LOGWARNING("%s: Cannot read Lua parameters", __FUNCTION__); + L.LogStackValues(); + L.LogStackTrace(); + } + + // Create the window, register it for GC and return it: + L.Push(new cLuaWindow(L, static_cast<cLuaWindow::WindowType>(windowType), slotsX, slotsY, title)); + tolua_register_gc(tolua_S, lua_gettop(tolua_S)); + return 1; +} + + + + + static int tolua_cRoot_GetBuildCommitID(lua_State * tolua_S) { cLuaState L(tolua_S); @@ -3167,7 +3392,7 @@ static int tolua_cBoundingBox_CalcLineIntersection(lua_State * a_LuaState) const cBoundingBox * bbox; if (!L.GetStackValues(1, bbox, pt1, pt2)) // Try the regular signature { - L.LogStack(); + L.LogStackValues(); tolua_error(a_LuaState, "Invalid function params. Expected either bbox:CalcLineIntersection(pt1, pt2) or cBoundingBox:CalcLineIntersection(min, max, pt1, pt2).", nullptr); return 0; } @@ -3197,7 +3422,7 @@ static int tolua_cBoundingBox_Intersect(lua_State * a_LuaState) const cBoundingBox * other; if (!L.GetStackValues(1, self, other)) { - L.LogStack(); + L.LogStackValues(); tolua_error(a_LuaState, "Invalid function params. Expected bbox:Intersect(otherBbox).", nullptr); return 0; } @@ -3570,6 +3795,9 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cLuaWindow"); + tolua_function(tolua_S, "new", tolua_cLuaWindow_new); + tolua_function(tolua_S, "new_local", tolua_cLuaWindow_new_local); + tolua_function(tolua_S, ".call", tolua_cLuaWindow_new_local); tolua_function(tolua_S, "SetOnClosing", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnClosing>); tolua_function(tolua_S, "SetOnSlotChanged", tolua_SetObjectCallback<cLuaWindow, &cLuaWindow::SetOnSlotChanged>); tolua_endmodule(tolua_S); @@ -3590,7 +3818,6 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_beginmodule(tolua_S, "cPlayer"); tolua_function(tolua_S, "GetPermissions", tolua_cPlayer_GetPermissions); tolua_function(tolua_S, "GetRestrictions", tolua_cPlayer_GetRestrictions); - tolua_function(tolua_S, "OpenWindow", tolua_cPlayer_OpenWindow); tolua_function(tolua_S, "PermissionMatches", tolua_cPlayer_PermissionMatches); tolua_endmodule(tolua_S); @@ -3655,13 +3882,13 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cWebAdmin"); - tolua_function(tolua_S, "GetHTMLEscapedString", tolua_AllToLua_cWebAdmin_GetHTMLEscapedString); - tolua_function(tolua_S, "GetPlugins", tolua_cWebAdmin_GetPlugins); - tolua_function(tolua_S, "GetURLEncodedString", tolua_AllToLua_cWebAdmin_GetURLEncodedString); - tolua_endmodule(tolua_S); - - tolua_beginmodule(tolua_S, "cWebPlugin"); - tolua_function(tolua_S, "GetTabNames", tolua_cWebPlugin_GetTabNames); + tolua_function(tolua_S, "AddWebTab", tolua_cWebAdmin_AddWebTab); + tolua_function(tolua_S, "GetAllWebTabs", tolua_cWebAdmin_GetAllWebTabs); + tolua_function(tolua_S, "GetBaseURL", tolua_cWebAdmin_GetBaseURL); + tolua_function(tolua_S, "GetContentTypeFromFileExt", tolua_cWebAdmin_GetContentTypeFromFileExt); + tolua_function(tolua_S, "GetHTMLEscapedString", tolua_cWebAdmin_GetHTMLEscapedString); + tolua_function(tolua_S, "GetPage", tolua_cWebAdmin_GetPage); + tolua_function(tolua_S, "GetURLEncodedString", tolua_cWebAdmin_GetURLEncodedString); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "HTTPRequest"); diff --git a/src/Bindings/ManualBindings_World.cpp b/src/Bindings/ManualBindings_World.cpp index 00d2169d8..b3170a636 100644 --- a/src/Bindings/ManualBindings_World.cpp +++ b/src/Bindings/ManualBindings_World.cpp @@ -103,7 +103,9 @@ static int tolua_cWorld_ChunkStay(lua_State * tolua_S) return 0; } - ChunkStay->Enable(*World->GetChunkMap(), 3, 4); + cLuaState::cCallbackPtr onChunkAvailable, onAllChunksAvailable; + L.GetStackValues(3, onChunkAvailable, onAllChunksAvailable); // Callbacks may be unassigned at all - as a request to load / generate chunks + ChunkStay->Enable(*World->GetChunkMap(), std::move(onChunkAvailable), std::move(onAllChunksAvailable)); return 0; } @@ -466,67 +468,41 @@ static int tolua_cWorld_PrepareChunk(lua_State * tolua_S) -class cLuaWorldTask : - public cPluginLua::cResettable -{ -public: - cLuaWorldTask(cPluginLua & a_Plugin, int a_FnRef) : - cPluginLua::cResettable(a_Plugin), - m_FnRef(a_FnRef) - { - } - - void Run(cWorld & a_World) - { - cCSLock Lock(m_CSPlugin); - if (m_Plugin != nullptr) - { - m_Plugin->Call(m_FnRef, &a_World); - } - } - -protected: - int m_FnRef; -}; - - - - - static int tolua_cWorld_QueueTask(lua_State * tolua_S) { - // Binding for cWorld::QueueTask - // Params: function + // Function signature: + // World:QueueTask(Callback) - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) + // Retrieve the args: + cLuaState L(tolua_S); + if ( + !L.CheckParamUserType(1, "cWorld") || + !L.CheckParamNumber (2) || + !L.CheckParamFunction(3) + ) { - // An error message has been already printed in GetLuaPlugin() return 0; } - - // Retrieve the args: - cWorld * self = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr)); - if (self == nullptr) + cWorld * World; + cLuaState::cCallbackSharedPtr Task; + if (!L.GetStackValues(1, World, Task)) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Cannot read parameters"); } - if (!lua_isfunction(tolua_S, 2)) + if (World == nullptr) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) + if (!Task->IsValid()) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); } - auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); - Plugin->AddResettable(ResettableTask); - self->QueueTask(std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1)); + World->QueueTask([Task](cWorld & a_World) + { + Task->Call(&a_World); + } + ); return 0; } @@ -576,16 +552,8 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S) static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { - // Binding for cWorld::ScheduleTask - // Params: function, Ticks - - // Retrieve the cPlugin from the LuaState: - cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S); - if (Plugin == nullptr) - { - // An error message has been already printed in GetLuaPlugin() - return 0; - } + // Function signature: + // World:ScheduleTask(NumTicks, Callback) // Retrieve the args: cLuaState L(tolua_S); @@ -597,22 +565,27 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) { return 0; } - cWorld * World = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr)); + cWorld * World; + int NumTicks; + auto Task = std::make_shared<cLuaState::cCallback>(); + if (!L.GetStackValues(1, World, NumTicks, Task)) + { + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Cannot read parameters"); + } if (World == nullptr) { return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - - // Create a reference to the function: - int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FnRef == LUA_REFNIL) + if (!Task->IsValid()) { - return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter"); } - auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef); - Plugin->AddResettable(ResettableTask); - World->ScheduleTask(static_cast<int>(tolua_tonumber(tolua_S, 2, 0)), std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1)); + World->ScheduleTask(NumTicks, [Task](cWorld & a_World) + { + Task->Call(&a_World); + } + ); return 0; } diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 3ee417361..3276fde67 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -110,21 +110,6 @@ public: virtual bool OnWorldStarted (cWorld & a_World) = 0; virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) = 0; - /** 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, const AString & a_FullCommand) = 0; - - /** 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, const AString & a_FullCommand) = 0; - - /** 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) {} - // tolua_begin const AString & GetName(void) const { return m_Name; } void SetName(const AString & a_Name) { m_Name = a_Name; } diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index a266e6223..d1fc2ae4f 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -15,6 +15,8 @@ #include "../CommandOutput.h" #include "PluginManager.h" #include "../Item.h" +#include "../Root.h" +#include "../WebAdmin.h" extern "C" { @@ -43,7 +45,6 @@ cPluginLua::cPluginLua(const AString & a_PluginDirectory) : cPluginLua::~cPluginLua() { - cCSLock Lock(m_CriticalSection); Close(); } @@ -53,45 +54,22 @@ cPluginLua::~cPluginLua() void cPluginLua::Close(void) { - cCSLock Lock(m_CriticalSection); - + cOperation op(*this); // If already closed, bail out: - if (!m_LuaState.IsValid()) + if (!op().IsValid()) { - ASSERT(m_Resettables.empty()); ASSERT(m_HookMap.empty()); return; } - // Remove the command bindings and web tabs: - ClearCommands(); - ClearConsoleCommands(); - ClearTabs(); - - // Notify and remove all m_Resettables (unlock the m_CriticalSection while resetting them): - cResettablePtrs resettables; - std::swap(m_Resettables, resettables); - { - cCSUnlock Unlock(Lock); - for (auto resettable: resettables) - { - resettable->Reset(); - } - m_Resettables.clear(); - } // cCSUnlock (m_CriticalSection) + // Remove the web tabs: + ClearWebTabs(); // Release all the references in the hook map: - for (cHookMap::iterator itrH = m_HookMap.begin(), endH = m_HookMap.end(); itrH != endH; ++itrH) - { - for (cLuaRefs::iterator itrR = itrH->second.begin(), endR = itrH->second.end(); itrR != endR; ++itrR) - { - delete *itrR; - } // for itrR - itrH->second[] - } // for itrH - m_HookMap[] m_HookMap.clear(); // Close the Lua engine: - m_LuaState.Close(); + op().Close(); } @@ -100,8 +78,8 @@ void cPluginLua::Close(void) bool cPluginLua::Load(void) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { m_LuaState.Create(); m_LuaState.RegisterAPILibs(); @@ -205,7 +183,7 @@ bool cPluginLua::Load(void) void cPluginLua::Unload(void) { - ClearTabs(); + ClearWebTabs(); super::Unload(); Close(); } @@ -216,12 +194,12 @@ void cPluginLua::Unload(void) void cPluginLua::OnDisable(void) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.HasFunction("OnDisable")) + cOperation op(*this); + if (!op().HasFunction("OnDisable")) { return; } - m_LuaState.Call("OnDisable"); + op().Call("OnDisable"); } @@ -230,16 +208,7 @@ void cPluginLua::OnDisable(void) void cPluginLua::Tick(float a_Dt) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), a_Dt); - } + CallSimpleHooks(cPluginManager::HOOK_TICK, a_Dt); } @@ -248,22 +217,7 @@ void cPluginLua::Tick(float a_Dt) bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_SPREAD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Source, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BLOCK_SPREAD, &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Source); } @@ -272,22 +226,7 @@ bool cPluginLua::OnBlockSpread(cWorld & a_World, int a_BlockX, int a_BlockY, int bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cItems & a_Pickups) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BLOCK_TO_PICKUPS]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BLOCK_TO_PICKUPS, &a_World, a_Digger, a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta, &a_Pickups); } @@ -296,22 +235,7 @@ bool cPluginLua::OnBlockToPickups(cWorld & a_World, cEntity * a_Digger, int a_Bl bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BREWING_COMPLETED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Brewingstand, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BREWING_COMPLETED, &a_World, &a_Brewingstand); } @@ -320,22 +244,7 @@ bool cPluginLua::OnBrewingCompleted(cWorld & a_World, cBrewingstandEntity & a_Br bool cPluginLua::OnBrewingCompleting(cWorld & a_World, cBrewingstandEntity & a_Brewingstand) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_BREWING_COMPLETING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Brewingstand, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_BREWING_COMPLETING, &a_World, &a_Brewingstand); } @@ -344,16 +253,16 @@ bool cPluginLua::OnBrewingCompleting(cWorld & a_World, cBrewingstandEntity & a_B bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHAT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_CHAT]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Message, cLuaState::Return, res, a_Message); + hook->Call(&a_Player, a_Message, cLuaState::Return, res, a_Message); if (res) { return true; @@ -368,22 +277,7 @@ bool cPluginLua::OnChat(cPlayer & a_Player, AString & a_Message) bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_AVAILABLE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_AVAILABLE, &a_World, a_ChunkX, a_ChunkZ); } @@ -392,22 +286,7 @@ bool cPluginLua::OnChunkAvailable(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_GENERATED, &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc); } @@ -416,22 +295,7 @@ bool cPluginLua::OnChunkGenerated(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, cChunkDesc * a_ChunkDesc) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_GENERATING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_GENERATING, &a_World, a_ChunkX, a_ChunkZ, a_ChunkDesc); } @@ -440,22 +304,7 @@ bool cPluginLua::OnChunkGenerating(cWorld & a_World, int a_ChunkX, int a_ChunkZ, bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_UNLOADED, &a_World, a_ChunkX, a_ChunkZ); } @@ -464,22 +313,7 @@ bool cPluginLua::OnChunkUnloaded(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CHUNK_UNLOADING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ChunkX, a_ChunkZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CHUNK_UNLOADING, &a_World, a_ChunkX, a_ChunkZ); } @@ -488,22 +322,7 @@ bool cPluginLua::OnChunkUnloading(cWorld & a_World, int a_ChunkX, int a_ChunkZ) bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_COLLECTING_PICKUP]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Pickup, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_COLLECTING_PICKUP, &a_Player, &a_Pickup); } @@ -512,22 +331,7 @@ bool cPluginLua::OnCollectingPickup(cPlayer & a_Player, cPickup & a_Pickup) bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_CRAFTING_NO_RECIPE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_CRAFTING_NO_RECIPE, &a_Player, &a_Grid, &a_Recipe); } @@ -536,22 +340,7 @@ bool cPluginLua::OnCraftingNoRecipe(cPlayer & a_Player, cCraftingGrid & a_Grid, bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_DISCONNECT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Reason, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_DISCONNECT, &a_Client, a_Reason); } @@ -560,22 +349,7 @@ bool cPluginLua::OnDisconnect(cClientHandle & a_Client, const AString & a_Reason bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_EffectDurationTicks, int a_EffectIntensity, double a_DistanceModifier) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_ADD_EFFECT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_ADD_EFFECT, &a_Entity, a_EffectType, a_EffectDurationTicks, a_EffectIntensity, a_DistanceModifier); } @@ -584,22 +358,7 @@ bool cPluginLua::OnEntityAddEffect(cEntity & a_Entity, int a_EffectType, int a_E bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGING_WORLD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, &a_World, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_CHANGING_WORLD, &a_Entity, &a_World); } @@ -608,22 +367,7 @@ bool cPluginLua::OnEntityChangingWorld(cEntity & a_Entity, cWorld & a_World) bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_CHANGED_WORLD]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, &a_World, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_CHANGED_WORLD, &a_Entity, &a_World); } @@ -632,16 +376,16 @@ bool cPluginLua::OnEntityChangedWorld(cEntity & a_Entity, cWorld & a_World) bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, cPluginManager::CommandResult & a_Result) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXECUTE_COMMAND]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); + hook->Call(a_Player, a_Split, a_EntireCommand, cLuaState::Return, res, a_Result); if (res) { return true; @@ -656,26 +400,26 @@ bool cPluginLua::OnExecuteCommand(cPlayer * a_Player, const AStringVector & a_Sp bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXPLODED]; + for (auto & hook: hooks) { switch (a_Source) { - case esBed: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res); break; - case esEnderCrystal: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res); break; - case esGhastFireball: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res); break; - case esMonster: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; - case esOther: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; - case esPlugin: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; - case esPrimedTNT: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res); break; - case esWitherBirth: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; - case esWitherSkull: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res); break; + case esBed: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res); break; + case esEnderCrystal: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res); break; + case esGhastFireball: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res); break; + case esMonster: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; + case esOther: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; + case esPlugin: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res); break; + case esPrimedTNT: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res); break; + case esWitherBirth: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res); break; + case esWitherSkull: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res); break; case esMax: { ASSERT(!"Invalid explosion source"); @@ -696,26 +440,26 @@ bool cPluginLua::OnExploded(cWorld & a_World, double a_ExplosionSize, bool a_Can bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & a_CanCauseFire, double a_X, double a_Y, double a_Z, eExplosionSource a_Source, void * a_SourceData) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_EXPLODING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_EXPLODING]; + for (auto & hook: hooks) { switch (a_Source) { - case esBed: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esEnderCrystal: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esGhastFireball: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esMonster: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esOther: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esPlugin: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esPrimedTNT: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esWitherBirth: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; - case esWitherSkull: m_LuaState.Call(static_cast<int>(**itr), &a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esBed: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<Vector3i *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esEnderCrystal: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esGhastFireball: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cGhastFireballEntity *>(a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esMonster: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esOther: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPlugin: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esPrimedTNT: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cTNTEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherBirth: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cMonster *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; + case esWitherSkull: hook->Call(&a_World, a_ExplosionSize, a_CanCauseFire, a_X, a_Y, a_Z, a_Source, reinterpret_cast<cWitherSkullEntity *> (a_SourceData), cLuaState::Return, res, a_CanCauseFire, a_ExplosionSize); break; case esMax: { ASSERT(!"Invalid explosion source"); @@ -736,22 +480,7 @@ bool cPluginLua::OnExploding(cWorld & a_World, double & a_ExplosionSize, bool & bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Username) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HANDSHAKE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Username, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HANDSHAKE, &a_Client, a_Username); } @@ -760,22 +489,7 @@ bool cPluginLua::OnHandshake(cClientHandle & a_Client, const AString & a_Usernam bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_DstSlotNum, cBlockEntityWithItems & a_SrcEntity, int a_SrcSlotNum) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PULLING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HOPPER_PULLING_ITEM, &a_World, &a_Hopper, a_DstSlotNum, &a_SrcEntity, a_SrcSlotNum); } @@ -784,22 +498,7 @@ bool cPluginLua::OnHopperPullingItem(cWorld & a_World, cHopperEntity & a_Hopper, bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, int a_SrcSlotNum, cBlockEntityWithItems & a_DstEntity, int a_DstSlotNum) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_HOPPER_PUSHING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_HOPPER_PUSHING_ITEM, &a_World, &a_Hopper, a_SrcSlotNum, &a_DstEntity, a_DstSlotNum); } @@ -808,16 +507,16 @@ bool cPluginLua::OnHopperPushingItem(cWorld & a_World, cHopperEntity & a_Hopper, bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & a_DeathMessage) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_KILLED]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); + hook->Call(&a_Victim, &a_TDI, a_DeathMessage, cLuaState::Return, res, a_DeathMessage); if (res) { return true; @@ -832,22 +531,7 @@ bool cPluginLua::OnKilled(cEntity & a_Victim, TakeDamageInfo & a_TDI, AString & bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInfo & a_TDI) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_KILLING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Victim, a_Killer, &a_TDI, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_KILLING, &a_Victim, a_Killer, &a_TDI); } @@ -856,22 +540,7 @@ bool cPluginLua::OnKilling(cEntity & a_Victim, cEntity * a_Killer, TakeDamageInf bool cPluginLua::OnLogin(cClientHandle & a_Client, UInt32 a_ProtocolVersion, const AString & a_Username) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_LOGIN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_ProtocolVersion, a_Username, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_LOGIN, &a_Client, a_ProtocolVersion, a_Username); } @@ -880,22 +549,7 @@ bool cPluginLua::OnLogin(cClientHandle & a_Client, UInt32 a_ProtocolVersion, con bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_ANIMATION]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Animation, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_ANIMATION, &a_Player, a_Animation); } @@ -904,22 +558,7 @@ bool cPluginLua::OnPlayerAnimation(cPlayer & a_Player, int a_Animation) bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BREAKING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_BREAKING_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta); } @@ -928,22 +567,7 @@ bool cPluginLua::OnPlayerBreakingBlock(cPlayer & a_Player, int a_BlockX, int a_B bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_BROKEN_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_BROKEN_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_BlockType, a_BlockMeta); } @@ -952,22 +576,7 @@ bool cPluginLua::OnPlayerBrokenBlock(cPlayer & a_Player, int a_BlockX, int a_Blo bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_DESTROYED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_DESTROYED, &a_Player); } @@ -976,22 +585,7 @@ bool cPluginLua::OnPlayerDestroyed(cPlayer & a_Player) bool cPluginLua::OnPlayerEating(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_EATING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_EATING, &a_Player); } @@ -1000,22 +594,7 @@ bool cPluginLua::OnPlayerEating(cPlayer & a_Player) bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_NewFoodLevel, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FOOD_LEVEL_CHANGE, &a_Player, a_NewFoodLevel); } @@ -1024,22 +603,7 @@ bool cPluginLua::OnPlayerFoodLevelChange(cPlayer & a_Player, int a_NewFoodLevel) bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_Reward, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FISHED, &a_Player, a_Reward); } @@ -1048,22 +612,7 @@ bool cPluginLua::OnPlayerFished(cPlayer & a_Player, const cItems & a_Reward) bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_FISHING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Reward, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_FISHING, &a_Player, &a_Reward); } @@ -1072,22 +621,7 @@ bool cPluginLua::OnPlayerFishing(cPlayer & a_Player, cItems & a_Reward) bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_JOINED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_JOINED, &a_Player); } @@ -1096,22 +630,7 @@ bool cPluginLua::OnPlayerJoined(cPlayer & a_Player) bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_LEFT_CLICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_LEFT_CLICK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status); } @@ -1120,22 +639,7 @@ bool cPluginLua::OnPlayerLeftClick(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_MOVING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_OldPosition, a_NewPosition, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_MOVING, &a_Player, a_OldPosition, a_NewPosition); } @@ -1144,22 +648,7 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_ENTITY_TELEPORT]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Entity, a_OldPosition, a_NewPosition, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_ENTITY_TELEPORT, &a_Entity, a_OldPosition, a_NewPosition); } @@ -1168,27 +657,11 @@ bool cPluginLua::OnEntityTeleport(cEntity & a_Entity, const Vector3d & a_OldPosi bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, - a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), - a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, - cLuaState::Return, - res - ); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_PLACED_BLOCK, + &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta + ); } @@ -1197,27 +670,11 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_Blo bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, - a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), - a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta, - cLuaState::Return, - res - ); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_PLACING_BLOCK, + &a_Player, + a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(), + a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta + ); } @@ -1226,22 +683,7 @@ bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_Bl bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_RIGHT_CLICK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1250,22 +692,7 @@ bool cPluginLua::OnPlayerRightClick(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_RIGHT_CLICKING_ENTITY, &a_Player, &a_Entity); } @@ -1274,22 +701,7 @@ bool cPluginLua::OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Ent bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SHOOTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SHOOTING, &a_Player); } @@ -1298,22 +710,7 @@ bool cPluginLua::OnPlayerShooting(cPlayer & a_Player) bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_SPAWNED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_SPAWNED, &a_Player); } @@ -1322,22 +719,7 @@ bool cPluginLua::OnPlayerSpawned(cPlayer & a_Player) bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_TOSSING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_TOSSING_ITEM, &a_Player); } @@ -1346,22 +728,7 @@ bool cPluginLua::OnPlayerTossingItem(cPlayer & a_Player) bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USED_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta); } @@ -1370,22 +737,7 @@ bool cPluginLua::OnPlayerUsedBlock(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USED_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USED_ITEM, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1394,22 +746,7 @@ bool cPluginLua::OnPlayerUsedItem(cPlayer & a_Player, int a_BlockX, int a_BlockY bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USING_BLOCK, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta); } @@ -1418,22 +755,7 @@ bool cPluginLua::OnPlayerUsingBlock(cPlayer & a_Player, int a_BlockX, int a_Bloc bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_USING_ITEM]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLAYER_USING_ITEM, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); } @@ -1442,22 +764,7 @@ bool cPluginLua::OnPlayerUsingItem(cPlayer & a_Player, int a_BlockX, int a_Block bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Channel, const AString & a_Message) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGIN_MESSAGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Client, a_Channel, a_Message, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PLUGIN_MESSAGE, &a_Client, a_Channel, a_Message); } @@ -1466,17 +773,17 @@ bool cPluginLua::OnPluginMessage(cClientHandle & a_Client, const AString & a_Cha bool cPluginLua::OnPluginsLoaded(void) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_PLUGINS_LOADED]; + for (auto & hook: hooks) { bool ret = false; - m_LuaState.Call(static_cast<int>(**itr), cLuaState::Return, ret); + hook->Call(cLuaState::Return, ret); res = res || ret; } return res; @@ -1488,22 +795,7 @@ bool cPluginLua::OnPluginsLoaded(void) bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_POST_CRAFTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_POST_CRAFTING, &a_Player, &a_Grid, &a_Recipe); } @@ -1512,22 +804,7 @@ bool cPluginLua::OnPostCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCra bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraftingRecipe & a_Recipe) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PRE_CRAFTING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Player, &a_Grid, &a_Recipe, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PRE_CRAFTING, &a_Player, &a_Grid, &a_Recipe); } @@ -1536,22 +813,7 @@ bool cPluginLua::OnPreCrafting(cPlayer & a_Player, cCraftingGrid & a_Grid, cCraf bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Face, const Vector3d & a_BlockHitPos) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_BLOCK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PROJECTILE_HIT_BLOCK, &a_Projectile, a_BlockX, a_BlockY, a_BlockZ, a_Face, a_BlockHitPos); } @@ -1560,22 +822,7 @@ bool cPluginLua::OnProjectileHitBlock(cProjectileEntity & a_Projectile, int a_Bl bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity & a_HitEntity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PROJECTILE_HIT_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Projectile, &a_HitEntity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_PROJECTILE_HIT_ENTITY, &a_Projectile, &a_HitEntity); } @@ -1584,16 +831,16 @@ bool cPluginLua::OnProjectileHitEntity(cProjectileEntity & a_Projectile, cEntity bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_ServerDescription, int & a_OnlinePlayersCount, int & a_MaxPlayersCount, AString & a_Favicon) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SERVER_PING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_SERVER_PING]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon, cLuaState::Return, res, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon); + hook->Call(&a_ClientHandle, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon, cLuaState::Return, res, a_ServerDescription, a_OnlinePlayersCount, a_MaxPlayersCount, a_Favicon); if (res) { return true; @@ -1608,22 +855,7 @@ bool cPluginLua::OnServerPing(cClientHandle & a_ClientHandle, AString & a_Server bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNED_ENTITY, &a_World, &a_Entity); } @@ -1632,22 +864,7 @@ bool cPluginLua::OnSpawnedEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNED_MONSTER]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Monster, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNED_MONSTER, &a_World, &a_Monster); } @@ -1656,22 +873,7 @@ bool cPluginLua::OnSpawnedMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_ENTITY]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Entity, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNING_ENTITY, &a_World, &a_Entity); } @@ -1680,22 +882,7 @@ bool cPluginLua::OnSpawningEntity(cWorld & a_World, cEntity & a_Entity) bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_SPAWNING_MONSTER]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, &a_Monster, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_SPAWNING_MONSTER, &a_World, &a_Monster); } @@ -1704,22 +891,7 @@ bool cPluginLua::OnSpawningMonster(cWorld & a_World, cMonster & a_Monster) bool cPluginLua::OnTakeDamage(cEntity & a_Receiver, TakeDamageInfo & a_TDI) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_TAKE_DAMAGE]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_Receiver, &a_TDI, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_TAKE_DAMAGE, &a_Receiver, &a_TDI); } @@ -1733,22 +905,7 @@ bool cPluginLua::OnUpdatedSign( cPlayer * a_Player ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATED_SIGN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_UPDATED_SIGN, &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player); } @@ -1762,16 +919,16 @@ bool cPluginLua::OnUpdatingSign( cPlayer * a_Player ) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_UPDATING_SIGN]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); + hook->Call(&a_World, a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, a_Player, cLuaState::Return, res, a_Line1, a_Line2, a_Line3, a_Line4); if (res) { return true; @@ -1786,22 +943,7 @@ bool cPluginLua::OnUpdatingSign( bool cPluginLua::OnWeatherChanged(cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, cLuaState::Return, res); - if (res) - { - return true; - } - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_WEATHER_CHANGED, &a_World); } @@ -1810,16 +952,16 @@ bool cPluginLua::OnWeatherChanged(cWorld & a_World) bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) + cOperation op(*this); + if (!op().IsValid()) { return false; } bool res = false; - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) + auto & hooks = m_HookMap[cPluginManager::HOOK_WEATHER_CHANGING]; + for (auto & hook: hooks) { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); + hook->Call(&a_World, a_NewWeather, cLuaState::Return, res, a_NewWeather); if (res) { return true; @@ -1834,17 +976,7 @@ bool cPluginLua::OnWeatherChanging(cWorld & a_World, eWeather & a_NewWeather) bool cPluginLua::OnWorldStarted(cWorld & a_World) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_STARTED]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World); - } - return false; + return CallSimpleHooks(cPluginManager::HOOK_WORLD_STARTED, &a_World); } @@ -1853,102 +985,7 @@ bool cPluginLua::OnWorldStarted(cWorld & a_World) bool cPluginLua::OnWorldTick(cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) { - cCSLock Lock(m_CriticalSection); - if (!m_LuaState.IsValid()) - { - return false; - } - cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_WORLD_TICK]; - for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr) - { - m_LuaState.Call(static_cast<int>(**itr), &a_World, a_Dt, a_LastTickDurationMSec); - } - return false; -} - - - - - -bool cPluginLua::HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) -{ - ASSERT(!a_Split.empty()); - CommandMap::iterator cmd = m_Commands.find(a_Split[0]); - if (cmd == m_Commands.end()) - { - LOGWARNING("Command handler is registered in cPluginManager but not in cPlugin, wtf? Command \"%s\".", a_Split[0].c_str()); - return false; - } - - cCSLock Lock(m_CriticalSection); - bool res = false; - m_LuaState.Call(cmd->second, a_Split, &a_Player, a_FullCommand, cLuaState::Return, res); - return res; -} - - - - - -bool cPluginLua::HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) -{ - 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\", plugin \"%s\".", - a_Split[0].c_str(), GetName().c_str() - ); - return false; - } - - cCSLock Lock(m_CriticalSection); - bool res = false; - AString str; - m_LuaState.Call(cmd->second, a_Split, a_FullCommand, cLuaState::Return, res, str); - if (res && !str.empty()) - { - a_Output.Out(str); - } - return res; -} - - - - - -void cPluginLua::ClearCommands(void) -{ - cCSLock Lock(m_CriticalSection); - - // Unreference the bound functions so that Lua can GC them - if (m_LuaState != nullptr) - { - for (CommandMap::iterator itr = m_Commands.begin(), end = m_Commands.end(); itr != end; ++itr) - { - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, itr->second); - } - } - m_Commands.clear(); -} - - - - - -void cPluginLua::ClearConsoleCommands(void) -{ - cCSLock Lock(m_CriticalSection); - - // Unreference the bound functions so that Lua can GC them - if (m_LuaState != nullptr) - { - 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(); + return CallSimpleHooks(cPluginManager::HOOK_WORLD_TICK, &a_World, a_Dt, a_LastTickDurationMSec); } @@ -2059,22 +1096,9 @@ const char * cPluginLua::GetHookFnName(int a_HookType) -bool cPluginLua::AddHookRef(int a_HookType, int a_FnRefIdx) +bool cPluginLua::AddHookCallback(int a_HookType, cLuaState::cCallbackPtr && a_Callback) { - ASSERT(m_CriticalSection.IsLockedByCurrentThread()); // It probably has to be, how else would we have a LuaState? - - // Check if the function reference is valid: - cLuaState::cRef * Ref = new cLuaState::cRef(m_LuaState, a_FnRefIdx); - if ((Ref == nullptr) || !Ref->IsValid()) - { - LOGWARNING("Plugin %s tried to add a hook %d with bad handler function.", GetName().c_str(), a_HookType); - m_LuaState.LogStackTrace(); - delete Ref; - Ref = nullptr; - return false; - } - - m_HookMap[a_HookType].push_back(Ref); + m_HookMap[a_HookType].push_back(std::move(a_Callback)); return true; } @@ -2089,10 +1113,10 @@ int cPluginLua::CallFunctionFromForeignState( int a_ParamEnd ) { - cCSLock Lock(m_CriticalSection); + cOperation op(*this); // Call the function: - int NumReturns = m_LuaState.CallFunctionWithForeignParams(a_FunctionName, a_ForeignState, a_ParamStart, a_ParamEnd); + int NumReturns = op().CallFunctionWithForeignParams(a_FunctionName, a_ForeignState, a_ParamStart, a_ParamEnd); if (NumReturns < 0) { // The call has failed, an error has already been output to the log, so just silently bail out with the same error @@ -2116,133 +1140,13 @@ int cPluginLua::CallFunctionFromForeignState( -void cPluginLua::AddResettable(cPluginLua::cResettablePtr a_Resettable) -{ - cCSLock Lock(m_CriticalSection); - m_Resettables.push_back(a_Resettable); -} - - - - - -AString cPluginLua::HandleWebRequest(const HTTPRequest & a_Request) -{ - // Find the tab to use for the request: - auto TabName = GetTabNameForRequest(a_Request); - AString SafeTabTitle = TabName.second; - if (SafeTabTitle.empty()) - { - return ""; - } - auto Tab = GetTabBySafeTitle(SafeTabTitle); - if (Tab == nullptr) - { - return ""; - } - - // Get the page content from the plugin: - cCSLock Lock(m_CriticalSection); - AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->m_Title.c_str()); - if (!m_LuaState.Call(Tab->m_UserData, &a_Request, cLuaState::Return, Contents)) - { - return "Lua encountered error while processing the page request"; - } - return Contents; -} - - - - - -bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference) +void cPluginLua::ClearWebTabs(void) { - cCSLock Lock(m_CriticalSection); - if (a_LuaState != m_LuaState) + auto webAdmin = cRoot::Get()->GetWebAdmin(); + if (webAdmin != nullptr) // can be nullptr when shutting down the server { - LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!"); - return false; + webAdmin->RemoveAllPluginWebTabs(m_Name); } - AddNewWebTab(a_Title, a_FunctionReference); - return true; -} - - - - - -void cPluginLua::BindCommand(const AString & a_Command, int a_FnRef) -{ - ASSERT(m_Commands.find(a_Command) == m_Commands.end()); - m_Commands[a_Command] = a_FnRef; -} - - - - - -void cPluginLua::BindConsoleCommand(const AString & a_Command, int a_FnRef) -{ - ASSERT(m_ConsoleCommands.find(a_Command) == m_ConsoleCommands.end()); - m_ConsoleCommands[a_Command] = a_FnRef; -} - - - - - -void cPluginLua::Unreference(int a_LuaRef) -{ - cCSLock Lock(m_CriticalSection); - luaL_unref(m_LuaState, LUA_REGISTRYINDEX, a_LuaRef); -} - - - - - -bool cPluginLua::CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse) -{ - ASSERT(a_FnRef != LUA_REFNIL); - - cCSLock Lock(m_CriticalSection); - bool res = false; - m_LuaState.Call(a_FnRef, &a_Window, &a_Player, a_CanRefuse, cLuaState::Return, res); - return res; -} - - - - - -void cPluginLua::CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum) -{ - ASSERT(a_FnRef != LUA_REFNIL); - - cCSLock Lock(m_CriticalSection); - m_LuaState.Call(a_FnRef, &a_Window, a_SlotNum); -} - - - - - -//////////////////////////////////////////////////////////////////////////////// -// cPluginLua::cResettable: - -cPluginLua::cResettable::cResettable(cPluginLua & a_Plugin): - m_Plugin(&a_Plugin) -{ -} - - - - - -void cPluginLua::cResettable::Reset(void) -{ - cCSLock Lock(m_CSPlugin); - m_Plugin = nullptr; } diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index db6612671..dc3c91880 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -10,7 +10,6 @@ #pragma once #include "Plugin.h" -#include "WebPlugin.h" #include "LuaState.h" // Names for the global variables through which the plugin is identified in its LuaState @@ -29,8 +28,7 @@ class cWindow; // tolua_begin class cPluginLua : - public cPlugin, - public cWebPlugin + public cPlugin { typedef cPlugin super; @@ -49,7 +47,7 @@ public: public: cOperation(cPluginLua & a_Plugin) : m_Plugin(a_Plugin), - m_Lock(a_Plugin.m_CriticalSection) + m_Lock(a_Plugin.m_LuaState) { } @@ -58,42 +56,12 @@ public: protected: cPluginLua & m_Plugin; - /** RAII lock for m_Plugin.m_CriticalSection */ - cCSLock m_Lock; + /** RAII lock for the Lua state. */ + cLuaState::cLock m_Lock; } ; - /** A base class that represents something related to a plugin - The plugin can reset this class so that the instance can continue to exist but will not engage the (possibly non-existent) plugin anymore. - This is used for scheduled tasks etc., so that they can be queued and reset when the plugin is terminated, without removing them from the queue. */ - class cResettable - { - public: - /** Creates a new instance bound to the specified plugin. */ - cResettable(cPluginLua & a_Plugin); - - // Force a virtual destructor in descendants: - virtual ~cResettable() {} - - /** Resets the plugin instance stored within. - The instance will continue to exist, but should not call into the plugin anymore. */ - virtual void Reset(void); - - protected: - /** The plugin that this instance references. - If nullptr, the plugin has already unloaded and the instance should bail out any processing. - Protected against multithreaded access by m_CSPlugin. */ - cPluginLua * m_Plugin; - - /** The mutex protecting m_Plugin against multithreaded access. */ - cCriticalSection m_CSPlugin; - }; - - typedef SharedPtr<cResettable> cResettablePtr; - typedef std::vector<cResettablePtr> cResettablePtrs; - - cPluginLua(const AString & a_PluginDirectory); ~cPluginLua(); @@ -170,52 +138,15 @@ public: virtual bool OnWorldStarted (cWorld & a_World) override; virtual bool OnWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec) override; - virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) override; - - virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) override; - - virtual void ClearCommands(void) override; - - virtual void ClearConsoleCommands(void) override; - /** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */ bool CanAddOldStyleHook(int a_HookType); - // cWebPlugin overrides - virtual const AString GetWebTitle(void) const override {return GetName(); } - virtual AString HandleWebRequest(const HTTPRequest & a_Request) override; - - /** Adds a new web tab to webadmin. - Displaying the tab calls the referenced function. */ - bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // Exported in ManualBindings.cpp - - /** 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); - - cLuaState & GetLuaState(void) { return m_LuaState; } - - cCriticalSection & GetCriticalSection(void) { return m_CriticalSection; } - - /** Removes a previously referenced object (luaL_unref()) */ - void Unreference(int a_LuaRef); - - /** Calls the plugin-specified "cLuaWindow closing" callback. Returns true only if the callback returned true */ - bool CallbackWindowClosing(int a_FnRef, cWindow & a_Window, cPlayer & a_Player, bool a_CanRefuse); - - /** Calls the plugin-specified "cLuaWindow slot changed" callback. */ - void CallbackWindowSlotChanged(int a_FnRef, cWindow & a_Window, int a_SlotNum); - /** Returns the name of Lua function that should handle the specified hook type in the older (#121) API */ static const char * GetHookFnName(int a_HookType); - /** Adds a Lua function to be called for the specified hook. - The function has to be on the Lua stack at the specified index a_FnRefIdx - Returns true if the hook was added successfully. - */ - bool AddHookRef(int a_HookType, int a_FnRefIdx); + /** Adds a Lua callback to be called for the specified hook. + Returns true if the hook was added successfully. */ + bool AddHookCallback(int a_HookType, cLuaState::cCallbackPtr && a_Callback); /** Calls a function in this plugin's LuaState with parameters copied over from a_ForeignState. The values that the function returns are placed onto a_ForeignState. @@ -231,45 +162,50 @@ public: template <typename FnT, typename... Args> bool Call(FnT a_Fn, Args && ... a_Args) { - cCSLock Lock(m_CriticalSection); - return m_LuaState.Call(a_Fn, a_Args...); + return cOperation(*this)().Call(a_Fn, a_Args...); } - /** Adds the specified cResettable instance to m_Resettables, so that it is notified when the plugin is being closed. */ - void AddResettable(cResettablePtr a_Resettable); - protected: - /** Maps command name into Lua function reference */ - typedef std::map<AString, int> CommandMap; - /** Provides an array of Lua function references */ - typedef std::vector<cLuaState::cRef *> cLuaRefs; + typedef std::vector<cLuaState::cCallbackPtr> cLuaCallbacks; /** Maps hook types into arrays of Lua function references to call for each hook type */ - typedef std::map<int, cLuaRefs> cHookMap; - + typedef std::map<int, cLuaCallbacks> cHookMap; - /** The mutex protecting m_LuaState and each of the m_Resettables[] against multithreaded use. */ - cCriticalSection m_CriticalSection; /** The plugin's Lua state. */ cLuaState m_LuaState; - /** Objects that need notification when the plugin is about to be unloaded. */ - cResettablePtrs m_Resettables; - - /** In-game commands that the plugin has registered. */ - CommandMap m_Commands; - - /** Console commands that the plugin has registered. */ - CommandMap m_ConsoleCommands; - /** Hooks that the plugin has registered. */ cHookMap m_HookMap; /** Releases all Lua references, notifies and removes all m_Resettables[] and closes the m_LuaState. */ void Close(void); + + /** Removes all WebTabs currently registered for this plugin from the WebAdmin. */ + void ClearWebTabs(void); + + /** Calls a hook that has the simple format - single bool return value specifying whether the chain should continue. + The advanced hook types that need more processing implement a similar loop manually instead. + Returns true if any of hook calls wants to abort the hook (returned true), false if all hook calls returned false. */ + template <typename... Args> + bool CallSimpleHooks(int a_HookType, Args && ... a_Args) + { + cOperation op(*this); + auto & hooks = m_HookMap[a_HookType]; + bool res = false; + for (auto & hook: hooks) + { + hook->Call(std::forward<Args>(a_Args)..., cLuaState::Return, res); + if (res) + { + // Hook wants to terminate the chain processing + return true; + } + } + return false; + } } ; // tolua_export diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 5b3ef7803..19d2e8b4d 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -1569,9 +1569,9 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player, return crNoPermission; } - ASSERT(cmd->second.m_Plugin != nullptr); + ASSERT(cmd->second.m_Handler != nullptr); - if (!cmd->second.m_Plugin->HandleCommand(Split, a_Player, a_Command)) + if (!cmd->second.m_Handler->ExecuteCommand(Split, &a_Player, a_Command, nullptr)) { return crError; } @@ -1654,11 +1654,6 @@ void cPluginManager::RemoveHooks(cPlugin * a_Plugin) void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin) { - if (a_Plugin != nullptr) - { - a_Plugin->ClearCommands(); - } - for (CommandMap::iterator itr = m_Commands.begin(); itr != m_Commands.end();) { if (itr->second.m_Plugin == a_Plugin) @@ -1694,7 +1689,13 @@ bool cPluginManager::IsPluginLoaded(const AString & a_PluginName) -bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) +bool cPluginManager::BindCommand( + const AString & a_Command, + cPlugin * a_Plugin, + cCommandHandlerPtr a_Handler, + const AString & a_Permission, + const AString & a_HelpString +) { CommandMap::iterator cmd = m_Commands.find(a_Command); if (cmd != m_Commands.end()) @@ -1703,9 +1704,11 @@ bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, return false; } - m_Commands[a_Command].m_Plugin = a_Plugin; - m_Commands[a_Command].m_Permission = a_Permission; - m_Commands[a_Command].m_HelpString = a_HelpString; + auto & reg = m_Commands[a_Command]; + reg.m_Plugin = a_Plugin; + reg.m_Handler = a_Handler; + reg.m_Permission = a_Permission; + reg.m_HelpString = a_HelpString; return true; } @@ -1768,11 +1771,6 @@ cPluginManager::CommandResult cPluginManager::ForceExecuteCommand(cPlayer & a_Pl void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin) { - if (a_Plugin != nullptr) - { - a_Plugin->ClearConsoleCommands(); - } - for (CommandMap::iterator itr = m_ConsoleCommands.begin(); itr != m_ConsoleCommands.end();) { if (itr->second.m_Plugin == a_Plugin) @@ -1792,7 +1790,12 @@ void cPluginManager::RemovePluginConsoleCommands(cPlugin * a_Plugin) -bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_HelpString) +bool cPluginManager::BindConsoleCommand( + const AString & a_Command, + cPlugin * a_Plugin, + cCommandHandlerPtr a_Handler, + const AString & a_HelpString +) { CommandMap::iterator cmd = m_ConsoleCommands.find(a_Command); if (cmd != m_ConsoleCommands.end()) @@ -1808,9 +1811,11 @@ bool cPluginManager::BindConsoleCommand(const AString & a_Command, cPlugin * a_P return false; } - m_ConsoleCommands[a_Command].m_Plugin = a_Plugin; - m_ConsoleCommands[a_Command].m_Permission = ""; - m_ConsoleCommands[a_Command].m_HelpString = a_HelpString; + auto & reg = m_ConsoleCommands[a_Command]; + reg.m_Plugin = a_Plugin; + reg.m_Handler = a_Handler; + reg.m_Permission = ""; + reg.m_HelpString = a_HelpString; return true; } @@ -1873,7 +1878,7 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma return (res == crExecuted); } - return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command); + return cmd->second.m_Handler->ExecuteCommand(a_Split, nullptr, a_Command, &a_Output); } @@ -1965,6 +1970,23 @@ bool cPluginManager::ForEachPlugin(cPluginCallback & a_Callback) +AString cPluginManager::GetPluginFolderName(const AString & a_PluginName) +{ + // TODO: Implement locking for plugins + for (auto & plugin: m_Plugins) + { + if (plugin->GetName() == a_PluginName) + { + return plugin->GetFolderName(); + } + } + return AString(); +} + + + + + void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook) { if (a_Plugin == nullptr) diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index f3f0b6d0b..0423d6af1 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -152,6 +152,7 @@ public: HOOK_MAX = HOOK_NUM_HOOKS - 1, } ; // tolua_export + /** Used as a callback for enumerating bound commands */ class cCommandEnumCallback { @@ -164,6 +165,30 @@ public: virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) = 0; } ; + + /** Interface that must be provided by any class that implements a command handler, either in-game or console command. */ + class cCommandHandler + { + public: + // Force a virtual destructor in descendants + virtual ~cCommandHandler() {} + + /** Executes the specified in-game command. + a_Split is the command string, split at the spaces. + a_Player is the player executing the command, nullptr in case of the console. + a_Command is the entire command string. + a_Output is the sink into which the additional text returned by the command handler should be sent; only used for console commands. */ + virtual bool ExecuteCommand( + const AStringVector & a_Split, + cPlayer * a_Player, + const AString & a_Command, + cCommandOutputCallback * a_Output = nullptr + ) = 0; + }; + + typedef SharedPtr<cCommandHandler> cCommandHandlerPtr; + + /** The interface used for enumerating and extern-calling plugins */ typedef cItemCallback<cPlugin> cPluginCallback; @@ -281,8 +306,16 @@ public: /** Returns true if the specified plugin is loaded. */ bool IsPluginLoaded(const AString & a_PluginName); // tolua_export - /** Binds a command to the specified plugin. Returns true if successful, false if command already bound. */ - bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param + /** Binds a command to the specified handler. + Returns true if successful, false if command already bound. + Exported in ManualBindings.cpp. */ + bool BindCommand( + const AString & a_Command, + cPlugin * a_Plugin, + cCommandHandlerPtr a_Handler, + const AString & a_Permission, + const AString & a_HelpString + ); /** Calls a_Callback for each bound command, returns true if all commands were enumerated */ bool ForEachCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp @@ -302,8 +335,15 @@ public: /** 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 + /** Binds a console command to the specified handler. + Returns true if successful, false if command already bound. + Exported in ManualBindings.cpp. */ + bool BindConsoleCommand( + const AString & a_Command, + cPlugin * a_Plugin, + cCommandHandlerPtr a_Handler, + const AString & a_HelpString + ); /** Calls a_Callback for each bound console command, returns true if all commands were enumerated */ bool ForEachConsoleCommand(cCommandEnumCallback & a_Callback); // Exported in ManualBindings.cpp @@ -332,6 +372,9 @@ public: Returns true if all plugins have been reported, false if the callback has aborted the enumeration by returning true. */ bool ForEachPlugin(cPluginCallback & a_Callback); + /** Returns the name of the folder (cPlugin::GetFolderName()) from which the specified plugin was loaded. */ + AString GetPluginFolderName(const AString & a_PluginName); // tolua_export + /** Returns the path where individual plugins' folders are expected. The path doesn't end in a slash. */ static AString GetPluginsPath(void) { return FILE_IO_PREFIX + AString("Plugins"); } // tolua_export @@ -345,6 +388,7 @@ private: cPlugin * m_Plugin; AString m_Permission; // Not used for console commands AString m_HelpString; + cCommandHandlerPtr m_Handler; } ; typedef std::map<int, cPluginManager::PluginList> HookMap; diff --git a/src/Bindings/WebPlugin.cpp b/src/Bindings/WebPlugin.cpp deleted file mode 100644 index 1eca7de93..000000000 --- a/src/Bindings/WebPlugin.cpp +++ /dev/null @@ -1,152 +0,0 @@ - -#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules - -#include "WebPlugin.h" -#include "../WebAdmin.h" -#include "../Root.h" - - - - - -cWebPlugin::cWebPlugin() -{ - cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); - if (WebAdmin != nullptr) - { - WebAdmin->AddPlugin(this); - } -} - - - - - -cWebPlugin::~cWebPlugin() -{ - ASSERT(m_Tabs.empty()); // Has ClearTabs() been called? - - // Remove from WebAdmin: - cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin(); - if (WebAdmin != nullptr) - { - WebAdmin->RemovePlugin(this); - } -} - - - - - -cWebPlugin::cTabNames cWebPlugin::GetTabNames(void) const -{ - std::list< std::pair<AString, AString>> NameList; - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - NameList.push_back(std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle)); - } - return NameList; -} - - - - - -cWebPlugin::cTabPtr cWebPlugin::GetTabBySafeTitle(const AString & a_SafeTitle) const -{ - cCSLock Lock(m_CSTabs); - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - if ((*itr)->m_SafeTitle == a_SafeTitle) - { - return *itr; - } - } - return nullptr; -} - - - - - -std::pair<AString, AString> cWebPlugin::GetTabNameForRequest(const HTTPRequest & a_Request) -{ - AStringVector Split = StringSplit(a_Request.Path, "/"); - if (Split.empty()) - { - return std::make_pair(AString(), AString()); - } - - cCSLock Lock(m_CSTabs); - cTabPtr Tab; - if (Split.size() > 2) // If we got the tab name, show that page - { - for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr) - { - if ((*itr)->m_SafeTitle.compare(Split[2]) == 0) // This is the one! - { - return std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle); - } - } - // Tab name not found, display an "empty" page: - return std::make_pair(AString(), AString()); - } - - // Show the first tab: - if (!m_Tabs.empty()) - { - return std::make_pair(m_Tabs.front()->m_SafeTitle, m_Tabs.front()->m_SafeTitle); - } - - // No tabs at all: - return std::make_pair(AString(), AString()); -} - - - - -AString cWebPlugin::SafeString(const AString & a_String) -{ - AString RetVal; - auto len = a_String.size(); - RetVal.reserve(len); - for (size_t i = 0; i < len; ++i) - { - char c = a_String[i]; - if (c == ' ') - { - c = '_'; - } - RetVal.push_back(c); - } - return RetVal; -} - - - - - -void cWebPlugin::AddNewWebTab(const AString & a_Title, int a_UserData) -{ - auto Tab = std::make_shared<cTab>(a_Title, a_UserData); - cCSLock Lock(m_CSTabs); - m_Tabs.push_back(Tab); -} - - - - - -void cWebPlugin::ClearTabs(void) -{ - // Remove the webadmin tabs: - cTabPtrs Tabs; - { - cCSLock Lock(m_CSTabs); - std::swap(Tabs, m_Tabs); - } -} - - - - diff --git a/src/Bindings/WebPlugin.h b/src/Bindings/WebPlugin.h deleted file mode 100644 index 6dc8db801..000000000 --- a/src/Bindings/WebPlugin.h +++ /dev/null @@ -1,80 +0,0 @@ - -#pragma once - -struct HTTPRequest; - - - - - -// tolua_begin -class cWebPlugin -{ -public: - // tolua_end - - struct cTab - { - AString m_Title; - AString m_SafeTitle; - int m_UserData; - - cTab(const AString & a_Title, int a_UserData): - m_Title(a_Title), - m_SafeTitle(cWebPlugin::SafeString(a_Title)), - m_UserData(a_UserData) - { - } - }; - - typedef SharedPtr<cTab> cTabPtr; - typedef std::list<cTabPtr> cTabPtrs; - typedef std::list<std::pair<AString, AString>> cTabNames; - - - cWebPlugin(); - - virtual ~cWebPlugin(); - - // tolua_begin - - /** Returns the title of the plugin, as it should be presented in the webadmin's pages tree. */ - virtual const AString GetWebTitle(void) const = 0; - - /** Sanitizes the input string, replacing spaces with underscores. */ - static AString SafeString(const AString & a_String); - - // tolua_end - - virtual AString HandleWebRequest(const HTTPRequest & a_Request) = 0; - - /** Adds a new web tab with the specified contents. */ - void AddNewWebTab(const AString & a_Title, int a_UserData); - - /** Removes all the tabs. */ - void ClearTabs(void); - - /** Returns all the tabs that this plugin has registered. */ - const cTabPtrs & GetTabs(void) const { return m_Tabs; } - - /** Returns all of the tabs that this plugin has registered. */ - cTabNames GetTabNames(void) const; // Exported in ManualBindings.cpp - - /** Returns the tab that has the specified SafeTitle. - Returns nullptr if no such tab. */ - cTabPtr GetTabBySafeTitle(const AString & a_SafeTitle) const; - - std::pair<AString, AString> GetTabNameForRequest(const HTTPRequest & a_Request); - -private: - /** All tabs that this plugin has registered. - Protected against multithreaded access by m_CSTabs. */ - cTabPtrs m_Tabs; - - /** Protects m_Tabs against multithreaded access. */ - mutable cCriticalSection m_CSTabs; -}; // tolua_export - - - - diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 2f1e892dc..f6e9da45e 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -224,11 +224,11 @@ public: cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export const cWindow * GetWindow(void) const { return m_CurrentWindow; } - /** Opens the specified window; closes the current one first using CloseWindow() */ - void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp - // tolua_begin + /** Opens the specified window; closes the current one first using CloseWindow() */ + void OpenWindow(cWindow * a_Window); + /** Closes the current window, resets current window to m_InventoryWindow. A plugin may refuse the closing if a_CanRefuse is true */ void CloseWindow(bool a_CanRefuse = true); diff --git a/src/Server.cpp b/src/Server.cpp index 5548e77d1..8405109de 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -607,18 +607,35 @@ void cServer::PrintHelp(const AStringVector & a_Split, cCommandOutputCallback & void cServer::BindBuiltInConsoleCommands(void) { + // Create an empty handler - the actual handling for the commands is performed before they are handed off to cPluginManager + class cEmptyHandler: + public cPluginManager::cCommandHandler + { + virtual bool ExecuteCommand( + const AStringVector & a_Split, + cPlayer * a_Player, + const AString & a_Command, + cCommandOutputCallback * a_Output = nullptr + ) override + { + return false; + } + }; + auto handler = std::make_shared<cEmptyHandler>(); + + // Register internal commands: cPluginManager * PlgMgr = cPluginManager::Get(); - PlgMgr->BindConsoleCommand("help", nullptr, "Shows the available commands"); - PlgMgr->BindConsoleCommand("reload", nullptr, "Reloads all plugins"); - PlgMgr->BindConsoleCommand("restart", nullptr, "Restarts the server cleanly"); - PlgMgr->BindConsoleCommand("stop", nullptr, "Stops the server cleanly"); - PlgMgr->BindConsoleCommand("chunkstats", nullptr, "Displays detailed chunk memory statistics"); - PlgMgr->BindConsoleCommand("load <pluginname>", nullptr, "Adds and enables the specified plugin"); - PlgMgr->BindConsoleCommand("unload <pluginname>", nullptr, "Disables the specified plugin"); - PlgMgr->BindConsoleCommand("destroyentities", nullptr, "Destroys all entities in all worlds"); + PlgMgr->BindConsoleCommand("help", nullptr, handler, "Shows the available commands"); + PlgMgr->BindConsoleCommand("reload", nullptr, handler, "Reloads all plugins"); + PlgMgr->BindConsoleCommand("restart", nullptr, handler, "Restarts the server cleanly"); + PlgMgr->BindConsoleCommand("stop", nullptr, handler, "Stops the server cleanly"); + PlgMgr->BindConsoleCommand("chunkstats", nullptr, handler, "Displays detailed chunk memory statistics"); + PlgMgr->BindConsoleCommand("load", nullptr, handler, "Adds and enables the specified plugin"); + PlgMgr->BindConsoleCommand("unload", nullptr, handler, "Disables the specified plugin"); + PlgMgr->BindConsoleCommand("destroyentities", nullptr, handler, "Destroys all entities in all worlds"); #if defined(_MSC_VER) && defined(_DEBUG) && defined(ENABLE_LEAK_FINDER) - PlgMgr->BindConsoleCommand("dumpmem", nullptr, " - Dumps all used memory blocks together with their callstacks into memdump.xml"); + PlgMgr->BindConsoleCommand("dumpmem", nullptr, handler, " - Dumps all used memory blocks together with their callstacks into memdump.xml"); #endif } diff --git a/src/UI/Window.cpp b/src/UI/Window.cpp index 5a2e55feb..4582d6cf4 100644 --- a/src/UI/Window.cpp +++ b/src/UI/Window.cpp @@ -307,7 +307,7 @@ bool cWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse) // Checks whether the player is still holding an item if (!a_Player.GetDraggingItem().IsEmpty()) { - LOGD("Player holds item! Dropping it..."); + LOGD("Player is holding an item while closing their window, dropping it as a pickup..."); a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount); } diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp index e43d749dd..5c08deb0d 100644 --- a/src/WebAdmin.cpp +++ b/src/WebAdmin.cpp @@ -2,10 +2,6 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "WebAdmin.h" -#include "Bindings/WebPlugin.h" - -#include "Bindings/PluginManager.h" -#include "Bindings/Plugin.h" #include "World.h" #include "Entities/Player.h" @@ -87,9 +83,9 @@ public: // cWebAdmin: cWebAdmin::cWebAdmin(void) : + m_TemplateScript("<webadmin_template>"), m_IsInitialized(false), - m_IsRunning(false), - m_TemplateScript("<webadmin_template>") + m_IsRunning(false) { } @@ -106,40 +102,9 @@ cWebAdmin::~cWebAdmin() -void cWebAdmin::AddPlugin(cWebPlugin * a_Plugin) -{ - m_Plugins.remove(a_Plugin); - m_Plugins.push_back(a_Plugin); -} - - - - - -void cWebAdmin::RemovePlugin(cWebPlugin * a_Plugin) -{ - m_Plugins.remove(a_Plugin); -} - - - - - bool cWebAdmin::Init(void) { - if (!m_IniFile.ReadFile("webadmin.ini")) - { - LOGWARN("Regenerating webadmin.ini, all settings will be reset"); - m_IniFile.AddHeaderComment(" This file controls the webadmin feature of Cuberite"); - m_IniFile.AddHeaderComment(" Username format: [User:*username*]"); - m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); - m_IniFile.AddHeaderComment(" [User:admin]"); - m_IniFile.AddHeaderComment(" Password=admin"); - m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS); - m_IniFile.WriteFile("webadmin.ini"); - } - - if (!m_IniFile.GetValueSetB("WebAdmin", "Enabled", true)) + if (!LoadIniFile()) { // WebAdmin is disabled, bail out faking a success return true; @@ -147,31 +112,7 @@ bool cWebAdmin::Init(void) LOGD("Initialising WebAdmin..."); - // Initialize the WebAdmin template script and load the file - m_TemplateScript.Create(); - m_TemplateScript.RegisterAPILibs(); - if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) - { - LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin disabled!", FILE_IO_PREFIX "webadmin/template.lua"); - m_TemplateScript.Close(); - m_HTTPServer.Stop(); - return false; - } - - // Load the login template, provide a fallback default if not found: - if (!LoadLoginTemplate()) - { - LOGWARN("Could not load WebAdmin login template \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html"); - - // Sets the fallback template: - m_LoginTemplate = \ - "<h1>Cuberite WebAdmin</h1>" \ - "<center>" \ - "<form method='get' action='webadmin/'>" \ - "<input type='submit' value='Log in'>" \ - "</form>" \ - "</center>"; - } + Reload(); // Read the ports to be used: // Note that historically the ports were stored in the "Port" and "PortsIPv6" values @@ -224,7 +165,7 @@ void cWebAdmin::Stop(void) -bool cWebAdmin::LoadLoginTemplate(void) +bool cWebAdmin::LoadLoginPage(void) { cFile File(FILE_IO_PREFIX "webadmin/login_template.html", cFile::fmRead); if (!File.IsOpen()) @@ -238,7 +179,8 @@ bool cWebAdmin::LoadLoginTemplate(void) return false; } - m_LoginTemplate = TemplateContent; + cCSLock Lock(m_CS); + m_LoginPage = TemplateContent; return true; } @@ -246,6 +188,89 @@ bool cWebAdmin::LoadLoginTemplate(void) +void cWebAdmin::RemoveAllPluginWebTabs(const AString & a_PluginName) +{ + cCSLock lock(m_CS); + m_WebTabs.erase(std::remove_if(m_WebTabs.begin(), m_WebTabs.end(), [=](cWebTabPtr a_CBWebTab) + { + return (a_CBWebTab->m_PluginName == a_PluginName); + }), + m_WebTabs.end() + ); +} + + + + + +void cWebAdmin::Reload(void) +{ + cCSLock lock(m_CS); + if (!LoadIniFile()) + { + // We are asked to disable the webadmin, cannot do that, so warn the admin: + LOGWARNING( + "WebAdmin was previously enabled and now the settings say to disable it." + " This will not take effect until you restart the server." + ); + } + + // Initialize the WebAdmin template script and reload the file: + if (m_TemplateScript.IsValid()) + { + m_TemplateScript.Close(); + } + m_TemplateScript.Create(); + m_TemplateScript.RegisterAPILibs(); + if (!m_TemplateScript.LoadFile(FILE_IO_PREFIX "webadmin/template.lua")) + { + LOGWARN("Could not load WebAdmin template \"%s\". WebAdmin will not work properly!", FILE_IO_PREFIX "webadmin/template.lua"); + m_TemplateScript.Close(); + } + + // Load the login template, provide a fallback default if not found: + if (!LoadLoginPage()) + { + LOGWARN("Could not load WebAdmin login page \"%s\", using fallback template.", FILE_IO_PREFIX "webadmin/login_template.html"); + + // Set the fallback: + m_LoginPage = \ + "<h1>Cuberite WebAdmin</h1>" \ + "<center>" \ + "<form method='get' action='webadmin/'>" \ + "<input type='submit' value='Log in'>" \ + "</form>" \ + "</center>"; + } +} + + + + + +bool cWebAdmin::LoadIniFile(void) +{ + m_IniFile.Clear(); + if (!m_IniFile.ReadFile("webadmin.ini")) + { + LOGWARN("Regenerating webadmin.ini, all settings will be reset"); + m_IniFile.AddHeaderComment(" This file controls the webadmin feature of Cuberite"); + m_IniFile.AddHeaderComment(" It specifies whether webadmin is enabled, and what logins are allowed. "); + m_IniFile.AddHeaderComment(" Username format: [User:*username*]"); + m_IniFile.AddHeaderComment(" Password format: Password=*password*; for example:"); + m_IniFile.AddHeaderComment(" [User:admin]"); + m_IniFile.AddHeaderComment(" Password=admin"); + m_IniFile.SetValue("WebAdmin", "Ports", DEFAULT_WEBADMIN_PORTS); + m_IniFile.WriteFile("webadmin.ini"); + } + + return m_IniFile.GetValueSetB("WebAdmin", "Enabled", true); +} + + + + + void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request) { if (!a_Request.HasAuth()) @@ -255,17 +280,20 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT } // Check auth: - AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); - if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) { - a_Connection.SendNeedAuth("Cuberite WebAdmin - bad username or password"); - return; + cCSLock Lock(m_CS); + AString UserPassword = m_IniFile.GetValue("User:" + a_Request.GetAuthUsername(), "Password", ""); + if ((UserPassword == "") || (a_Request.GetAuthPassword() != UserPassword)) + { + a_Connection.SendNeedAuth("Cuberite WebAdmin - bad username or password"); + return; + } } // Check if the contents should be wrapped in the template: auto BareURL = a_Request.GetURLPath(); ASSERT(BareURL.length() > 0); - bool ShouldWrapInTemplate = ((BareURL.length() > 1) && (BareURL[1] != '~')); + bool ShouldWrapInTemplate = (!BareURL.empty() && (BareURL[1] != '~')); // Retrieve the request data: auto Data = std::static_pointer_cast<cWebadminRequestData>(a_Request.GetUserData()); @@ -312,6 +340,7 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT // Try to get the template from the Lua template script if (ShouldWrapInTemplate) { + cCSLock Lock(m_CS); if (m_TemplateScript.Call("ShowPage", this, &TemplateRequest, cLuaState::Return, Template)) { cHTTPOutgoingResponse Resp; @@ -325,59 +354,12 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT return; } - AString BaseURL = GetBaseURL(BareURL); - AString Menu; - Template = "{CONTENT}"; - AString FoundPlugin; - - for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - cWebPlugin * WebPlugin = *itr; - std::list< std::pair<AString, AString> > NameList = WebPlugin->GetTabNames(); - for (std::list< std::pair<AString, AString> >::iterator Names = NameList.begin(); Names != NameList.end(); ++Names) - { - Menu += "<li><a href='" + BaseURL + WebPlugin->GetWebTitle().c_str() + "/" + (*Names).second + "'>" + (*Names).first + "</a></li>"; - } - } - - sWebAdminPage Page = GetPage(TemplateRequest.Request); - AString Content = Page.Content; - FoundPlugin = Page.PluginName; - if (!Page.TabName.empty()) - { - FoundPlugin += " - " + Page.TabName; - } - - if (FoundPlugin.empty()) // Default page - { - Content = GetDefaultPage(); - } - - int MemUsageKiB = cRoot::GetPhysicalRAMUsage(); - if (MemUsageKiB > 0) - { - ReplaceString(Template, "{MEM}", Printf("%.02f", static_cast<double>(MemUsageKiB) / 1024)); - ReplaceString(Template, "{MEMKIB}", Printf("%d", MemUsageKiB)); - } - else - { - ReplaceString(Template, "{MEM}", "unknown"); - ReplaceString(Template, "{MEMKIB}", "unknown"); - } - ReplaceString(Template, "{USERNAME}", a_Request.GetAuthUsername()); - ReplaceString(Template, "{MENU}", Menu); - ReplaceString(Template, "{PLUGIN_NAME}", FoundPlugin); - ReplaceString(Template, "{CONTENT}", Content); - ReplaceString(Template, "{TITLE}", "Cuberite"); - - AString NumChunks; - Printf(NumChunks, "%d", cRoot::Get()->GetTotalChunkCount()); - ReplaceString(Template, "{NUMCHUNKS}", NumChunks); - - cHTTPOutgoingResponse Resp; - Resp.SetContentType("text/html"); - a_Connection.Send(Resp); - a_Connection.Send(Template.c_str(), Template.length()); + // Send the un-decorated page content: + auto page = GetPage(TemplateRequest.Request); + cHTTPOutgoingResponse resp; + resp.SetContentType(page.ContentType); + a_Connection.Send(resp); + a_Connection.Send(page.Content.c_str(), page.Content.length()); a_Connection.FinishResponse(); } @@ -392,7 +374,7 @@ void cWebAdmin::HandleRootRequest(cHTTPServerConnection & a_Connection, cHTTPInc cHTTPOutgoingResponse Resp; Resp.SetContentType("text/html"); a_Connection.Send(Resp); - a_Connection.Send(m_LoginTemplate); + a_Connection.Send(m_LoginPage); a_Connection.FinishResponse(); } @@ -406,7 +388,7 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc std::replace(FileURL.begin(), FileURL.end(), '\\', '/'); // Remove all leading backslashes: - if (FileURL[0] == '/') + if (!FileURL.empty() && (FileURL[0] == '/')) { size_t FirstCharToRead = FileURL.find_first_not_of('/'); if (FirstCharToRead != AString::npos) @@ -418,8 +400,9 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc // Remove all "../" strings: ReplaceString(FileURL, "../", ""); - bool LoadedSuccessfull = false; + // Read the file contents and guess its mime-type, based on the extension: AString Content = "<h2>404 Not Found</h2>"; + AString ContentType; AString Path = Printf(FILE_IO_PREFIX "webadmin/files/%s", FileURL.c_str()); if (cFile::IsFile(Path)) { @@ -427,18 +410,17 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc AString FileContent; if (File.IsOpen() && (File.ReadRestOfFile(FileContent) != -1)) { - LoadedSuccessfull = true; - Content = FileContent; + std::swap(Content, FileContent); + size_t LastPointPosition = Path.find_last_of('.'); + if (LastPointPosition != AString::npos) + { + ContentType = GetContentTypeFromFileExt(Path.substr(LastPointPosition + 1)); + } } } - - // Find content type (The currently method is very bad. We should change it later) - AString ContentType = "text/html"; - size_t LastPointPosition = Path.find_last_of('.'); - if (LoadedSuccessfull && (LastPointPosition != AString::npos) && (LastPointPosition < Path.length())) + if (ContentType.empty()) { - AString FileExtension = Path.substr(LastPointPosition + 1); - ContentType = GetContentTypeFromFileExt(FileExtension); + ContentType = "application/unknown"; } // Send the response: @@ -456,32 +438,36 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) { static bool IsInitialized = false; - static std::map<AString, AString> ContentTypeMap; + static AStringMap ContentTypeMap; if (!IsInitialized) { // Initialize the ContentTypeMap: - ContentTypeMap["png"] = "image/png"; - ContentTypeMap["fif"] = "image/fif"; - ContentTypeMap["gif"] = "image/gif"; - ContentTypeMap["jpeg"] = "image/jpeg"; - ContentTypeMap["jpg"] = "image/jpeg"; - ContentTypeMap["jpe"] = "image/jpeg"; - ContentTypeMap["tiff"] = "image/tiff"; - ContentTypeMap["ico"] = "image/ico"; - ContentTypeMap["csv"] = "image/comma-separated-values"; - ContentTypeMap["css"] = "text/css"; - ContentTypeMap["js"] = "text/javascript"; - ContentTypeMap["txt"] = "text/plain"; - ContentTypeMap["rtx"] = "text/richtext"; - ContentTypeMap["xml"] = "text/xml"; - } - - AString FileExtension = StrToLower(a_FileExtension); - if (ContentTypeMap.find(a_FileExtension) == ContentTypeMap.end()) - { - return "text/html"; - } - return ContentTypeMap[FileExtension]; + ContentTypeMap["png"] = "image/png"; + ContentTypeMap["fif"] = "image/fif"; + ContentTypeMap["gif"] = "image/gif"; + ContentTypeMap["jpeg"] = "image/jpeg"; + ContentTypeMap["jpg"] = "image/jpeg"; + ContentTypeMap["jpe"] = "image/jpeg"; + ContentTypeMap["tiff"] = "image/tiff"; + ContentTypeMap["ico"] = "image/ico"; + ContentTypeMap["csv"] = "text/csv"; + ContentTypeMap["css"] = "text/css"; + ContentTypeMap["js"] = "text/javascript"; + ContentTypeMap["txt"] = "text/plain"; + ContentTypeMap["rtx"] = "text/richtext"; + ContentTypeMap["rtf"] = "text/richtext"; + ContentTypeMap["xml"] = "text/xml"; + ContentTypeMap["html"] = "text/html"; + ContentTypeMap["htm"] = "text/html"; + ContentTypeMap["xhtml"] = "application/xhtml+xml"; // Not recomended for IE6, but no-one uses that anymore + } + + auto itr = ContentTypeMap.find(StrToLower(a_FileExtension)); + if (itr == ContentTypeMap.end()) + { + return AString(); + } + return itr->second; } @@ -490,86 +476,93 @@ AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension) sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request) { - sWebAdminPage Page; - AStringVector Split = StringSplit(a_Request.Path, "/"); + sWebAdminPage page; + auto split = StringSplit(a_Request.Path, "/"); + + // If no specific page was requested, return an empty object: + if (split.size() <= 2) + { + return page; + } - // Find the plugin that corresponds to the requested path - AString FoundPlugin; - if (Split.size() > 1) + // Find the WebTab handler responsible for the request: + cWebTabPtr tab; { - for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) + cCSLock Lock(m_CS); + for (auto & wt: m_WebTabs) { - if ((*itr)->GetWebTitle() == Split[1]) + if ( + (wt->m_PluginName == split[1]) && + (wt->m_UrlPath == split[2]) + ) { - Page.Content = (*itr)->HandleWebRequest(a_Request); - cWebPlugin * WebPlugin = *itr; - FoundPlugin = WebPlugin->GetWebTitle(); - AString TabName = WebPlugin->GetTabNameForRequest(a_Request).first; - Page.PluginName = FoundPlugin; - Page.TabName = TabName; + tab = wt; break; } + } // for wt - m_WebTabs[] + } + + // If a WebTab handler was found, call it: + if (tab != nullptr) + { + page.ContentType = "text/html"; // Default to HTML content type, unless overridden by a plugin + if (!tab->m_Callback->Call(a_Request, split[1], page.Content, page.ContentType)) + { + page.Content = GetHTMLEscapedString(Printf( + "WebTab callback for plugin %s, page %s has failed.", + tab->m_PluginName.c_str(), tab->m_Title.c_str() + )); } + page.PluginName = tab->m_PluginName; + page.TabTitle = tab->m_Title; + page.TabUrlPath = split[1]; } - // Return the page contents - return Page; + return page; } -AString cWebAdmin::GetDefaultPage(void) +AString cWebAdmin::GetBaseURL(const AString & a_URL) { - AString Content; - Content += "<h4>Server Name:</h4>"; - Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>"; + return GetBaseURL(StringSplit(a_URL, "/")); +} - // Display a list of all plugins: - Content += "<h4>Plugins:</h4><ul>"; - struct cPluginCallback: - public cPluginManager::cPluginCallback - { - AString & m_Content; - cPluginCallback(AString & a_Content): - m_Content(a_Content) - { - } - virtual bool Item(cPlugin * a_Plugin) override - { - if (a_Plugin->IsLoaded()) - { - AppendPrintf(m_Content, "<li>%s V.%i</li>", a_Plugin->GetName().c_str(), a_Plugin->GetVersion()); - } - return false; - } - } Callback(Content); - cPluginManager::Get()->ForEachPlugin(Callback); - Content += "</ul>"; - // Display a list of all players: - Content += "<h4>Players:</h4><ul>"; - cPlayerAccum PlayerAccum; - cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players - if (World != nullptr) - { - World->ForEachPlayer(PlayerAccum); - Content.append(PlayerAccum.m_Contents); - } - Content += "</ul><br>"; - return Content; + +void cWebAdmin::AddWebTab( + const AString & a_Title, + const AString & a_UrlPath, + const AString & a_PluginName, + SharedPtr<cWebAdmin::cWebTabCallback> a_Callback +) +{ + cCSLock lock(m_CS); + m_WebTabs.emplace_back(std::make_shared<cWebTab>(a_Title, a_UrlPath, a_PluginName, a_Callback)); } -AString cWebAdmin::GetBaseURL(const AString & a_URL) +bool cWebAdmin::DelWebTab(const AString & a_UrlPath) { - return GetBaseURL(StringSplit(a_URL, "/")); + cCSLock lock(m_CS); + for (auto itr = m_WebTabs.begin(), end = m_WebTabs.end(); itr != end; ++itr) + { + if ((*itr)->m_UrlPath == a_UrlPath) + { + m_WebTabs.erase(itr); + return true; + } + } // for itr - m_WebTabs[] + + // Not found: + return false; } diff --git a/src/WebAdmin.h b/src/WebAdmin.h index 5e48f597c..b76ca6df8 100644 --- a/src/WebAdmin.h +++ b/src/WebAdmin.h @@ -89,14 +89,14 @@ struct HTTPTemplateRequest -// tolua_begin struct sWebAdminPage { AString Content; AString PluginName; - AString TabName; + AString TabTitle; + AString TabUrlPath; + AString ContentType; }; -// tolua_end @@ -111,7 +111,49 @@ class cWebAdmin : public: // tolua_end - typedef std::list< cWebPlugin* > PluginList; + /** Interface for getting the content of a single WebTab. */ + class cWebTabCallback abstract + { + public: + // Force a virtual destructor in descendants + virtual ~cWebTabCallback() {} + + /** Returns the contents for the specified request. + Returns true if the call was successful, false on an error. + a_Request is the full HTTP request object, as received from the client. + a_UrlPath is the UrlPath of the WebTab registered for this request, as parsed from a_Request. + Descendants should fill a_Content with the page contents + and optionally set a_ContentType [defaults to "text/html"] */ + virtual bool Call( + const HTTPRequest & a_Request, + const AString & a_UrlPath, + AString & a_Content, + AString & a_ContentType + ) = 0; + }; + + + /** Container for a single web tab. + Each web tab has a title, URL path and an associated plugin's name. + Each web tab is registered with a callback to provide the content. */ + class cWebTab + { + public: + AString m_Title; + AString m_UrlPath; + AString m_PluginName; + SharedPtr<cWebTabCallback> m_Callback; + + cWebTab(const AString & a_Title, const AString & a_UrlPath, const AString & a_PluginName, SharedPtr<cWebTabCallback> a_Callback): + m_Title(a_Title), + m_UrlPath(a_UrlPath), + m_PluginName(a_PluginName), + m_Callback(a_Callback) + { + } + }; + typedef SharedPtr<cWebTab> cWebTabPtr; + typedef std::vector<cWebTabPtr> cWebTabPtrs; cWebAdmin(void); @@ -120,81 +162,115 @@ public: /** Initializes the object. Returns true if successfully initialized and ready to start */ bool Init(void); - /** Starts the HTTP server taking care of the admin. Returns true if successful */ + /** Starts the HTTP server taking care of the webadmin. Returns true if successful */ bool Start(void); /** Stops the HTTP server, if it was started. */ void Stop(void); - /** Loads the login template. Returns true if the loading succeeds, false if not. */ - bool LoadLoginTemplate(void); - - void AddPlugin(cWebPlugin * a_Plugin); - void RemovePlugin(cWebPlugin * a_Plugin); + /** Loads the login template into m_LoginPage. + Returns true if the loading succeeds, false if not. */ + bool LoadLoginPage(void); - // TODO: Convert this to the auto-locking callback mechanism used for looping players in worlds and such - PluginList GetPlugins() const { return m_Plugins; } // >> EXPORTED IN MANUALBINDINGS << + /** Returns a copy of all the registered web tabs. + Exported to Lua in ManualBindings.cpp. */ + cWebTabPtrs GetAllWebTabs(void) { return m_WebTabs; } - // tolua_begin + /** Removes all WebTabs registered by the specified plugin. */ + void RemoveAllPluginWebTabs(const AString & a_PluginName); + /** Returns the (inner) page contents for the specified request. + Calls the appropriate WebTab handler to get the contents. + Exported to Lua in ManualBindings.cpp. */ sWebAdminPage GetPage(const HTTPRequest & a_Request); - /** Returns the contents of the default page - the list of plugins and players */ - AString GetDefaultPage(void); + // tolua_begin - /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ - AString GetBaseURL(const AString & a_URL); + /** Reloads m_IniFile, m_LoginPage and m_TemplateScript. + Note that reloading will not change the "enabled" state of the server, and it will not update listening ports. */ + void Reload(void); - /** Returns the list of ports used for the webadmin. */ + /** Returns the list of ports on which the webadmin is configured to listen. */ AString GetPorts(void) const { return StringsConcat(m_Ports, ','); } - - /** OBSOLETE: Returns the list of IPv4 ports used for the webadmin. - Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */ - AString GetIPv4Ports(void) const { return GetPorts(); } - - /** OBSOLETE: Returns the list of IPv6 ports used for the webadmin. - Currently there is no distinction between IPv4 and IPv6; use GetPorts() instead. */ - AString GetIPv6Ports(void) const { return GetPorts(); } - // tolua_end - /** Escapes text passed into it, so it can be embedded into html. */ + /** Adds a new WebTab handler. + a_Title is the display title of the tab + a_UrlPath is the part of the URL that uniquely identifies this tab. + a_PluginName is the display name of the plugin creating this tab. + a_Callback is used to provide the actual WebTab contents, when requested. + Exported in ManualBindings.cpp. */ + void AddWebTab( + const AString & a_Title, + const AString & a_UrlPath, + const AString & a_PluginName, + SharedPtr<cWebTabCallback> a_Callback + ); + + /** Removes the WebTab with the specified URL path. + Returns true if WebTab was found and removed, false if not found. + Exported in ManualBindings.cpp */ + bool DelWebTab(const AString & a_UrlPath); + + /** Escapes text passed into it, so it can be embedded into html. + Exported to Lua in ManualBindings.cpp. */ static AString GetHTMLEscapedString(const AString & a_Input); - /** Escapes the string for use in an URL */ + /** Escapes the string for use in an URL + Exported to Lua in ManualBindings.cpp. */ static AString GetURLEncodedString(const AString & a_Input); + /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style). + Exported to Lua in ManualBindings.cpp. */ + static AString GetBaseURL(const AString & a_URL); + /** Returns the prefix needed for making a link point to the webadmin root from the given URL ("../../../webadmin"-style) */ static AString GetBaseURL(const AStringVector & a_URLSplit); - /** Returns the content type from the file extension. If the extension isn't in the list, the function returns "text/html" */ + /** Returns the content type from the file extension. + If the extension isn't in the list, the function returns an empty string. + Exported to Lua in ManualBindings.cpp. */ static AString GetContentTypeFromFileExt(const AString & a_FileExtension); protected: + /** Protects m_WebTabs, m_TemplateScript, m_LoginTemplate and m_IniFile against multithreaded access. */ + cCriticalSection m_CS; + + /** All registered WebTab handlers. + Protected against multithreaded access by m_CS. */ + cWebTabPtrs m_WebTabs; + + /** The Lua template script to provide templates. + Protected against multithreaded access by m_CS. */ + cLuaState m_TemplateScript; + + /** The HTML page that provides the login. + Protected against multithreaded access by m_CS. */ + AString m_LoginPage; + + /** The webadmin.ini file, used for the settings and allowed logins. + Protected against multithreaded access by m_CS. */ + cIniFile m_IniFile; + /** Set to true if Init() succeeds and the webadmin isn't to be disabled */ bool m_IsInitialized; /** Set to true if Start() succeeds in starting the server, reset back to false in Stop(). */ bool m_IsRunning; - /** The webadmin.ini file, used for the settings and allowed logins */ - cIniFile m_IniFile; - - PluginList m_Plugins; - /** The ports on which the webadmin is running. */ AStringVector m_Ports; - /** The Lua template script to provide templates: */ - cLuaState m_TemplateScript; - - /** The template that provides the login site: */ - AString m_LoginTemplate; - /** The HTTP server which provides the underlying HTTP parsing, serialization and events */ cHTTPServer m_HTTPServer; + + /** Loads webadmin.ini into m_IniFile. + Creates a default file if it doesn't exist. + Returns true if webadmin is enabled, false if disabled. */ + bool LoadIniFile(void); + /** Handles requests coming to the "/webadmin" or "/~webadmin" URLs */ void HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTTPIncomingRequest & a_Request); |