Revert "Lua callback"
This commit is contained in:
parent
8fc9cfa411
commit
e3d34d9917
@ -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 cPlugin object.
|
||||
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.
|
||||
]],
|
||||
Functions =
|
||||
{
|
||||
@ -22,10 +22,10 @@ return
|
||||
|
||||
cPluginLua =
|
||||
{
|
||||
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.",
|
||||
Desc = "",
|
||||
Functions =
|
||||
{
|
||||
AddWebTab = { Params = "Title, HandlerFn", Return = "", Notes = "<b>OBSOLETE</b> - Use {{cWebAdmin}}:AddWebTab() instead." },
|
||||
AddWebTab = { Params = "", Return = "", Notes = "Adds a new webadmin tab" },
|
||||
},
|
||||
Inherits = "cPlugin",
|
||||
}, -- cPluginLua
|
||||
@ -81,7 +81,6 @@ 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)" },
|
||||
|
@ -5,15 +5,7 @@ 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
|
||||
|
||||
|
@ -1876,8 +1876,8 @@ end
|
||||
|
||||
function HandleConsoleSchedule(a_Split)
|
||||
local prev = os.clock()
|
||||
LOG("Scheduling a task for 5 seconds in the future (current os.clock is " .. prev .. ")")
|
||||
cRoot:Get():GetDefaultWorld():ScheduleTask(5 * 20,
|
||||
LOG("Scheduling a task for 2 seconds in the future (current os.clock is " .. prev .. ")")
|
||||
cRoot:Get():GetDefaultWorld():ScheduleTask(40,
|
||||
function ()
|
||||
local current = os.clock()
|
||||
local diff = current - prev
|
||||
|
@ -20,7 +20,7 @@ end
|
||||
|
||||
|
||||
|
||||
local function GetDefaultPage()
|
||||
function GetDefaultPage()
|
||||
local PM = cRoot:Get():GetPluginManager()
|
||||
|
||||
local SubTitle = "Current Game"
|
||||
@ -55,31 +55,30 @@ end
|
||||
|
||||
function ShowPage(WebAdmin, TemplateRequest)
|
||||
SiteContent = {}
|
||||
local BaseURL = cWebAdmin:GetBaseURL(TemplateRequest.Request.Path)
|
||||
local BaseURL = WebAdmin: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 = cWebAdmin:GetPage(TemplateRequest.Request)
|
||||
local PluginPage = WebAdmin:GetPage(TemplateRequest.Request)
|
||||
local PageContent = PluginPage.Content
|
||||
local SubTitle = PluginPage.PluginFolder
|
||||
if (PluginPage.UrlPath ~= "") then
|
||||
SubTitle = PluginPage.PluginFolder .. " - " .. PluginPage.TabTitle
|
||||
local SubTitle = PluginPage.PluginName
|
||||
if (PluginPage.TabName ~= "") then
|
||||
SubTitle = PluginPage.PluginName .. " - " .. PluginPage.TabName
|
||||
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 -->
|
||||
@ -134,39 +133,20 @@ function ShowPage(WebAdmin, TemplateRequest)
|
||||
<td class="trow1 smalltext">
|
||||
]])
|
||||
|
||||
-- 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
|
||||
)
|
||||
|
||||
-- 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")
|
||||
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")
|
||||
end
|
||||
|
||||
Output("<br>\n");
|
||||
end
|
||||
Output("<br>\n");
|
||||
end
|
||||
|
||||
|
||||
|
@ -38,6 +38,7 @@ $cfile "LuaFunctions.h"
|
||||
$cfile "PluginManager.h"
|
||||
$cfile "Plugin.h"
|
||||
$cfile "PluginLua.h"
|
||||
$cfile "WebPlugin.h"
|
||||
$cfile "LuaWindow.h"
|
||||
|
||||
$cfile "../BlockID.h"
|
||||
|
@ -23,6 +23,7 @@ SET (SRCS
|
||||
Plugin.cpp
|
||||
PluginLua.cpp
|
||||
PluginManager.cpp
|
||||
WebPlugin.cpp
|
||||
)
|
||||
|
||||
SET (HDRS
|
||||
@ -43,6 +44,7 @@ SET (HDRS
|
||||
Plugin.h
|
||||
PluginLua.h
|
||||
PluginManager.h
|
||||
WebPlugin.h
|
||||
tolua++.h
|
||||
)
|
||||
|
||||
@ -64,6 +66,7 @@ set(BINDING_DEPENDENCIES
|
||||
../Bindings/Plugin.h
|
||||
../Bindings/PluginLua.h
|
||||
../Bindings/PluginManager.h
|
||||
../Bindings/WebPlugin.h
|
||||
../BiomeDef.h
|
||||
../BlockArea.h
|
||||
../BlockEntities/BeaconEntity.h
|
||||
|
@ -20,10 +20,6 @@ extern "C"
|
||||
#include "../Entities/Entity.h"
|
||||
#include "../BlockEntities/BlockEntity.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// fwd: "SQLite/lsqlite3.c"
|
||||
extern "C"
|
||||
{
|
||||
@ -43,10 +39,6 @@ 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";
|
||||
|
||||
|
||||
|
||||
|
||||
@ -121,101 +113,6 @@ cLuaStateTracker & cLuaStateTracker::Get(void)
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cLuaState::cCallback:
|
||||
|
||||
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:
|
||||
a_LuaState.TrackCallback(*this);
|
||||
|
||||
// Store the new callback:
|
||||
cCSLock Lock(m_CS);
|
||||
m_Ref.RefStack(a_LuaState, a_StackPos);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::cCallback::Clear(void)
|
||||
{
|
||||
// Free the callback reference:
|
||||
lua_State * luaState = nullptr;
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
if (!m_Ref.IsValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
luaState = m_Ref.GetLuaState();
|
||||
m_Ref.UnRef();
|
||||
}
|
||||
|
||||
// Remove from LuaState's callback-tracking:
|
||||
cLuaState(luaState).UntrackCallback(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cLuaState::cCallback::IsValid(void)
|
||||
{
|
||||
cCSLock lock(m_CS);
|
||||
return m_Ref.IsValid();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cLuaState::cCallback::IsSameLuaState(cLuaState & a_LuaState)
|
||||
{
|
||||
cCSLock lock(m_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)
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
if (!m_Ref.IsValid())
|
||||
{
|
||||
LOGD("%s: Invalidating an already invalid callback at %p, this should not happen",
|
||||
__FUNCTION__, reinterpret_cast<void *>(this)
|
||||
);
|
||||
return;
|
||||
}
|
||||
m_Ref.UnRef();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cLuaState:
|
||||
|
||||
@ -273,10 +170,6 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
@ -313,16 +206,6 @@ 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;
|
||||
@ -938,18 +821,6 @@ 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;
|
||||
@ -976,24 +847,6 @@ 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)
|
||||
{
|
||||
return a_Callback->RefStack(*this, a_StackPos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cLuaState::GetStackValue(int a_StackPos, cPluginManager::CommandResult & a_Result)
|
||||
{
|
||||
if (lua_isnumber(m_LuaState, a_StackPos))
|
||||
@ -1678,7 +1531,7 @@ int cLuaState::CopyStackFrom(cLuaState & a_SrcLuaState, int a_SrcStart, int a_Sr
|
||||
LOGWARNING("%s: Unsupported value: '%s' at stack position %d. Can only copy numbers, strings, bools and classes!",
|
||||
__FUNCTION__, lua_typename(a_SrcLuaState, t), i
|
||||
);
|
||||
a_SrcLuaState.LogStackValues("Stack where copying failed:");
|
||||
a_SrcLuaState.LogStack("Stack where copying failed:");
|
||||
lua_pop(m_LuaState, i - a_SrcStart);
|
||||
return -1;
|
||||
}
|
||||
@ -1705,16 +1558,16 @@ void cLuaState::ToString(int a_StackPos, AString & a_String)
|
||||
|
||||
|
||||
|
||||
void cLuaState::LogStackValues(const char * a_Header)
|
||||
void cLuaState::LogStack(const char * a_Header)
|
||||
{
|
||||
LogStackValues(m_LuaState, a_Header);
|
||||
LogStack(m_LuaState, a_Header);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaState::LogStackValues(lua_State * a_LuaState, const char * a_Header)
|
||||
void cLuaState::LogStack(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:");
|
||||
@ -1739,21 +1592,6 @@ void cLuaState::LogStackValues(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));
|
||||
@ -1788,50 +1626,6 @@ 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:
|
||||
|
||||
@ -1887,7 +1681,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);
|
||||
}
|
||||
@ -1898,9 +1692,11 @@ 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;
|
||||
|
@ -80,20 +80,8 @@ 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:
|
||||
lua_State * m_LuaState;
|
||||
cLuaState * m_LuaState;
|
||||
int m_Ref;
|
||||
|
||||
// Remove the copy-constructor:
|
||||
@ -124,76 +112,6 @@ 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().
|
||||
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) = default;
|
||||
|
||||
~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)
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
if (!m_Ref.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cLuaState(m_Ref.GetLuaState()).Call(m_Ref, std::forward<Args>(args)...);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 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. */
|
||||
cCallback(const cCallback &) = delete;
|
||||
|
||||
/** This class cannot be moved, because it is tracked in the LuaState by-ptr. */
|
||||
cCallback(cCallback &&) = delete;
|
||||
};
|
||||
typedef SharedPtr<cCallback> cCallbackPtr;
|
||||
|
||||
|
||||
/** A dummy class that's used only to delimit function args from return values for cLuaState::Call() */
|
||||
class cRet
|
||||
{
|
||||
@ -343,16 +261,11 @@ 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, cPluginManager::CommandResult & a_Result);
|
||||
bool GetStackValue(int a_StackPos, cRef & a_Ref);
|
||||
bool GetStackValue(int a_StackPos, double & a_Value);
|
||||
@ -515,14 +428,10 @@ 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 LogStackValues(const char * a_Header = nullptr);
|
||||
void LogStack(const char * a_Header = nullptr);
|
||||
|
||||
/** Logs all the elements' types on the API stack, with an optional header for the listing. */
|
||||
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);
|
||||
static void LogStack(lua_State * a_LuaState, const char * a_Header = nullptr);
|
||||
|
||||
protected:
|
||||
|
||||
@ -532,7 +441,8 @@ protected:
|
||||
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) */
|
||||
@ -541,15 +451,6 @@ 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)
|
||||
@ -632,14 +533,6 @@ 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);
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -15,13 +15,14 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// cLuaWindow:
|
||||
|
||||
cLuaWindow::cLuaWindow(cLuaState & a_LuaState, cWindow::WindowType a_WindowType, int a_SlotsX, int a_SlotsY, const AString & a_Title) :
|
||||
Super(a_WindowType, a_Title),
|
||||
cLuaWindow::cLuaWindow(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_LuaState(a_LuaState.QueryCanonLuaState())
|
||||
m_Plugin(nullptr),
|
||||
m_LuaRef(LUA_REFNIL),
|
||||
m_OnClosingFnRef(LUA_REFNIL),
|
||||
m_OnSlotChangedFnRef(LUA_REFNIL)
|
||||
{
|
||||
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));
|
||||
|
||||
@ -66,42 +67,62 @@ cLuaWindow::~cLuaWindow()
|
||||
|
||||
|
||||
|
||||
void cLuaWindow::SetOnClosing(cLuaState::cCallbackPtr a_OnClosing)
|
||||
void cLuaWindow::SetLuaRef(cPluginLua * a_Plugin, int a_LuaRef)
|
||||
{
|
||||
// Only one Lua state can be a cLuaWindow object callback:
|
||||
ASSERT(a_OnClosing->IsSameLuaState(*m_LuaState));
|
||||
|
||||
// Store the new reference, releasing the old one if appropriate:
|
||||
m_OnClosing = 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaWindow::SetOnSlotChanged(cLuaState::cCallbackPtr a_OnSlotChanged)
|
||||
bool cLuaWindow::IsLuaReferenced(void) const
|
||||
{
|
||||
// Only one Lua state can be a cLuaWindow object callback:
|
||||
ASSERT(a_OnSlotChanged->IsSameLuaState(*m_LuaState));
|
||||
|
||||
// Store the new reference, releasing the old one if appropriate:
|
||||
m_OnSlotChanged = a_OnSlotChanged;
|
||||
return ((m_Plugin != nullptr) && (m_LuaRef != LUA_REFNIL));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaWindow::OpenedByPlayer(cPlayer & a_Player)
|
||||
void cLuaWindow::SetOnClosing(cPluginLua * a_Plugin, int a_FnRef)
|
||||
{
|
||||
// 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)
|
||||
// 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_OnClosingFnRef != LUA_REFNIL)
|
||||
{
|
||||
m_LuaRef.CreateFromObject(*m_LuaState, this);
|
||||
m_Plugin->Unreference(m_OnClosingFnRef);
|
||||
}
|
||||
|
||||
++m_PlayerCount;
|
||||
Super::OpenedByPlayer(a_Player);
|
||||
// Store the new reference
|
||||
m_Plugin = a_Plugin;
|
||||
m_OnClosingFnRef = a_FnRef;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cLuaWindow::SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef)
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
m_Plugin->Unreference(m_OnSlotChangedFnRef);
|
||||
}
|
||||
|
||||
// Store the new reference
|
||||
m_Plugin = a_Plugin;
|
||||
m_OnSlotChangedFnRef = a_FnRef;
|
||||
}
|
||||
|
||||
|
||||
@ -111,27 +132,17 @@ void cLuaWindow::OpenedByPlayer(cPlayer & a_Player)
|
||||
bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
|
||||
{
|
||||
// First notify the plugin through the registered callback:
|
||||
if (m_OnClosing != nullptr)
|
||||
if (m_OnClosingFnRef != LUA_REFNIL)
|
||||
{
|
||||
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
|
||||
)
|
||||
ASSERT(m_Plugin != nullptr);
|
||||
if (m_Plugin->CallbackWindowClosing(m_OnClosingFnRef, *this, a_Player, a_CanRefuse))
|
||||
{
|
||||
// The callback disagrees (the higher levels check the CanRefuse flag compliance)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
return super::ClosedByPlayer(a_Player, a_CanRefuse);
|
||||
}
|
||||
|
||||
|
||||
@ -140,7 +151,13 @@ bool cLuaWindow::ClosedByPlayer(cPlayer & a_Player, bool a_CanRefuse)
|
||||
|
||||
void cLuaWindow::Destroy(void)
|
||||
{
|
||||
Super::Destroy();
|
||||
super::Destroy();
|
||||
|
||||
if ((m_LuaRef != LUA_REFNIL) && (m_Plugin != nullptr))
|
||||
{
|
||||
// The object is referenced by Lua, un-reference it
|
||||
m_Plugin->Unreference(m_LuaRef);
|
||||
}
|
||||
|
||||
// Lua will take care of this object, it will garbage-collect it, so we must not delete it!
|
||||
m_IsDestroyed = false;
|
||||
@ -161,7 +178,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);
|
||||
}
|
||||
|
||||
|
||||
@ -177,9 +194,9 @@ void cLuaWindow::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
|
||||
}
|
||||
|
||||
// If an OnSlotChanged callback has been registered, call it:
|
||||
if (m_OnSlotChanged != nullptr)
|
||||
if (m_OnSlotChangedFnRef != LUA_REFNIL)
|
||||
{
|
||||
m_OnSlotChanged->Call(this, a_SlotNum);
|
||||
m_Plugin->CallbackWindowSlotChanged(m_OnSlotChangedFnRef, *this, a_SlotNum);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include "LuaState.h"
|
||||
#include "../UI/Window.h"
|
||||
#include "../ItemGrid.h"
|
||||
|
||||
@ -18,30 +16,35 @@
|
||||
|
||||
|
||||
|
||||
// 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.
|
||||
- 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.
|
||||
*/
|
||||
- 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. */
|
||||
// tolua_begin
|
||||
class cLuaWindow :
|
||||
public cWindow
|
||||
// tolua_end
|
||||
, public cItemGrid::cListener
|
||||
{ // tolua_export
|
||||
typedef cWindow Super;
|
||||
// tolua_begin
|
||||
{
|
||||
typedef cWindow super;
|
||||
|
||||
public:
|
||||
/** 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);
|
||||
/** 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);
|
||||
|
||||
// tolua_begin
|
||||
virtual ~cLuaWindow();
|
||||
|
||||
/** Returns the internal representation of the contents that are manipulated by Lua */
|
||||
@ -49,37 +52,36 @@ public:
|
||||
|
||||
// tolua_end
|
||||
|
||||
/** Sets the Lua callback function to call when the window is about to close */
|
||||
void SetOnClosing(cLuaState::cCallbackPtr a_OnClosing);
|
||||
/** 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);
|
||||
|
||||
/** Sets the Lua callback function to call when a slot is changed */
|
||||
void SetOnSlotChanged(cLuaState::cCallbackPtr a_OnSlotChanged);
|
||||
/** 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 callback function (Lua reference) to call when a slot is changed */
|
||||
void SetOnSlotChanged(cPluginLua * a_Plugin, int a_FnRef);
|
||||
|
||||
protected:
|
||||
|
||||
/** Contents of the non-inventory part */
|
||||
cItemGrid m_Contents;
|
||||
|
||||
/** The Lua state that has opened the window and owns the m_LuaRef */
|
||||
cLuaState * m_LuaState;
|
||||
/** The plugin that has opened the window and owns the m_LuaRef */
|
||||
cPluginLua * m_Plugin;
|
||||
|
||||
/** The Lua callback to call when the window is closing for any player */
|
||||
cLuaState::cCallbackPtr m_OnClosing;
|
||||
/** The Lua object reference, used for keeping the object alive as long as any player has the window open */
|
||||
int m_LuaRef;
|
||||
|
||||
/** The Lua callback to call when a slot has changed */
|
||||
cLuaState::cCallbackPtr m_OnSlotChanged;
|
||||
|
||||
/** Number of players that are currently using the window.
|
||||
Used to manager the m_LuaRef lifetime. */
|
||||
std::atomic<int> m_PlayerCount;
|
||||
|
||||
/** 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 the window is closing for any player */
|
||||
int m_OnClosingFnRef;
|
||||
|
||||
/** 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;
|
||||
|
@ -992,13 +992,7 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager,
|
||||
}
|
||||
|
||||
// Retrieve and check the hook type
|
||||
int HookType;
|
||||
if (!S.GetStackValue(a_ParamIdx, HookType))
|
||||
{
|
||||
LOGWARNING("cPluginManager.AddHook(): Cannot read the hook type.");
|
||||
S.LogStackTrace();
|
||||
return 0;
|
||||
}
|
||||
int HookType = static_cast<int>(tolua_tonumber(S, a_ParamIdx, -1));
|
||||
if (!a_PluginManager->IsValidHookType(HookType))
|
||||
{
|
||||
LOGWARNING("cPluginManager.AddHook(): Invalid HOOK_TYPE parameter: %d", HookType);
|
||||
@ -1007,14 +1001,7 @@ static int tolua_cPluginManager_AddHook_FnRef(cPluginManager * a_PluginManager,
|
||||
}
|
||||
|
||||
// Add the hook to the plugin
|
||||
auto callback = std::make_shared<cLuaState::cCallback>();
|
||||
if (!S.GetStackValue(a_ParamIdx + 1, callback))
|
||||
{
|
||||
LOGWARNING("cPluginManager.AddHook(): Cannot read the callback parameter");
|
||||
S.LogStackTrace();
|
||||
return 0;
|
||||
}
|
||||
if (!Plugin->AddHookCallback(HookType, callback))
|
||||
if (!Plugin->AddHookRef(HookType, a_ParamIdx + 1))
|
||||
{
|
||||
LOGWARNING("cPluginManager.AddHook(): Cannot add hook %d, unknown error.", HookType);
|
||||
S.LogStackTrace();
|
||||
@ -1071,11 +1058,10 @@ static int tolua_cPluginManager_AddHook_DefFn(cPluginManager * a_PluginManager,
|
||||
}
|
||||
|
||||
// Retrieve the function to call and add it to the plugin:
|
||||
auto callback = std::make_shared<cLuaState::cCallback>();
|
||||
lua_getglobal(S, FnName);
|
||||
bool res = S.GetStackValue(-1, callback);
|
||||
lua_pop(S, 1);
|
||||
if (!res || !callback->IsValid())
|
||||
lua_pushstring(S, FnName);
|
||||
bool res = Plugin->AddHookRef(HookType, 1);
|
||||
lua_pop(S, 1); // Pop the function off the stack
|
||||
if (!res)
|
||||
{
|
||||
LOGWARNING("cPluginManager.AddHook(): Function %s not found. Hook not added.", FnName);
|
||||
S.LogStackTrace();
|
||||
@ -1599,6 +1585,55 @@ 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
|
||||
@ -1629,25 +1664,36 @@ static int tolua_cPlayer_PermissionMatches(lua_State * tolua_S)
|
||||
|
||||
template <
|
||||
class OBJTYPE,
|
||||
void (OBJTYPE::*SetCallback)(cLuaState::cCallbackPtr a_CallbackFn)
|
||||
void (OBJTYPE::*SetCallback)(cPluginLua * a_Plugin, int a_FnRef)
|
||||
>
|
||||
static int tolua_SetObjectCallback(lua_State * tolua_S)
|
||||
{
|
||||
// Function signature: OBJTYPE:SetWhateverCallback(CallbackFunction)
|
||||
|
||||
// Get the parameters - self and the function reference:
|
||||
cLuaState L(tolua_S);
|
||||
OBJTYPE * self;
|
||||
cLuaState::cCallbackPtr callback;
|
||||
if (!L.GetStackValues(1, self, callback))
|
||||
// Retrieve the plugin instance from the Lua state
|
||||
cPluginLua * Plugin = cManualBindings::GetLuaPlugin(tolua_S);
|
||||
if (Plugin == nullptr)
|
||||
{
|
||||
LOGWARNING("%s: Cannot get parameters", __FUNCTION__);
|
||||
L.LogStackTrace();
|
||||
// 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)
|
||||
{
|
||||
LOGERROR("%s: Cannot create a function reference. Callback not set.", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Set the callback
|
||||
(self->*SetCallback)(callback);
|
||||
(self->*SetCallback)(Plugin, FnRef);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1655,70 +1701,46 @@ static int tolua_SetObjectCallback(lua_State * tolua_S)
|
||||
|
||||
|
||||
|
||||
// Callback class used for the WebTab:
|
||||
class cWebTabCallback:
|
||||
public cWebAdmin::cWebTabCallback
|
||||
{
|
||||
public:
|
||||
/** The Lua callback to call to generate the page contents. */
|
||||
cLuaState::cCallback m_Callback;
|
||||
|
||||
virtual bool Call(
|
||||
const HTTPRequest & a_Request,
|
||||
const AString & a_UrlPath,
|
||||
AString & a_Content,
|
||||
AString & a_ContentType
|
||||
) override
|
||||
{
|
||||
AString content, contentType;
|
||||
return m_Callback.Call(&a_Request, a_UrlPath, cLuaState::Return, a_Content, a_ContentType);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S)
|
||||
{
|
||||
// OBSOLETE, use cWebAdmin:AddWebTab() instead!
|
||||
// Function signature:
|
||||
// cPluginLua:AddWebTab(Title, CallbackFn, [UrlPath])
|
||||
|
||||
// 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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (
|
||||
!LuaState.CheckParamString(2) ||
|
||||
!LuaState.CheckParamFunction(3) ||
|
||||
// Optional string as param 4
|
||||
!LuaState.CheckParamEnd(5)
|
||||
)
|
||||
cPluginLua * self = nullptr;
|
||||
|
||||
if (!LuaState.GetStackValue(1, self))
|
||||
{
|
||||
LOGWARNING("cPluginLua:AddWebTab: invalid self as first argument");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read the params:
|
||||
AString title, urlPath;
|
||||
auto callback = std::make_shared<cWebTabCallback>();
|
||||
if (!LuaState.GetStackValues(2, title, callback->m_Callback))
|
||||
tolua_Error tolua_err;
|
||||
tolua_err.array = 0;
|
||||
tolua_err.index = 3;
|
||||
tolua_err.type = "function";
|
||||
|
||||
std::string Title;
|
||||
int Reference = LUA_REFNIL;
|
||||
|
||||
if (LuaState.CheckParamString(2) && LuaState.CheckParamFunction(3))
|
||||
{
|
||||
LOGWARNING("cPlugin:AddWebTab(): Cannot read required parameters");
|
||||
return 0;
|
||||
Reference = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
|
||||
LuaState.GetStackValue(2, Title);
|
||||
}
|
||||
if (!LuaState.GetStackValue(4, urlPath))
|
||||
else
|
||||
{
|
||||
urlPath = cWebAdmin::GetURLEncodedString(title);
|
||||
return cManualBindings::tolua_do_error(tolua_S, "#ferror calling function '#funcname#'", &tolua_err);
|
||||
}
|
||||
|
||||
cRoot::Get()->GetWebAdmin()->AddWebTab(title, urlPath, self->GetName(), callback);
|
||||
if (Reference != LUA_REFNIL)
|
||||
{
|
||||
if (!self->AddWebTab(Title.c_str(), tolua_S, Reference))
|
||||
{
|
||||
luaL_unref(tolua_S, LUA_REGISTRYINDEX, Reference);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGWARNING("cPluginLua:AddWebTab: invalid function reference in 2nd argument (Title: \"%s\")", Title.c_str());
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -2084,68 +2106,22 @@ static int tolua_cUrlParser_ParseAuthorityPart(lua_State * a_LuaState)
|
||||
|
||||
|
||||
|
||||
static int tolua_cWebAdmin_AddWebTab(lua_State * tolua_S)
|
||||
static int tolua_cWebAdmin_GetPlugins(lua_State * tolua_S)
|
||||
{
|
||||
// Function signatures:
|
||||
// cWebAdmin:AddWebTab(Title, UrlPath, CallbackFn)
|
||||
cWebAdmin * self = reinterpret_cast<cWebAdmin *>(tolua_tousertype(tolua_S, 1, nullptr));
|
||||
|
||||
// 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;
|
||||
}
|
||||
const cWebAdmin::PluginList & AllPlugins = self->GetPlugins();
|
||||
|
||||
// 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)
|
||||
{
|
||||
// Function signature:
|
||||
// cWebAdmin:GetAllWebTabs() -> { {"PluginName", "UrlPath", "Title"}, {"PluginName", "UrlPath", "Title"}, ...}
|
||||
|
||||
// Don't care about params at all
|
||||
|
||||
auto webTabs = cRoot::Get()->GetWebAdmin()->GetAllWebTabs();
|
||||
lua_createtable(tolua_S, static_cast<int>(webTabs.size()), 0);
|
||||
lua_createtable(tolua_S, static_cast<int>(AllPlugins.size()), 0);
|
||||
int newTable = lua_gettop(tolua_S);
|
||||
int index = 1;
|
||||
cLuaState L(tolua_S);
|
||||
for (const auto & wt: webTabs)
|
||||
cWebAdmin::PluginList::const_iterator iter = AllPlugins.begin();
|
||||
while (iter != AllPlugins.end())
|
||||
{
|
||||
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");
|
||||
const cWebPlugin * Plugin = *iter;
|
||||
tolua_pushusertype(tolua_S, reinterpret_cast<void *>(const_cast<cWebPlugin*>(Plugin)), "const cWebPlugin");
|
||||
lua_rawseti(tolua_S, newTable, index);
|
||||
++iter;
|
||||
++index;
|
||||
}
|
||||
return 1;
|
||||
@ -2155,70 +2131,14 @@ static int tolua_cWebAdmin_GetAllWebTabs(lua_State * tolua_S)
|
||||
|
||||
|
||||
|
||||
/** Binding for cWebAdmin::GetBaseURL.
|
||||
Manual code required because ToLua generates an extra return value */
|
||||
static int tolua_cWebAdmin_GetBaseURL(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::GetBaseURL(Input));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binding for cWebAdmin::GetContentTypeFromFileExt.
|
||||
Manual code required because ToLua generates an extra return value */
|
||||
static int tolua_cWebAdmin_GetContentTypeFromFileExt(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::GetContentTypeFromFileExt(Input));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** Binding for cWebAdmin::GetHTMLEscapedString.
|
||||
Manual code required because ToLua generates an extra return value */
|
||||
static int tolua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S)
|
||||
static int tolua_AllToLua_cWebAdmin_GetHTMLEscapedString(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.CheckParamUserTable(1, "cWebAdmin") ||
|
||||
!S.CheckParamString(2) ||
|
||||
!S.CheckParamEnd(3)
|
||||
)
|
||||
@ -2239,71 +2159,14 @@ static int tolua_cWebAdmin_GetHTMLEscapedString(lua_State * tolua_S)
|
||||
|
||||
|
||||
|
||||
/** 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)
|
||||
static int tolua_AllToLua_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.CheckParamUserTable(1, "cWebAdmin") ||
|
||||
!S.CheckParamString(2) ||
|
||||
!S.CheckParamEnd(3)
|
||||
)
|
||||
@ -2324,6 +2187,27 @@ static int tolua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S)
|
||||
|
||||
|
||||
|
||||
static int tolua_cWebPlugin_GetTabNames(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)
|
||||
{
|
||||
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 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static int tolua_cClientHandle_SendPluginMessage(lua_State * L)
|
||||
{
|
||||
cLuaState S(L);
|
||||
@ -2739,79 +2623,6 @@ 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);
|
||||
@ -3655,9 +3466,6 @@ 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);
|
||||
@ -3678,6 +3486,7 @@ 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);
|
||||
|
||||
@ -3741,13 +3550,13 @@ void cManualBindings::Bind(lua_State * tolua_S)
|
||||
tolua_endmodule(tolua_S);
|
||||
|
||||
tolua_beginmodule(tolua_S, "cWebAdmin");
|
||||
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_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_endmodule(tolua_S);
|
||||
|
||||
tolua_beginmodule(tolua_S, "HTTPRequest");
|
||||
|
@ -466,41 +466,67 @@ 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)
|
||||
{
|
||||
// Function signature:
|
||||
// World:QueueTask(Callback)
|
||||
// Binding for cWorld::QueueTask
|
||||
// Params: function
|
||||
|
||||
// Retrieve the args:
|
||||
cLuaState L(tolua_S);
|
||||
if (
|
||||
!L.CheckParamUserType(1, "cWorld") ||
|
||||
!L.CheckParamNumber (2) ||
|
||||
!L.CheckParamFunction(3)
|
||||
)
|
||||
// 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;
|
||||
}
|
||||
cWorld * World;
|
||||
auto Task = std::make_shared<cLuaState::cCallback>();
|
||||
if (!L.GetStackValues(1, World, Task))
|
||||
{
|
||||
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Cannot read parameters");
|
||||
}
|
||||
if (World == nullptr)
|
||||
|
||||
// Retrieve the args:
|
||||
cWorld * self = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr));
|
||||
if (self == nullptr)
|
||||
{
|
||||
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
|
||||
}
|
||||
if (!Task->IsValid())
|
||||
if (!lua_isfunction(tolua_S, 2))
|
||||
{
|
||||
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter");
|
||||
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1");
|
||||
}
|
||||
|
||||
World->QueueTask([Task](cWorld & a_World)
|
||||
{
|
||||
Task->Call(&a_World);
|
||||
}
|
||||
);
|
||||
// Create a reference to the function:
|
||||
int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
|
||||
if (FnRef == LUA_REFNIL)
|
||||
{
|
||||
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
|
||||
}
|
||||
|
||||
auto ResettableTask = std::make_shared<cLuaWorldTask>(*Plugin, FnRef);
|
||||
Plugin->AddResettable(ResettableTask);
|
||||
self->QueueTask(std::bind(&cLuaWorldTask::Run, ResettableTask, std::placeholders::_1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -550,8 +576,16 @@ static int tolua_cWorld_SetSignLines(lua_State * tolua_S)
|
||||
|
||||
static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
|
||||
{
|
||||
// Function signature:
|
||||
// World:ScheduleTask(NumTicks, Callback)
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Retrieve the args:
|
||||
cLuaState L(tolua_S);
|
||||
@ -563,27 +597,22 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
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");
|
||||
}
|
||||
cWorld * World = reinterpret_cast<cWorld *>(tolua_tousertype(tolua_S, 1, nullptr));
|
||||
if (World == nullptr)
|
||||
{
|
||||
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance");
|
||||
}
|
||||
if (!Task->IsValid())
|
||||
|
||||
// Create a reference to the function:
|
||||
int FnRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX);
|
||||
if (FnRef == LUA_REFNIL)
|
||||
{
|
||||
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not store the callback parameter");
|
||||
return cManualBindings::lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1");
|
||||
}
|
||||
|
||||
World->ScheduleTask(NumTicks, [Task](cWorld & a_World)
|
||||
{
|
||||
Task->Call(&a_World);
|
||||
}
|
||||
);
|
||||
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));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,7 @@
|
||||
#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
|
||||
@ -28,7 +29,8 @@ class cWindow;
|
||||
|
||||
// tolua_begin
|
||||
class cPluginLua :
|
||||
public cPlugin
|
||||
public cPlugin,
|
||||
public cWebPlugin
|
||||
{
|
||||
typedef cPlugin super;
|
||||
|
||||
@ -62,6 +64,36 @@ public:
|
||||
|
||||
|
||||
|
||||
/** 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();
|
||||
|
||||
@ -149,6 +181,14 @@ public:
|
||||
/** 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);
|
||||
|
||||
@ -171,9 +211,11 @@ public:
|
||||
/** 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 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);
|
||||
/** 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);
|
||||
|
||||
/** 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.
|
||||
@ -193,23 +235,29 @@ public:
|
||||
return m_LuaState.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::cCallbackPtr> cLuaCallbacks;
|
||||
typedef std::vector<cLuaState::cRef *> cLuaRefs;
|
||||
|
||||
/** Maps hook types into arrays of Lua function references to call for each hook type */
|
||||
typedef std::map<int, cLuaCallbacks> cHookMap;
|
||||
typedef std::map<int, cLuaRefs> cHookMap;
|
||||
|
||||
|
||||
/** The mutex protecting m_LuaState against multithreaded use. */
|
||||
/** 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;
|
||||
|
||||
@ -222,30 +270,6 @@ protected:
|
||||
|
||||
/** 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)
|
||||
{
|
||||
cCSLock lock(m_CriticalSection);
|
||||
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
|
||||
|
||||
|
||||
|
@ -1965,23 +1965,6 @@ 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)
|
||||
|
@ -332,9 +332,6 @@ 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
|
||||
|
152
src/Bindings/WebPlugin.cpp
Normal file
152
src/Bindings/WebPlugin.cpp
Normal file
@ -0,0 +1,152 @@
|
||||
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
80
src/Bindings/WebPlugin.h
Normal file
80
src/Bindings/WebPlugin.h
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
#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
|
||||
|
||||
|
||||
|
||||
|
@ -225,10 +225,10 @@ public:
|
||||
cWindow * GetWindow(void) { return m_CurrentWindow; } // tolua_export
|
||||
const cWindow * GetWindow(void) const { return m_CurrentWindow; }
|
||||
|
||||
// tolua_begin
|
||||
|
||||
/** Opens the specified window; closes the current one first using CloseWindow() */
|
||||
void OpenWindow(cWindow * a_Window);
|
||||
void OpenWindow(cWindow * a_Window); // Exported in ManualBindings.cpp
|
||||
|
||||
// tolua_begin
|
||||
|
||||
/** 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);
|
||||
|
@ -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 is holding an item while closing their window, dropping it as a pickup...");
|
||||
LOGD("Player holds item! Dropping it...");
|
||||
a_Player.TossHeldItem(a_Player.GetDraggingItem().m_ItemCount);
|
||||
}
|
||||
|
||||
|
425
src/WebAdmin.cpp
425
src/WebAdmin.cpp
@ -2,6 +2,10 @@
|
||||
#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"
|
||||
@ -83,9 +87,9 @@ public:
|
||||
// cWebAdmin:
|
||||
|
||||
cWebAdmin::cWebAdmin(void) :
|
||||
m_TemplateScript("<webadmin_template>"),
|
||||
m_IsInitialized(false),
|
||||
m_IsRunning(false)
|
||||
m_IsRunning(false),
|
||||
m_TemplateScript("<webadmin_template>")
|
||||
{
|
||||
}
|
||||
|
||||
@ -102,9 +106,40 @@ 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 (!LoadIniFile())
|
||||
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))
|
||||
{
|
||||
// WebAdmin is disabled, bail out faking a success
|
||||
return true;
|
||||
@ -112,7 +147,31 @@ bool cWebAdmin::Init(void)
|
||||
|
||||
LOGD("Initialising WebAdmin...");
|
||||
|
||||
Reload();
|
||||
// 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>";
|
||||
}
|
||||
|
||||
// Read the ports to be used:
|
||||
// Note that historically the ports were stored in the "Port" and "PortsIPv6" values
|
||||
@ -165,7 +224,7 @@ void cWebAdmin::Stop(void)
|
||||
|
||||
|
||||
|
||||
bool cWebAdmin::LoadLoginPage(void)
|
||||
bool cWebAdmin::LoadLoginTemplate(void)
|
||||
{
|
||||
cFile File(FILE_IO_PREFIX "webadmin/login_template.html", cFile::fmRead);
|
||||
if (!File.IsOpen())
|
||||
@ -179,8 +238,7 @@ bool cWebAdmin::LoadLoginPage(void)
|
||||
return false;
|
||||
}
|
||||
|
||||
cCSLock Lock(m_CS);
|
||||
m_LoginPage = TemplateContent;
|
||||
m_LoginTemplate = TemplateContent;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -188,89 +246,6 @@ bool cWebAdmin::LoadLoginPage(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())
|
||||
@ -280,20 +255,17 @@ 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))
|
||||
{
|
||||
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;
|
||||
}
|
||||
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.empty() && (BareURL[1] != '~'));
|
||||
bool ShouldWrapInTemplate = ((BareURL.length() > 1) && (BareURL[1] != '~'));
|
||||
|
||||
// Retrieve the request data:
|
||||
auto Data = std::static_pointer_cast<cWebadminRequestData>(a_Request.GetUserData());
|
||||
@ -340,7 +312,6 @@ 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;
|
||||
@ -354,12 +325,59 @@ void cWebAdmin::HandleWebadminRequest(cHTTPServerConnection & a_Connection, cHTT
|
||||
return;
|
||||
}
|
||||
|
||||
// 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());
|
||||
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());
|
||||
a_Connection.FinishResponse();
|
||||
}
|
||||
|
||||
@ -374,7 +392,7 @@ void cWebAdmin::HandleRootRequest(cHTTPServerConnection & a_Connection, cHTTPInc
|
||||
cHTTPOutgoingResponse Resp;
|
||||
Resp.SetContentType("text/html");
|
||||
a_Connection.Send(Resp);
|
||||
a_Connection.Send(m_LoginPage);
|
||||
a_Connection.Send(m_LoginTemplate);
|
||||
a_Connection.FinishResponse();
|
||||
}
|
||||
|
||||
@ -388,7 +406,7 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc
|
||||
std::replace(FileURL.begin(), FileURL.end(), '\\', '/');
|
||||
|
||||
// Remove all leading backslashes:
|
||||
if (!FileURL.empty() && (FileURL[0] == '/'))
|
||||
if (FileURL[0] == '/')
|
||||
{
|
||||
size_t FirstCharToRead = FileURL.find_first_not_of('/');
|
||||
if (FirstCharToRead != AString::npos)
|
||||
@ -400,9 +418,8 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc
|
||||
// Remove all "../" strings:
|
||||
ReplaceString(FileURL, "../", "");
|
||||
|
||||
// Read the file contents and guess its mime-type, based on the extension:
|
||||
bool LoadedSuccessfull = false;
|
||||
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))
|
||||
{
|
||||
@ -410,17 +427,18 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc
|
||||
AString FileContent;
|
||||
if (File.IsOpen() && (File.ReadRestOfFile(FileContent) != -1))
|
||||
{
|
||||
std::swap(Content, FileContent);
|
||||
size_t LastPointPosition = Path.find_last_of('.');
|
||||
if (LastPointPosition != AString::npos)
|
||||
{
|
||||
ContentType = GetContentTypeFromFileExt(Path.substr(LastPointPosition + 1));
|
||||
}
|
||||
LoadedSuccessfull = true;
|
||||
Content = FileContent;
|
||||
}
|
||||
}
|
||||
if (ContentType.empty())
|
||||
|
||||
// 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()))
|
||||
{
|
||||
ContentType = "application/unknown";
|
||||
AString FileExtension = Path.substr(LastPointPosition + 1);
|
||||
ContentType = GetContentTypeFromFileExt(FileExtension);
|
||||
}
|
||||
|
||||
// Send the response:
|
||||
@ -438,36 +456,32 @@ void cWebAdmin::HandleFileRequest(cHTTPServerConnection & a_Connection, cHTTPInc
|
||||
AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension)
|
||||
{
|
||||
static bool IsInitialized = false;
|
||||
static AStringMap ContentTypeMap;
|
||||
static std::map<AString, AString> 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"] = "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
|
||||
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";
|
||||
}
|
||||
|
||||
auto itr = ContentTypeMap.find(StrToLower(a_FileExtension));
|
||||
if (itr == ContentTypeMap.end())
|
||||
AString FileExtension = StrToLower(a_FileExtension);
|
||||
if (ContentTypeMap.find(a_FileExtension) == ContentTypeMap.end())
|
||||
{
|
||||
return AString();
|
||||
return "text/html";
|
||||
}
|
||||
return itr->second;
|
||||
return ContentTypeMap[FileExtension];
|
||||
}
|
||||
|
||||
|
||||
@ -476,49 +490,77 @@ AString cWebAdmin::GetContentTypeFromFileExt(const AString & a_FileExtension)
|
||||
|
||||
sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request)
|
||||
{
|
||||
sWebAdminPage page;
|
||||
auto split = StringSplit(a_Request.Path, "/");
|
||||
sWebAdminPage Page;
|
||||
AStringVector Split = StringSplit(a_Request.Path, "/");
|
||||
|
||||
// If no specific page was requested, return an empty object:
|
||||
if (split.size() <= 2)
|
||||
// Find the plugin that corresponds to the requested path
|
||||
AString FoundPlugin;
|
||||
if (Split.size() > 1)
|
||||
{
|
||||
return page;
|
||||
}
|
||||
|
||||
// Find the WebTab handler responsible for the request:
|
||||
cWebTabPtr tab;
|
||||
{
|
||||
cCSLock Lock(m_CS);
|
||||
for (auto & wt: m_WebTabs)
|
||||
for (PluginList::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr)
|
||||
{
|
||||
if (
|
||||
(wt->m_PluginName == split[1]) &&
|
||||
(wt->m_UrlPath == split[2])
|
||||
)
|
||||
if ((*itr)->GetWebTitle() == Split[1])
|
||||
{
|
||||
tab = wt;
|
||||
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;
|
||||
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 page;
|
||||
// Return the page contents
|
||||
return Page;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cWebAdmin::GetDefaultPage(void)
|
||||
{
|
||||
AString Content;
|
||||
Content += "<h4>Server Name:</h4>";
|
||||
Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>";
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
@ -534,41 +576,6 @@ AString cWebAdmin::GetBaseURL(const AString & a_URL)
|
||||
|
||||
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cWebAdmin::DelWebTab(const AString & a_UrlPath)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
AString cWebAdmin::GetHTMLEscapedString(const AString & a_Input)
|
||||
{
|
||||
AString dst;
|
||||
|
160
src/WebAdmin.h
160
src/WebAdmin.h
@ -89,14 +89,14 @@ struct HTTPTemplateRequest
|
||||
|
||||
|
||||
|
||||
// tolua_begin
|
||||
struct sWebAdminPage
|
||||
{
|
||||
AString Content;
|
||||
AString PluginName;
|
||||
AString TabTitle;
|
||||
AString TabUrlPath;
|
||||
AString ContentType;
|
||||
AString TabName;
|
||||
};
|
||||
// tolua_end
|
||||
|
||||
|
||||
|
||||
@ -111,49 +111,7 @@ class cWebAdmin :
|
||||
public:
|
||||
// tolua_end
|
||||
|
||||
/** 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;
|
||||
typedef std::list< cWebPlugin* > PluginList;
|
||||
|
||||
|
||||
cWebAdmin(void);
|
||||
@ -162,115 +120,81 @@ public:
|
||||
/** Initializes the object. Returns true if successfully initialized and ready to start */
|
||||
bool Init(void);
|
||||
|
||||
/** Starts the HTTP server taking care of the webadmin. Returns true if successful */
|
||||
/** Starts the HTTP server taking care of the admin. Returns true if successful */
|
||||
bool Start(void);
|
||||
|
||||
/** Stops the HTTP server, if it was started. */
|
||||
void Stop(void);
|
||||
|
||||
/** Loads the login template into m_LoginPage.
|
||||
Returns true if the loading succeeds, false if not. */
|
||||
bool LoadLoginPage(void);
|
||||
/** Loads the login template. Returns true if the loading succeeds, false if not. */
|
||||
bool LoadLoginTemplate(void);
|
||||
|
||||
/** Returns a copy of all the registered web tabs.
|
||||
Exported to Lua in ManualBindings.cpp. */
|
||||
cWebTabPtrs GetAllWebTabs(void) { return m_WebTabs; }
|
||||
void AddPlugin(cWebPlugin * a_Plugin);
|
||||
void RemovePlugin(cWebPlugin * a_Plugin);
|
||||
|
||||
/** 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);
|
||||
// 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 <<
|
||||
|
||||
// tolua_begin
|
||||
|
||||
/** 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);
|
||||
sWebAdminPage GetPage(const HTTPRequest & a_Request);
|
||||
|
||||
/** Returns the list of ports on which the webadmin is configured to listen. */
|
||||
/** Returns the contents of the default page - the list of plugins and players */
|
||||
AString GetDefaultPage(void);
|
||||
|
||||
/** 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);
|
||||
|
||||
/** Returns the list of ports used for the webadmin. */
|
||||
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
|
||||
|
||||
/** 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. */
|
||||
/** Escapes text passed into it, so it can be embedded into html. */
|
||||
static AString GetHTMLEscapedString(const AString & a_Input);
|
||||
|
||||
/** Escapes the string for use in an URL
|
||||
Exported to Lua in ManualBindings.cpp. */
|
||||
/** Escapes the string for use in an URL */
|
||||
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 an empty string.
|
||||
Exported to Lua in ManualBindings.cpp. */
|
||||
/** Returns the content type from the file extension. If the extension isn't in the list, the function returns "text/html" */
|
||||
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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user