1
0

Revert "Lua callback"

This commit is contained in:
Mattes D 2016-03-21 09:58:15 +01:00
parent 8fc9cfa411
commit e3d34d9917
22 changed files with 1970 additions and 1333 deletions

View File

@ -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)" },

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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);
} ;

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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)