From 08624348f4e84fdc9909eb5de4478443bad8cc8d Mon Sep 17 00:00:00 2001 From: Mattes D Date: Thu, 23 Apr 2015 19:41:01 +0200 Subject: Implemented cPluginManager:DoWithPlugin(), fixed ForEachPlugin(). Both functions are exported as static. --- MCServer/Plugins/APIDump/Classes/Plugins.lua | 7 +- MCServer/Plugins/Debuggers/Debuggers.lua | 34 ++++ src/Bindings/LuaState.cpp | 108 ++++++++-- src/Bindings/LuaState.h | 75 ++++--- src/Bindings/ManualBindings.cpp | 281 ++++++++++++++++----------- 5 files changed, 332 insertions(+), 173 deletions(-) diff --git a/MCServer/Plugins/APIDump/Classes/Plugins.lua b/MCServer/Plugins/APIDump/Classes/Plugins.lua index fa502ccfc..ff5d4a180 100644 --- a/MCServer/Plugins/APIDump/Classes/Plugins.lua +++ b/MCServer/Plugins/APIDump/Classes/Plugins.lua @@ -66,12 +66,13 @@ cPluginManager.AddHook(cPluginManager.HOOK_CHAT, OnChatMessage); { Params = "Command, Callback, HelpString", Return = "[bool]", Notes = "Binds a console command with the specified callback function and help string. By common convention, providing an empty string for HelpString will hide the command from the \"help\" console command. Returns true if successful, logs to console and returns no value on error. The callback uses the following signature:
function(Split)
The Split parameter contains an array-table of the words that the admin has typed. If the callback returns true, the command is assumed to have executed successfully; in all other cases the server issues a warning to the console that the command is unknown (this is so that subcommands can be implemented)." }, }, CallPlugin = { Params = "PluginName, FunctionName, [FunctionArgs...]", Return = "[FunctionRets]", Notes = "(STATIC) Calls the specified function in the specified plugin, passing all the given arguments to it. If it succeeds, it returns all the values returned by that function. If it fails, returns no value at all. Note that only strings, numbers, bools, nils and classes can be used for parameters and return values; tables and functions cannot be copied across plugins." }, + DoWithPlugin = { Params = "PluginName, CallbackFn", Return = "bool", Notes = "(STATIC) Calls the CallbackFn for the specified plugin, if found. A plugin can be found even if it is currently unloaded, disabled or errored, the callback should check the plugin status. If the plugin is not found, this function returns false, otherwise it returns the bool value that the callback has returned. The CallbackFn has the following signature:
function ({{cPlugin|Plugin}})
" }, ExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Executes the command as if given by the specified Player. Checks permissions." }, FindPlugins = { Params = "", Return = "", Notes = "OBSOLETE, use RefreshPluginList() instead"}, ForceExecuteCommand = { Params = "{{cPlayer|Player}}, CommandStr", Return = "{{cPluginManager#CommandResult|CommandResult}}", Notes = "Same as ExecuteCommand, but doesn't check permissions" }, - ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature:
function(Command, Permission, HelpString)
. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, - ForEachConsoleCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature:
function (Command, HelpString)
. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, - ForEachPlugin = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature:
function ({{cPlugin|Plugin}})
. If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, + ForEachCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindCommand(). The CallbackFn has the following signature:
function(Command, Permission, HelpString)
If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, + ForEachConsoleCommand = { Params = "CallbackFn", Return = "bool", Notes = "Calls the CallbackFn function for each command that has been bound using BindConsoleCommand(). The CallbackFn has the following signature:
function (Command, HelpString)
If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, + ForEachPlugin = { Params = "CallbackFn", Return = "bool", Notes = "(STATIC) Calls the CallbackFn function for each plugin that is currently discovered by MCServer (including disabled, unloaded and errrored plugins). The CallbackFn has the following signature:
function ({{cPlugin|Plugin}})
If the callback returns true, the enumeration is aborted and this API function returns false; if it returns false or no value, the enumeration continues with the next command, and the API function returns true." }, Get = { Params = "", Return = "cPluginManager", Notes = "(STATIC) Returns the single instance of the plugin manager" }, GetAllPlugins = { Params = "", Return = "table", Notes = "Returns a table (dictionary) of all plugins, [name => value], where value is a valid {{cPlugin}} if the plugin is loaded, or the bool value false if the plugin is not loaded." }, GetCommandPermission = { Params = "Command", Return = "Permission", Notes = "Returns the permission needed for executing the specified command" }, diff --git a/MCServer/Plugins/Debuggers/Debuggers.lua b/MCServer/Plugins/Debuggers/Debuggers.lua index 01a5de81e..6580e9dbc 100644 --- a/MCServer/Plugins/Debuggers/Debuggers.lua +++ b/MCServer/Plugins/Debuggers/Debuggers.lua @@ -62,6 +62,7 @@ function Initialize(a_Plugin) -- TestRankMgr() TestFileExt() TestFileLastMod() + TestPluginInterface() local LastSelfMod = cFile:GetLastModificationTime(a_Plugin:GetLocalFolder() .. "/Debuggers.lua") LOG("Debuggers.lua last modified on " .. os.date("%Y-%m-%dT%H:%M:%S", LastSelfMod)) @@ -75,6 +76,18 @@ function Initialize(a_Plugin) ) --]] + -- Test the crash in #1889: + cPluginManager:AddHook(cPluginManager.HOOK_PLAYER_RIGHT_CLICKING_ENTITY, + function (a_CBPlayer, a_CBEntity) + a_CBPlayer:GetWorld():DoWithEntityByID( -- This will crash the server in #1889 + a_CBEntity:GetUniqueID(), + function(Entity) + LOG("RightClicking an entity, crash #1889 fixed") + end + ) + end + ) + return true end; @@ -82,6 +95,27 @@ end; +function TestPluginInterface() + cPluginManager:DoWithPlugin("Core", + function (a_CBPlugin) + if (a_CBPlugin:GetStatus() == cPluginManager.psLoaded) then + LOG("Core plugin was found, version " .. a_CBPlugin:GetVersion()) + else + LOG("Core plugin is not loaded") + end + end + ) + + cPluginManager:ForEachPlugin( + function (a_CBPlugin) + LOG("Plugin in " .. a_CBPlugin:GetFolderName() .. " has an API name of " .. a_CBPlugin:GetName() .. " and status " .. a_CBPlugin:GetStatus()) + end + ) +end + + + + function TestFileExt() assert(cFile:ChangeFileExt("fileless_dir/", "new") == "fileless_dir/") assert(cFile:ChangeFileExt("fileless_dir/", ".new") == "fileless_dir/") diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp index 25c77a652..468e54f32 100644 --- a/src/Bindings/LuaState.cpp +++ b/src/Bindings/LuaState.cpp @@ -740,6 +740,18 @@ void cLuaState::Push(cPlayer * a_Player) +void cLuaState::Push(cPlugin * a_Plugin) +{ + ASSERT(IsValid()); + + tolua_pushusertype(m_LuaState, a_Plugin, "cPlugin"); + m_NumCurrentFunctionArgs += 1; +} + + + + + void cLuaState::Push(cPluginLua * a_Plugin) { ASSERT(IsValid()); @@ -911,6 +923,20 @@ void cLuaState::PushUserType(void * a_Object, const char * a_Type) +void cLuaState::GetStackValue(int a_StackPos, AString & a_Value) +{ + size_t len = 0; + const char * data = lua_tolstring(m_LuaState, a_StackPos, &len); + if (data != nullptr) + { + a_Value.assign(data, len); + } +} + + + + + void cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal) { a_ReturnedVal = (tolua_toboolean(m_LuaState, a_StackPos, a_ReturnedVal ? 1 : 0) > 0); @@ -920,25 +946,20 @@ void cLuaState::GetStackValue(int a_StackPos, bool & a_ReturnedVal) -void cLuaState::GetStackValue(int a_StackPos, AString & a_Value) +void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref) { - size_t len = 0; - const char * data = lua_tolstring(m_LuaState, a_StackPos, &len); - if (data != nullptr) - { - a_Value.assign(data, len); - } + a_Ref.RefStack(*this, a_StackPos); } -void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal) { if (lua_isnumber(m_LuaState, a_StackPos)) { - a_ReturnedVal = (int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); + a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); } } @@ -946,23 +967,27 @@ void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal) -void cLuaState::GetStackValue(int a_StackPos, double & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) { - if (lua_isnumber(m_LuaState, a_StackPos)) + if (!lua_isnumber(m_LuaState, a_StackPos)) { - a_ReturnedVal = tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal); + return; } + a_ReturnedVal = static_cast(Clamp( + static_cast(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)), + static_cast(wSunny), static_cast(wThunderstorm)) + ); } -void cLuaState::GetStackValue(int a_StackPos, eWeather & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, int & a_ReturnedVal) { if (lua_isnumber(m_LuaState, a_StackPos)) { - a_ReturnedVal = (eWeather)Clamp((int)tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal), (int)wSunny, (int)wThunderstorm); + a_ReturnedVal = static_cast(tolua_tonumber(m_LuaState, a_StackPos, a_ReturnedVal)); } } @@ -988,7 +1013,7 @@ void cLuaState::GetStackValue(int a_StackPos, pBoundingBox & a_ReturnedVal) -void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal) +void cLuaState::GetStackValue(int a_StackPos, pPluginManager & a_ReturnedVal) { if (lua_isnil(m_LuaState, a_StackPos)) { @@ -996,9 +1021,9 @@ void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal) return; } tolua_Error err; - if (tolua_isusertype(m_LuaState, a_StackPos, "cWorld", false, &err)) + if (tolua_isusertype(m_LuaState, a_StackPos, "cPluginManager", false, &err)) { - a_ReturnedVal = *((cWorld **)lua_touserdata(m_LuaState, a_StackPos)); + a_ReturnedVal = *(reinterpret_cast(lua_touserdata(m_LuaState, a_StackPos))); } } @@ -1006,9 +1031,54 @@ void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal) -void cLuaState::GetStackValue(int a_StackPos, cRef & a_Ref) +void cLuaState::GetStackValue(int a_StackPos, pRoot & a_ReturnedVal) { - a_Ref.RefStack(*this, a_StackPos); + if (lua_isnil(m_LuaState, a_StackPos)) + { + a_ReturnedVal = nullptr; + return; + } + tolua_Error err; + if (tolua_isusertype(m_LuaState, a_StackPos, "cRoot", false, &err)) + { + a_ReturnedVal = *(reinterpret_cast(lua_touserdata(m_LuaState, a_StackPos))); + } +} + + + + + +void cLuaState::GetStackValue(int a_StackPos, pScoreboard & a_ReturnedVal) +{ + if (lua_isnil(m_LuaState, a_StackPos)) + { + a_ReturnedVal = nullptr; + return; + } + tolua_Error err; + if (tolua_isusertype(m_LuaState, a_StackPos, "cScoreboard", false, &err)) + { + a_ReturnedVal = *(reinterpret_cast(lua_touserdata(m_LuaState, a_StackPos))); + } +} + + + + + +void cLuaState::GetStackValue(int a_StackPos, pWorld & a_ReturnedVal) +{ + if (lua_isnil(m_LuaState, a_StackPos)) + { + a_ReturnedVal = nullptr; + return; + } + tolua_Error err; + if (tolua_isusertype(m_LuaState, a_StackPos, "cWorld", false, &err)) + { + a_ReturnedVal = *(reinterpret_cast(lua_touserdata(m_LuaState, a_StackPos))); + } } diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h index 159483634..b77556117 100644 --- a/src/Bindings/LuaState.h +++ b/src/Bindings/LuaState.h @@ -37,34 +37,41 @@ extern "C" -class cWorld; -class cPlayer; +class cBlockEntity; +class cBoundingBox; +class cChunkDesc; +class cClientHandle; +class cCraftingGrid; +class cCraftingRecipe; class cEntity; -class cProjectileEntity; -class cMonster; +class cHopperEntity; class cItem; class cItems; -class cClientHandle; +class cLuaServerHandle; +class cLuaTCPLink; +class cLuaUDPEndpoint; +class cMonster; class cPickup; -class cChunkDesc; -class cCraftingGrid; -class cCraftingRecipe; -struct TakeDamageInfo; -class cWindow; +class cPlayer; +class cPlugin; class cPluginLua; -struct HTTPRequest; +class cPluginManager; +class cProjectileEntity; +class cRoot; +class cScoreboard; +class cTNTEntity; class cWebAdmin; +class cWindow; +class cWorld; +struct HTTPRequest; struct HTTPTemplateRequest; -class cTNTEntity; -class cHopperEntity; -class cBlockEntity; -class cBoundingBox; -class cLuaTCPLink; -class cLuaServerHandle; -class cLuaUDPEndpoint; +struct TakeDamageInfo; -typedef cBoundingBox * pBoundingBox; -typedef cWorld * pWorld; +typedef cBoundingBox * pBoundingBox; +typedef cPluginManager * pPluginManager; +typedef cRoot * pRoot; +typedef cScoreboard * pScoreboard; +typedef cWorld * pWorld; @@ -217,6 +224,7 @@ public: void Push(cMonster * a_Monster); void Push(cPickup * a_Pickup); void Push(cPlayer * a_Player); + void Push(cPlugin * a_Plugin); void Push(cPluginLua * a_Plugin); void Push(cProjectileEntity * a_ProjectileEntity); void Push(cTNTEntity * a_TNTEntity); @@ -231,30 +239,19 @@ public: void Push(void * a_Ptr); void Push(std::chrono::milliseconds a_time); - /** Retrieve value at a_StackPos, if it is a valid bool. If not, a_Value is unchanged */ - void GetStackValue(int a_StackPos, bool & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid string. If not, a_Value is unchanged */ + // GetStackValue() retrieves the value at a_StackPos, if it is a valid type. If not, a_Value is unchanged. + // Enum values are clamped to their allowed range. void GetStackValue(int a_StackPos, AString & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */ - void GetStackValue(int a_StackPos, int & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid number. If not, a_Value is unchanged */ + void GetStackValue(int a_StackPos, bool & a_Value); + void GetStackValue(int a_StackPos, cRef & a_Ref); void GetStackValue(int a_StackPos, double & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid number, converting and clamping it to eWeather. - If not, a_Value is unchanged. */ void GetStackValue(int a_StackPos, eWeather & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid cBoundingBox class. If not, a_Value is unchanged */ + void GetStackValue(int a_StackPos, int & a_Value); void GetStackValue(int a_StackPos, pBoundingBox & a_Value); - - /** Retrieve value at a_StackPos, if it is a valid cWorld class. If not, a_Value is unchanged */ + void GetStackValue(int a_StackPos, pPluginManager & a_Value); + void GetStackValue(int a_StackPos, pRoot & a_Value); + void GetStackValue(int a_StackPos, pScoreboard & a_Value); void GetStackValue(int a_StackPos, pWorld & a_Value); - - /** Store the value at a_StackPos as a reference. */ - void GetStackValue(int a_StackPos, cRef & a_Ref); /** Call the specified Lua function. Returns true if call succeeded, false if there was an error. diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 4c8d9ff96..ddd77b75b 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -67,7 +67,7 @@ static int lua_do_error(lua_State* L, const char * a_pFormat, ...) // Insert function name into error msg AString msg(a_pFormat); - ReplaceString(msg, "#funcname#", entry.name?entry.name:"?"); + ReplaceString(msg, "#funcname#", (entry.name != nullptr) ? entry.name : "?"); // Copied from luaL_error and modified va_list argp; @@ -493,94 +493,130 @@ static int tolua_cFile_GetFolderContents(lua_State * tolua_S) +/** Binds the DoWith(ItemName) functions of regular classes. */ template < class Ty1, class Ty2, - bool (Ty1::*Func1)(const AString &, cItemCallback &) + bool (Ty1::*DoWithFn)(const AString &, cItemCallback &) > -static int tolua_DoWith(lua_State* tolua_S) +static int tolua_DoWith(lua_State * tolua_S) { - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 2) && (NumArgs != 3)) + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamString(2) || + !L.CheckParamFunction(3) + ) { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 2 or 3 arguments, got %i", NumArgs); + return 0; } - Ty1 * self = (Ty1 *) tolua_tousertype(tolua_S, 1, nullptr); - - const char * ItemName = tolua_tocppstring(tolua_S, 2, ""); - if ((ItemName == nullptr) || (ItemName[0] == 0)) + // Get parameters: + Ty1 * Self; + AString ItemName; + cLuaState::cRef FnRef; + L.GetStackValues(1, Self, ItemName, FnRef); + if (Self == nullptr) { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1", NumArgs); + return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'"); } - if (!lua_isfunction(tolua_S, 3)) + if (ItemName.empty() || (ItemName[0] == 0)) { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1"); + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2"); } - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 3) + class cLuaCallback : public cItemCallback { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #3", NumArgs); } + + private: + virtual bool Item(Ty2 * a_Item) override + { + bool ret = false; + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret); + return ret; + } + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); + + // Call the DoWith function: + bool res = (Self->*DoWithFn)(ItemName, Callback); + + // Push the result as the return value: + L.Push(res); + return 1; +} + + + + + +/** Template for static functions DoWith(ItemName), on a type that has a static ::Get() function. */ +template < + class Ty1, + class Ty2, + bool (Ty1::*DoWithFn)(const AString &, cItemCallback &) +> +static int tolua_StaticDoWith(lua_State * tolua_S) +{ + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamString(2) || + !L.CheckParamFunction(3) + ) + { + return 0; } - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) + // Get parameters: + AString ItemName; + cLuaState::cRef FnRef; + L.GetStackValues(2, ItemName, FnRef); + if (ItemName.empty() || (ItemName[0] == 0)) { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #2", NumArgs); + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1"); + } + if (!FnRef.IsValid()) + { + return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a valid callback function for parameter #2"); } class cLuaCallback : public cItemCallback { public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef): - LuaState(a_LuaState), - FuncRef(a_FuncRef), - TableRef(a_TableRef) + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) { } private: virtual bool Item(Ty2 * a_Item) override { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; // Abort enumeration - } - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ + bool ret = false; + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, ret); + return ret; } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - - bool bRetVal = (self->*Func1)(ItemName, Callback); + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + } Callback(L, FnRef); - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + // Call the DoWith function: + bool res = (Ty1::Get()->*DoWithFn)(ItemName, Callback); - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); + // Push the result as the return value: + L.Push(res); return 1; } @@ -916,6 +952,9 @@ static int tolua_ForEachInBox(lua_State * tolua_S) } private: + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + // cItemCallback overrides: virtual bool Item(Ty2 * a_Item) override { @@ -929,8 +968,6 @@ static int tolua_ForEachInBox(lua_State * tolua_S) return res; } - cLuaState & m_LuaState; - cLuaState::cRef & m_FnRef; } Callback(L, FnRef); bool bRetVal = (Self->*Func1)(*Box, Callback); @@ -953,86 +990,105 @@ template < > static int tolua_ForEach(lua_State * tolua_S) { - int NumArgs = lua_gettop(tolua_S) - 1; /* This includes 'self' */ - if ((NumArgs != 1) && (NumArgs != 2)) + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamFunction(2) || + !L.CheckParamEnd(3) + ) { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Requires 1 or 2 arguments, got %i", NumArgs); + return 0; } - Ty1 * self = (Ty1 *)tolua_tousertype(tolua_S, 1, nullptr); + // Get the params: + Ty1 * self; + L.GetStackValues(1, self); + cLuaState::cRef FnRef(L, 2); if (self == nullptr) { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); + return lua_do_error(tolua_S, "Error in function call '#funcname#': Invalid 'self'."); } - if (!lua_isfunction(tolua_S, 2)) + class cLuaCallback : public cItemCallback { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); - } + public: + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) + { + } - /* luaL_ref gets reference to value on top of the stack, the table is the last argument and therefore on the top */ - int TableRef = LUA_REFNIL; - if (NumArgs == 2) - { - TableRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (TableRef == LUA_REFNIL) + private: + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + + virtual bool Item(Ty2 * a_Item) override { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get value reference of parameter #2"); + bool res = false; // By default continue the enumeration + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res); + return res; } - } + } Callback(L, FnRef); - /* table value is popped, and now function is on top of the stack */ - int FuncRef = luaL_ref(tolua_S, LUA_REGISTRYINDEX); - if (FuncRef == LUA_REFNIL) + // Call the enumeration: + bool res = (self->*Func1)(Callback); + + // Push the return value: + L.Push(res); + return 1; +} + + + + + +/** Implements bindings for ForEach() functions in a class that is static (has a ::Get() static function). */ +template < + class Ty1, + class Ty2, + bool (Ty1::*Func1)(cItemCallback &) +> +static int tolua_StaticForEach(lua_State * tolua_S) +{ + // Check params: + cLuaState L(tolua_S); + if ( + !L.CheckParamFunction(2) || + !L.CheckParamEnd(3) + ) { - return lua_do_error(tolua_S, "Error in function call '#funcname#': Could not get function reference of parameter #1"); + return 0; } + // Get the params: + cLuaState::cRef FnRef(L, 2); + class cLuaCallback : public cItemCallback { public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef): - LuaState(a_LuaState), - FuncRef(a_FuncRef), - TableRef(a_TableRef) + cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FnRef): + m_LuaState(a_LuaState), + m_FnRef(a_FnRef) { } private: + cLuaState & m_LuaState; + cLuaState::cRef & m_FnRef; + virtual bool Item(Ty2 * a_Item) override { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype(LuaState, a_Item, Ty2::GetClassStatic()); - if (TableRef != LUA_REFNIL) - { - lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ - } - - int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); - if (cLuaState::ReportErrors(LuaState, s)) - { - return true; /* Abort enumeration */ - } - - if (lua_isboolean(LuaState, -1)) - { - return (tolua_toboolean(LuaState, -1, 0) > 0); - } - return false; /* Continue enumeration */ + bool res = false; // By default continue the enumeration + m_LuaState.Call(m_FnRef, a_Item, cLuaState::Return, res); + return res; } - lua_State * LuaState; - int FuncRef; - int TableRef; - } Callback(tolua_S, FuncRef, TableRef); - - bool bRetVal = (self->*Func1)(Callback); + } Callback(L, FnRef); - /* Unreference the values again, so the LUA_REGISTRYINDEX can make place for other references */ - luaL_unref(tolua_S, LUA_REGISTRYINDEX, TableRef); - luaL_unref(tolua_S, LUA_REGISTRYINDEX, FuncRef); + // Call the enumeration: + bool res = (Ty1::Get()->*Func1)(Callback); - /* Push return value on stack */ - tolua_pushboolean(tolua_S, bRetVal); + // Push the return value: + L.Push(res); return 1; } @@ -3814,10 +3870,11 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "BindCommand", tolua_cPluginManager_BindCommand); tolua_function(tolua_S, "BindConsoleCommand", tolua_cPluginManager_BindConsoleCommand); tolua_function(tolua_S, "CallPlugin", tolua_cPluginManager_CallPlugin); + tolua_function(tolua_S, "DoWithPlugin", tolua_StaticDoWith); tolua_function(tolua_S, "FindPlugins", tolua_cPluginManager_FindPlugins); tolua_function(tolua_S, "ForEachCommand", tolua_cPluginManager_ForEachCommand); tolua_function(tolua_S, "ForEachConsoleCommand", tolua_cPluginManager_ForEachConsoleCommand); - tolua_function(tolua_S, "ForEachPlugin", tolua_ForEach); + tolua_function(tolua_S, "ForEachPlugin", tolua_StaticForEach); tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); tolua_function(tolua_S, "GetCurrentPlugin", tolua_cPluginManager_GetCurrentPlugin); tolua_function(tolua_S, "GetPlugin", tolua_cPluginManager_GetPlugin); -- cgit v1.2.3