diff options
author | Mattes D <github@xoft.cz> | 2015-04-19 10:57:41 +0200 |
---|---|---|
committer | Mattes D <github@xoft.cz> | 2015-04-19 10:57:41 +0200 |
commit | a9b5a6c3a63d8500f3c512574fd802d40b562661 (patch) | |
tree | e6d0d362ed42116d6c9deaaa789287f464569514 /src/Bindings | |
parent | Merge pull request #1869 from mathias-gh/master (diff) | |
download | cuberite-a9b5a6c3a63d8500f3c512574fd802d40b562661.tar cuberite-a9b5a6c3a63d8500f3c512574fd802d40b562661.tar.gz cuberite-a9b5a6c3a63d8500f3c512574fd802d40b562661.tar.bz2 cuberite-a9b5a6c3a63d8500f3c512574fd802d40b562661.tar.lz cuberite-a9b5a6c3a63d8500f3c512574fd802d40b562661.tar.xz cuberite-a9b5a6c3a63d8500f3c512574fd802d40b562661.tar.zst cuberite-a9b5a6c3a63d8500f3c512574fd802d40b562661.zip |
Diffstat (limited to 'src/Bindings')
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 212 | ||||
-rw-r--r-- | src/Bindings/Plugin.cpp | 34 | ||||
-rw-r--r-- | src/Bindings/Plugin.h | 95 | ||||
-rw-r--r-- | src/Bindings/PluginLua.cpp | 19 | ||||
-rw-r--r-- | src/Bindings/PluginLua.h | 5 | ||||
-rw-r--r-- | src/Bindings/PluginManager.cpp | 315 | ||||
-rw-r--r-- | src/Bindings/PluginManager.h | 142 |
7 files changed, 458 insertions, 364 deletions
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index 6e579b364..aebdbc34b 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -513,7 +513,7 @@ static int tolua_DoWith(lua_State* tolua_S) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a non-empty string for parameter #1", NumArgs); } - if (!lua_isfunction( tolua_S, 3)) + if (!lua_isfunction(tolua_S, 3)) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #2", NumArgs); } @@ -539,20 +539,21 @@ static int tolua_DoWith(lua_State* tolua_S) class cLuaCallback : public cItemCallback<Ty2> { public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - , TableRef( a_TableRef) - {} + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef): + LuaState(a_LuaState), + FuncRef(a_FuncRef), + TableRef(a_TableRef) + { + } private: virtual bool Item(Ty2 * a_Item) override { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + 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 */ + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ } int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); @@ -633,7 +634,8 @@ static int tolua_DoWithID(lua_State* tolua_S) LuaState(a_LuaState), FuncRef(a_FuncRef), TableRef(a_TableRef) - {} + { + } private: virtual bool Item(Ty2 * a_Item) override @@ -699,7 +701,7 @@ static int tolua_DoWithXYZ(lua_State* tolua_S) int ItemX = ((int)tolua_tonumber(tolua_S, 2, 0)); int ItemY = ((int)tolua_tonumber(tolua_S, 3, 0)); int ItemZ = ((int)tolua_tonumber(tolua_S, 4, 0)); - if (!lua_isfunction( tolua_S, 5)) + if (!lua_isfunction(tolua_S, 5)) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #4"); } @@ -725,20 +727,21 @@ static int tolua_DoWithXYZ(lua_State* tolua_S) class cLuaCallback : public cItemCallback<Ty2> { public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - , TableRef( a_TableRef) - {} + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef): + LuaState(a_LuaState), + FuncRef(a_FuncRef), + TableRef(a_TableRef) + { + } private: virtual bool Item(Ty2 * a_Item) override { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + 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 */ + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ } int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); @@ -794,7 +797,7 @@ static int tolua_ForEachInChunk(lua_State * tolua_S) int ChunkX = ((int)tolua_tonumber(tolua_S, 2, 0)); int ChunkZ = ((int)tolua_tonumber(tolua_S, 3, 0)); - if (!lua_isfunction( tolua_S, 4)) + if (!lua_isfunction(tolua_S, 4)) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #3"); } @@ -820,20 +823,21 @@ static int tolua_ForEachInChunk(lua_State * tolua_S) class cLuaCallback : public cItemCallback<Ty2> { public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - , TableRef( a_TableRef) - {} + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef): + LuaState(a_LuaState), + FuncRef(a_FuncRef), + TableRef(a_TableRef) + { + } private: virtual bool Item(Ty2 * a_Item) override { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ + 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 */ + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ } int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); @@ -908,7 +912,8 @@ static int tolua_ForEachInBox(lua_State * tolua_S) cLuaCallback(cLuaState & a_LuaState, cLuaState::cRef & a_FuncRef) : m_LuaState(a_LuaState), m_FnRef(a_FuncRef) - {} + { + } private: // cItemCallback<Ty2> overrides: @@ -960,7 +965,7 @@ static int tolua_ForEach(lua_State * tolua_S) return lua_do_error(tolua_S, "Error in function call '#funcname#': Not called on an object instance"); } - if (!lua_isfunction( tolua_S, 2)) + if (!lua_isfunction(tolua_S, 2)) { return lua_do_error(tolua_S, "Error in function call '#funcname#': Expected a function for parameter #1"); } @@ -986,20 +991,21 @@ static int tolua_ForEach(lua_State * tolua_S) class cLuaCallback : public cItemCallback<Ty2> { public: - cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - , TableRef( a_TableRef) - {} + cLuaCallback(lua_State* a_LuaState, int a_FuncRef, int a_TableRef): + LuaState(a_LuaState), + FuncRef(a_FuncRef), + TableRef(a_TableRef) + { + } private: virtual bool Item(Ty2 * a_Item) override { - lua_rawgeti( LuaState, LUA_REGISTRYINDEX, FuncRef); /* Push function reference */ - tolua_pushusertype( LuaState, a_Item, Ty2::GetClassStatic()); + 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 */ + lua_rawgeti(LuaState, LUA_REGISTRYINDEX, TableRef); /* Push table reference */ } int s = lua_pcall(LuaState, (TableRef == LUA_REFNIL ? 1 : 2), 1, 0); @@ -1010,7 +1016,7 @@ static int tolua_ForEach(lua_State * tolua_S) if (lua_isboolean(LuaState, -1)) { - return (tolua_toboolean( LuaState, -1, 0) > 0); + return (tolua_toboolean(LuaState, -1, 0) > 0); } return false; /* Continue enumeration */ } @@ -1447,29 +1453,12 @@ static int tolua_cWorld_ScheduleTask(lua_State * tolua_S) static int tolua_cPluginManager_GetAllPlugins(lua_State * tolua_S) { - cPluginManager * self = (cPluginManager *)tolua_tousertype(tolua_S, 1, nullptr); - - const cPluginManager::PluginMap & AllPlugins = self->GetAllPlugins(); + // API function no longer available: + LOGWARNING("cPluginManager:GetAllPlugins() is no longer available, use cPluginManager:ForEachPlugin() instead"); + cLuaState::LogStackTrace(tolua_S); + // Return an empty table: lua_newtable(tolua_S); - int index = 1; - cPluginManager::PluginMap::const_iterator iter = AllPlugins.begin(); - while (iter != AllPlugins.end()) - { - const cPlugin* Plugin = iter->second; - tolua_pushstring(tolua_S, iter->first.c_str()); - if (Plugin != nullptr) - { - tolua_pushusertype(tolua_S, (void *)Plugin, "const cPlugin"); - } - else - { - tolua_pushboolean(tolua_S, 0); - } - lua_rawset(tolua_S, -3); - ++iter; - ++index; - } return 1; } @@ -1493,6 +1482,18 @@ static int tolua_cPluginManager_GetCurrentPlugin(lua_State * S) +static int tolua_cPluginManager_GetPlugin(lua_State * tolua_S) +{ + // API function no longer available: + LOGWARNING("cPluginManager:GetPlugin() is no longer available. Use cPluginManager:DoWithPlugin() or cPluginManager:CallPlugin() instead."); + cLuaState::LogStackTrace(tolua_S); + return 0; +} + + + + + static int tolua_cPluginManager_LogStackTrace(lua_State * S) { cLuaState::LogStackTrace(S); @@ -1694,10 +1695,11 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) class cLuaCallback : public cPluginManager::cCommandEnumCallback { public: - cLuaCallback(lua_State * a_LuaState, int a_FuncRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - {} + cLuaCallback(lua_State * a_LuaState, int a_FuncRef): + LuaState(a_LuaState), + FuncRef(a_FuncRef) + { + } private: virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override @@ -1717,7 +1719,7 @@ static int tolua_cPluginManager_ForEachCommand(lua_State * tolua_S) if (lua_isboolean(LuaState, -1)) { - return (tolua_toboolean( LuaState, -1, 0) > 0); + return (tolua_toboolean(LuaState, -1, 0) > 0); } return false; /* Continue enumeration */ } @@ -1771,10 +1773,11 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) class cLuaCallback : public cPluginManager::cCommandEnumCallback { public: - cLuaCallback(lua_State * a_LuaState, int a_FuncRef) - : LuaState( a_LuaState) - , FuncRef( a_FuncRef) - {} + cLuaCallback(lua_State * a_LuaState, int a_FuncRef): + LuaState(a_LuaState), + FuncRef(a_FuncRef) + { + } private: virtual bool Command(const AString & a_Command, const cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString) override @@ -1794,7 +1797,7 @@ static int tolua_cPluginManager_ForEachConsoleCommand(lua_State * tolua_S) if (lua_isboolean(LuaState, -1)) { - return (tolua_toboolean( LuaState, -1, 0) > 0); + return (tolua_toboolean(LuaState, -1, 0) > 0); } return false; /* Continue enumeration */ } @@ -2011,7 +2014,11 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) virtual bool Item(cPlugin * a_Plugin) override { - m_NumReturns = ((cPluginLua *)a_Plugin)->CallFunctionFromForeignState( + if (!a_Plugin->IsLoaded()) + { + return false; + } + m_NumReturns = static_cast<cPluginLua *>(a_Plugin)->CallFunctionFromForeignState( m_FunctionName, m_SrcLuaState, 4, lua_gettop(m_SrcLuaState) ); return true; @@ -2019,9 +2026,6 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) } Callback(FunctionName, L); if (!cPluginManager::Get()->DoWithPlugin(PluginName, Callback)) { - // TODO 2014_01_20 _X: This might be too much logging, plugins cannot know if other plugins are loaded (async) - LOGWARNING("cPluginManager::CallPlugin: No such plugin name (\"%s\")", PluginName.c_str()); - L.LogStackTrace(); return 0; } return Callback.m_NumReturns; @@ -2031,6 +2035,21 @@ static int tolua_cPluginManager_CallPlugin(lua_State * tolua_S) +static int tolua_cPluginManager_FindPlugins(lua_State * tolua_S) +{ + // API function no longer exists: + LOGWARNING("cPluginManager:FindPlugins() is obsolete, use cPluginManager:RefreshPluginList() instead!"); + cLuaState::LogStackTrace(tolua_S); + + // Still, do the actual work performed by the API function when it existed: + cPluginManager::Get()->RefreshPluginList(); + return 0; +} + + + + + static int tolua_cWorld_ChunkStay(lua_State * tolua_S) { /* Function signature: @@ -2337,40 +2356,40 @@ static int tolua_cPluginLua_AddWebTab(lua_State * tolua_S) -static int tolua_cPluginLua_AddTab(lua_State* tolua_S) +static int tolua_cPlugin_GetDirectory(lua_State * tolua_S) { - cPluginLua * self = (cPluginLua *) tolua_tousertype(tolua_S, 1, nullptr); - LOGWARN("WARNING: Using deprecated function AddTab()! Use AddWebTab() instead. (plugin \"%s\" in folder \"%s\")", - self->GetName().c_str(), self->GetDirectory().c_str() - ); - return tolua_cPluginLua_AddWebTab( tolua_S); + cLuaState L(tolua_S); + + // Log the obsoletion warning: + LOGWARNING("cPlugin:GetDirectory() is obsolete, use cPlugin:GetFolderName() instead."); + L.LogStackTrace(); + + // Retrieve the params: + cPlugin * Plugin = static_cast<cPluginLua *>(tolua_tousertype(tolua_S, 1, nullptr)); + + // Get the folder name: + L.Push(Plugin->GetFolderName()); + return 1; } -static int tolua_cPlugin_Call(lua_State * tolua_S) +static int tolua_cPlugin_GetLocalDirectory(lua_State * tolua_S) { cLuaState L(tolua_S); // Log the obsoletion warning: - LOGWARNING("cPlugin:Call() is obsolete and unsafe, use cPluginManager:CallPlugin() instead."); + LOGWARNING("cPlugin:GetLocalDirectory() is obsolete, use cPlugin:GetLocalFolder() instead."); L.LogStackTrace(); - // Retrieve the params: plugin and the function name to call - cPluginLua * TargetPlugin = (cPluginLua *) tolua_tousertype(tolua_S, 1, nullptr); - AString FunctionName = tolua_tostring(tolua_S, 2, ""); + // Retrieve the params: + cPlugin * Plugin = static_cast<cPluginLua *>(tolua_tousertype(tolua_S, 1, nullptr)); - // Call the function: - int NumReturns = TargetPlugin->CallFunctionFromForeignState(FunctionName, L, 3, lua_gettop(L)); - if (NumReturns < 0) - { - LOGWARNING("cPlugin::Call() failed to call destination function"); - L.LogStackTrace(); - return 0; - } - return NumReturns; + // Get the folder: + L.Push(Plugin->GetLocalFolder()); + return 1; } @@ -2639,8 +2658,8 @@ static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S) { const AString & FancyName = iter->first; const AString & WebName = iter->second; - tolua_pushstring( tolua_S, WebName.c_str()); // Because the WebName is supposed to be unique, use it as key - tolua_pushstring( tolua_S, FancyName.c_str()); + tolua_pushstring(tolua_S, WebName.c_str()); // Because the WebName is supposed to be unique, use it as key + tolua_pushstring(tolua_S, FancyName.c_str()); // lua_rawset(tolua_S, -3); ++iter; @@ -3792,7 +3811,8 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPlugin"); - tolua_function(tolua_S, "Call", tolua_cPlugin_Call); + tolua_function(tolua_S, "GetDirectory", tolua_cPlugin_GetDirectory); + tolua_function(tolua_S, "GetLocalDirectory", tolua_cPlugin_GetLocalDirectory); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPluginManager"); @@ -3800,10 +3820,13 @@ 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, "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<cPluginManager, cPlugin, &cPluginManager::ForEachPlugin>); tolua_function(tolua_S, "GetAllPlugins", tolua_cPluginManager_GetAllPlugins); tolua_function(tolua_S, "GetCurrentPlugin", tolua_cPluginManager_GetCurrentPlugin); + tolua_function(tolua_S, "GetPlugin", tolua_cPluginManager_GetPlugin); tolua_function(tolua_S, "LogStackTrace", tolua_cPluginManager_LogStackTrace); tolua_endmodule(tolua_S); @@ -3819,7 +3842,6 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cPluginLua"); - tolua_function(tolua_S, "AddTab", tolua_cPluginLua_AddTab); tolua_function(tolua_S, "AddWebTab", tolua_cPluginLua_AddWebTab); tolua_endmodule(tolua_S); diff --git a/src/Bindings/Plugin.cpp b/src/Bindings/Plugin.cpp index 98ccfb88c..2f2771e38 100644 --- a/src/Bindings/Plugin.cpp +++ b/src/Bindings/Plugin.cpp @@ -7,11 +7,11 @@ -cPlugin::cPlugin(const AString & a_PluginDirectory) : - m_Language(E_CPP), - m_Name(a_PluginDirectory), +cPlugin::cPlugin(const AString & a_FolderName) : + m_Status(cPluginManager::psDisabled), + m_Name(a_FolderName), m_Version(0), - m_Directory(a_PluginDirectory) + m_FolderName(a_FolderName) { } @@ -28,9 +28,33 @@ cPlugin::~cPlugin() +void cPlugin::Unload(void) +{ + auto pm = cPluginManager::Get(); + pm->RemovePluginCommands(this); + pm->RemovePluginConsoleCommands(this); + pm->RemoveHooks(this); + OnDisable(); + m_Status = cPluginManager::psUnloaded; + m_LoadError.clear(); +} + + + + + AString cPlugin::GetLocalFolder(void) const { - return std::string("Plugins/") + m_Directory; + return std::string("Plugins/") + m_FolderName; +} + + + + +void cPlugin::SetLoadError(const AString & a_LoadError) +{ + m_Status = cPluginManager::psError; + m_LoadError = a_LoadError; } diff --git a/src/Bindings/Plugin.h b/src/Bindings/Plugin.h index 3f9fa7655..5c43f9042 100644 --- a/src/Bindings/Plugin.h +++ b/src/Bindings/Plugin.h @@ -1,30 +1,16 @@ -#pragma once - -#include "Defines.h" +// Plugin.h -class cCommandOutputCallback; -class cItems; -class cHopperEntity; +// Declares the cPlugin class representing an interface that a plugin implementation needs to expose, with some helping functions -class cBlockEntityWithItems; -class cClientHandle; -class cPickup; -class cPlayer; -class cProjectileEntity; -class cEntity; -class cMonster; -class cWorld; -class cChunkDesc; -struct TakeDamageInfo; -// fwd: CraftingRecipes.h -class cCraftingGrid; -class cCraftingRecipe; +#pragma once +#include "Defines.h" +#include "PluginManager.h" @@ -35,11 +21,23 @@ class cPlugin public: // tolua_end - cPlugin( const AString & a_PluginDirectory); + /** Creates a new instance. + a_FolderName is the name of the folder (in the Plugins folder) from which the plugin is loaded. + The plugin's name defaults to the folder name. */ + cPlugin(const AString & a_FolderName); + virtual ~cPlugin(); + /** Called as the last call into the plugin before it is unloaded. */ virtual void OnDisable(void) {} - virtual bool Initialize(void) = 0; + + /** Loads and initializes the plugin. Sets m_Status to psLoaded or psError accordingly. + Returns true if the initialization succeeded, false otherwise. */ + virtual bool Load(void) = 0; + + /** Unloads the plugin. Sets m_Status to psDisabled. + The default implementation removes the plugin's associations with cPluginManager, descendants should call it as well. */ + virtual void Unload(void); // Called each tick virtual void Tick(float a_Dt) = 0; @@ -109,19 +107,17 @@ public: /** Handles the command split into a_Split, issued by player a_Player. Command permissions have already been checked. - Returns true if command handled successfully - */ + Returns true if command handled successfully. */ virtual bool HandleCommand(const AStringVector & a_Split, cPlayer & a_Player, const AString & a_FullCommand) = 0; /** Handles the console command split into a_Split. - Returns true if command handled successfully. Output is to be sent to the a_Output callback. - */ + Returns true if command handled successfully. Output is to be sent to the a_Output callback. */ virtual bool HandleConsoleCommand(const AStringVector & a_Split, cCommandOutputCallback & a_Output, const AString & a_FullCommand) = 0; - /// All bound commands are to be removed, do any language-dependent cleanup here + /** All bound commands are to be removed, do any language-dependent cleanup here */ virtual void ClearCommands(void) {} - /// All bound console commands are to be removed, do any language-dependent cleanup here + /** All bound console commands are to be removed, do any language-dependent cleanup here */ virtual void ClearConsoleCommands(void) {} // tolua_begin @@ -131,28 +127,43 @@ public: int GetVersion(void) const { return m_Version; } void SetVersion(int a_Version) { m_Version = a_Version; } - const AString & GetDirectory(void) const {return m_Directory; } - AString GetLocalDirectory(void) const {return GetLocalFolder(); } // OBSOLETE, use GetLocalFolder() instead + /** Returns the name of the folder (in the Plugins folder) from which the plugin is loaded. */ + const AString & GetFolderName(void) const {return m_FolderName; } + + /** Returns the folder relative to the MCS Executable, from which the plugin is loaded. */ AString GetLocalFolder(void) const; + + /** Returns the error encountered while loading the plugin. Only valid if m_Status == psError. */ + const AString & GetLoadError(void) const { return m_LoadError; } + + cPluginManager::ePluginStatus GetStatus(void) const { return m_Status; } + + bool IsLoaded(void) const { return (m_Status == cPluginManager::psLoaded); } // tolua_end + // Needed for ManualBindings' tolua_ForEach<> + static const char * GetClassStatic(void) { return "cPlugin"; } + +protected: + friend class cPluginManager; + + cPluginManager::ePluginStatus m_Status; - /* This should not be exposed to scripting languages */ - enum PluginLanguage - { - E_CPP, - E_LUA, - E_SQUIRREL, // OBSOLETE, but kept in place to remind us of the horrors lurking in the history - }; - PluginLanguage GetLanguage() { return m_Language; } - void SetLanguage( PluginLanguage a_Language) { m_Language = a_Language; } - -private: - PluginLanguage m_Language; + /** The name of the plugin, used to identify the plugin in the system and for inter-plugin calls. */ AString m_Name; + int m_Version; - AString m_Directory; + /** Name of the folder (in the Plugins folder) from which the plugin is loaded. */ + AString m_FolderName; + + /** The error encountered while loading the plugin. + Only valid if m_Status == psError. */ + AString m_LoadError; + + + /** Sets m_LoadError to the specified string and m_Status to psError. */ + void SetLoadError(const AString & a_LoadError); }; // tolua_export diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp index 9bbac92b0..cb2ea3b45 100644 --- a/src/Bindings/PluginLua.cpp +++ b/src/Bindings/PluginLua.cpp @@ -93,7 +93,7 @@ void cPluginLua::Close(void) -bool cPluginLua::Initialize(void) +bool cPluginLua::Load(void) { cCSLock Lock(m_CriticalSection); if (!m_LuaState.IsValid()) @@ -144,6 +144,7 @@ bool cPluginLua::Initialize(void) // Warn if there are no Lua files in the plugin folder: if (LuaFiles.empty()) { + SetLoadError("No lua files found, plugin is probably missing."); LOGWARNING("No lua files found: plugin %s is missing.", GetName().c_str()); Close(); return false; @@ -155,6 +156,7 @@ bool cPluginLua::Initialize(void) AString Path = PluginPath + *itr; if (!m_LuaState.LoadFile(Path)) { + SetLoadError(Printf("Failed to load file %s.", itr->c_str())); Close(); return false; } @@ -164,6 +166,8 @@ bool cPluginLua::Initialize(void) AString Path = PluginPath + "Info.lua"; if (!m_LuaState.LoadFile(Path)) { + SetLoadError("Failed to load file Info.lua."); + m_Status = cPluginManager::psError; Close(); return false; } @@ -173,17 +177,20 @@ bool cPluginLua::Initialize(void) bool res = false; if (!m_LuaState.Call("Initialize", this, cLuaState::Return, res)) { + SetLoadError("Cannot call the Initialize() function."); LOGWARNING("Error in plugin %s: Cannot call the Initialize() function. Plugin is temporarily disabled.", GetName().c_str()); Close(); return false; } if (!res) { + SetLoadError("The Initialize() function failed."); LOGINFO("Plugin %s: Initialize() call failed, plugin is temporarily disabled.", GetName().c_str()); Close(); return false; } + m_Status = cPluginManager::psLoaded; return true; } @@ -191,6 +198,16 @@ bool cPluginLua::Initialize(void) +void cPluginLua::Unload(void) +{ + super::Unload(); + Close(); +} + + + + + void cPluginLua::OnDisable(void) { cCSLock Lock(m_CriticalSection); diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h index c14b02687..70d203244 100644 --- a/src/Bindings/PluginLua.h +++ b/src/Bindings/PluginLua.h @@ -32,6 +32,8 @@ class cPluginLua : public cPlugin, public cWebPlugin { + typedef cPlugin super; + public: // tolua_end @@ -96,7 +98,8 @@ public: ~cPluginLua(); virtual void OnDisable(void) override; - virtual bool Initialize(void) override; + virtual bool Load(void) override; + virtual void Unload(void) override; virtual void Tick(float a_Dt) override; diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp index 8935f7dd3..45a778eda 100644 --- a/src/Bindings/PluginManager.cpp +++ b/src/Bindings/PluginManager.cpp @@ -59,39 +59,48 @@ void cPluginManager::ReloadPlugins(void) -void cPluginManager::FindPlugins(void) +void cPluginManager::RefreshPluginList(void) { + // Get a list of currently available folders: AString PluginsPath = GetPluginsPath() + "/"; - - // First get a clean list of only the currently running plugins, we don't want to mess those up - for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();) + AStringVector Contents = cFile::GetFolderContents(PluginsPath.c_str()); + AStringVector Folders; + for (auto & item: Contents) { - if (itr->second == nullptr) + if ((item == ".") || (item == "..") || (!cFile::IsFolder(PluginsPath + item))) { - PluginMap::iterator thiz = itr; - ++thiz; - m_Plugins.erase( itr); - itr = thiz; + // We only want folders, and don't want "." or ".." continue; } - ++itr; - } + Folders.push_back(item); + } // for item - Contents[] - AStringVector Files = cFile::GetFolderContents(PluginsPath.c_str()); - for (AStringVector::const_iterator itr = Files.begin(); itr != Files.end(); ++itr) + // Set all plugins with invalid folders as psNotFound: + for (auto & plugin: m_Plugins) { - if ((*itr == ".") || (*itr == "..") || (!cFile::IsFolder(PluginsPath + *itr))) + if (std::find(Folders.cbegin(), Folders.cend(), plugin->GetFolderName()) == Folders.end()) { - // We only want folders, and don't want "." or ".." - continue; + plugin->m_Status = psNotFound; } + } // for plugin - m_Plugins[] - // Add plugin name/directory to the list - if (m_Plugins.find(*itr) == m_Plugins.end()) + // Add all newly discovered plugins: + for (auto & folder: Folders) + { + bool hasFound = false; + for (auto & plugin: m_Plugins) { - m_Plugins[*itr] = nullptr; + if (plugin->GetFolderName() == folder) + { + hasFound = true; + break; + } + } // for plugin - m_Plugins[] + if (!hasFound) + { + m_Plugins.push_back(std::make_shared<cPluginLua>(folder)); } - } + } // for folder - Folders[] } @@ -112,57 +121,23 @@ void cPluginManager::ReloadPluginsNow(void) void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) { LOG("-- Loading Plugins --"); + + // Unload any existing plugins: m_bReloadPlugins = false; UnloadPluginsNow(); - FindPlugins(); - - cServer::BindBuiltInConsoleCommands(); - - // Check if the Plugins section exists. - int KeyNum = a_SettingsIni.FindKey("Plugins"); + // Refresh the list of plugins to load new ones from disk / remove the deleted ones: + RefreshPluginList(); - if (KeyNum == -1) + // Load the plugins: + AStringVector ToLoad = GetFoldersToLoad(a_SettingsIni); + for (auto & pluginFolder: ToLoad) { - InsertDefaultPlugins(a_SettingsIni); - KeyNum = a_SettingsIni.FindKey("Plugins"); - } - - // How many plugins are there? - int NumPlugins = a_SettingsIni.GetNumValues(KeyNum); - - for (int i = 0; i < NumPlugins; i++) - { - AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); - if (ValueName.compare("Plugin") == 0) - { - AString PluginFile = a_SettingsIni.GetValue(KeyNum, i); - if (!PluginFile.empty()) - { - if (m_Plugins.find(PluginFile) != m_Plugins.end()) - { - LoadPlugin(PluginFile); - } - } - } - } - - - // Remove invalid plugins from the PluginMap. - for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();) - { - if (itr->second == nullptr) - { - PluginMap::iterator thiz = itr; - ++thiz; - m_Plugins.erase(itr); - itr = thiz; - continue; - } - ++itr; - } + LoadPlugin(pluginFolder); + } // for pluginFolder - ToLoad[] - size_t NumLoadedPlugins = GetNumPlugins(); + // Log a report of the loading process + size_t NumLoadedPlugins = GetNumLoadedPlugins(); if (NumLoadedPlugins == 0) { LOG("-- No Plugins Loaded --"); @@ -173,7 +148,7 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni) } else { - LOG("-- Loaded %i Plugins --", (int)NumLoadedPlugins); + LOG("-- Loaded %u Plugins --", static_cast<unsigned>(NumLoadedPlugins)); } CallHookPluginsLoaded(); } @@ -200,12 +175,21 @@ void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni) void cPluginManager::Tick(float a_Dt) { - while (!m_DisablePluginList.empty()) + // Unload plugins that have been scheduled for unloading: + AStringVector PluginsToUnload; { - RemovePlugin(m_DisablePluginList.front()); - m_DisablePluginList.pop_front(); + cCSLock Lock(m_CSPluginsToUnload); + std::swap(m_PluginsToUnload, PluginsToUnload); } + for (auto & plugin: m_Plugins) + { + if (std::find(PluginsToUnload.cbegin(), PluginsToUnload.cend(), plugin->GetFolderName()) != PluginsToUnload.cend()) + { + plugin->Unload(); + } + } // for plugin - m_Plugins[] + // If a plugin reload has been scheduled, reload now: if (m_bReloadPlugins) { ReloadPluginsNow(); @@ -1477,68 +1461,56 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player, -cPlugin * cPluginManager::GetPlugin(const AString & a_Plugin) const +void cPluginManager::UnloadPluginsNow() { - for (PluginMap::const_iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - if (itr->second == nullptr) - { - // The plugin is currently unloaded - continue; - } + // Remove all bindings: + m_Hooks.clear(); + m_Commands.clear(); + m_ConsoleCommands.clear(); + + // Re-bind built-in console commands: + cServer::BindBuiltInConsoleCommands(); - if (itr->second->GetName().compare(a_Plugin) == 0) + // Unload all loaded plugins: + for (auto & plugin: m_Plugins) + { + if (plugin->IsLoaded()) { - return itr->second; + plugin->Unload(); } } - return 0; } -const cPluginManager::PluginMap & cPluginManager::GetAllPlugins() const +void cPluginManager::UnloadPlugin(const AString & a_PluginFolder) { - return m_Plugins; + cCSLock Lock(m_CSPluginsToUnload); + m_PluginsToUnload.push_back(a_PluginFolder); } -void cPluginManager::UnloadPluginsNow() +bool cPluginManager::LoadPlugin(const AString & a_FolderName) { - m_Hooks.clear(); - - while (!m_Plugins.empty()) + for (auto & plugin: m_Plugins) { - RemovePlugin(m_Plugins.begin()->second); - } - - m_Commands.clear(); - m_ConsoleCommands.clear(); -} - - - - - -bool cPluginManager::DisablePlugin(const AString & a_PluginName) -{ - PluginMap::iterator itr = m_Plugins.find(a_PluginName); - if (itr == m_Plugins.end()) - { - return false; - } + if (plugin->GetFolderName() == a_FolderName) + { + if (!plugin->IsLoaded()) + { + return plugin->Load(); + } + return true; + } + } // for plugin - m_Plugins[] - if (itr->first.compare(a_PluginName) == 0) // _X 2013_02_01: wtf? Isn't this supposed to be what find() does? - { - m_DisablePluginList.push_back(itr->second); - itr->second = nullptr; // Get rid of this thing right away - return true; - } + // Plugin not found + LOGD("%s: Plugin folder %s not found in the list of plugins.", __FUNCTION__, a_FolderName.c_str()); return false; } @@ -1546,15 +1518,6 @@ bool cPluginManager::DisablePlugin(const AString & a_PluginName) -bool cPluginManager::LoadPlugin(const AString & a_PluginName) -{ - return AddPlugin(new cPluginLua(a_PluginName.c_str())); -} - - - - - void cPluginManager::RemoveHooks(cPlugin * a_Plugin) { for (HookMap::iterator itr = m_Hooks.begin(), end = m_Hooks.end(); itr != end; ++itr) @@ -1567,32 +1530,6 @@ void cPluginManager::RemoveHooks(cPlugin * a_Plugin) -void cPluginManager::RemovePlugin(cPlugin * a_Plugin) -{ - for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end(); ++itr) - { - if (itr->second == a_Plugin) - { - m_Plugins.erase(itr); - break; - } - } - - RemovePluginCommands(a_Plugin); - RemovePluginConsoleCommands(a_Plugin); - RemoveHooks(a_Plugin); - if (a_Plugin != nullptr) - { - a_Plugin->OnDisable(); - } - delete a_Plugin; - a_Plugin = nullptr; -} - - - - - void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin) { if (a_Plugin != nullptr) @@ -1836,31 +1773,31 @@ bool cPluginManager::IsValidHookType(int a_HookType) bool cPluginManager::DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback) { // TODO: Implement locking for plugins - PluginMap::iterator itr = m_Plugins.find(a_PluginName); - if ((itr == m_Plugins.end()) || (itr->second == nullptr)) + for (auto & plugin: m_Plugins) { - return false; + if (plugin->GetName() == a_PluginName) + { + return a_Callback.Item(plugin.get()); + } } - return a_Callback.Item(itr->second); + return false; } -bool cPluginManager::AddPlugin(cPlugin * a_Plugin) +bool cPluginManager::ForEachPlugin(cPluginCallback & a_Callback) { - m_Plugins[a_Plugin->GetDirectory()] = a_Plugin; - - if (a_Plugin->Initialize()) + // TODO: Implement locking for plugins + for (auto & plugin: m_Plugins) { - // Initialization OK - return true; + if (a_Callback.Item(plugin.get())) + { + return false; + } } - - // Initialization failed - RemovePlugin(a_Plugin); // Also undoes any registrations that Initialize() might have made - return false; + return true; } @@ -1869,21 +1806,23 @@ bool cPluginManager::AddPlugin(cPlugin * a_Plugin) void cPluginManager::AddHook(cPlugin * a_Plugin, int a_Hook) { - if (!a_Plugin) + if (a_Plugin == nullptr) { LOGWARN("Called cPluginManager::AddHook() with a_Plugin == nullptr"); return; } PluginList & Plugins = m_Hooks[a_Hook]; - Plugins.remove(a_Plugin); - Plugins.push_back(a_Plugin); + if (std::find(Plugins.cbegin(), Plugins.cend(), a_Plugin) == Plugins.cend()) + { + Plugins.push_back(a_Plugin); + } } -size_t cPluginManager::GetNumPlugins() const +size_t cPluginManager::GetNumPlugins(void) const { return m_Plugins.size(); } @@ -1891,3 +1830,53 @@ size_t cPluginManager::GetNumPlugins() const + +size_t cPluginManager::GetNumLoadedPlugins(void) const +{ + size_t res = 0; + for (auto & plugin: m_Plugins) + { + if (plugin->IsLoaded()) + { + res += 1; + } + } + return res; +} + + + + + +AStringVector cPluginManager::GetFoldersToLoad(cIniFile & a_SettingsIni) +{ + // Check if the Plugins section exists. + int KeyNum = a_SettingsIni.FindKey("Plugins"); + if (KeyNum == -1) + { + InsertDefaultPlugins(a_SettingsIni); + KeyNum = a_SettingsIni.FindKey("Plugins"); + } + + // Get the list of plugins to load: + AStringVector res; + int NumPlugins = a_SettingsIni.GetNumValues(KeyNum); + for (int i = 0; i < NumPlugins; i++) + { + AString ValueName = a_SettingsIni.GetValueName(KeyNum, i); + if (ValueName.compare("Plugin") == 0) + { + AString PluginFile = a_SettingsIni.GetValue(KeyNum, i); + if (!PluginFile.empty()) + { + res.push_back(PluginFile); + } + } + } // for i - ini values + + return res; +} + + + + diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h index be6ff021a..f32feb8a3 100644 --- a/src/Bindings/PluginManager.h +++ b/src/Bindings/PluginManager.h @@ -6,48 +6,30 @@ -class cPlugin; -// fwd: World.h -class cWorld; -// fwd: ChunkDesc.h +// fwd: +class cBlockEntityWithItems; class cChunkDesc; - -// fwd: Entities/Entity.h -class cEntity; - -// fwd: Entities/ProjectileEntity.h -class cProjectileEntity; - -// fwd: Mobs/Monster.h -class cMonster; - -// fwd: Player.h -class cPlayer; - -// fwd: CraftingRecipes.h +class cClientHandle; +class cCommandOutputCallback; class cCraftingGrid; class cCraftingRecipe; - -// fwd: Pickup.h +class cEntity; +class cHopperEntity; +class cItems; +class cMonster; class cPickup; - -// fwd: Pawn.h +class cPlayer; +class cPlugin; +class cProjectileEntity; +class cWorld; struct TakeDamageInfo; -// fwd: CommandOutput.h -class cCommandOutputCallback; +typedef SharedPtr<cPlugin> cPluginPtr; +typedef std::vector<cPluginPtr> cPluginPtrs; -// fwd: BlockEntities/HopperEntity.h -class cHopperEntity; -// fwd: BlockEntities/BlockEntityWithItems.h -class cBlockEntityWithItems; - - - -class cItems; @@ -55,12 +37,7 @@ class cItems; class cPluginManager { public: - // tolua_end - - // Called each tick - virtual void Tick(float a_Dt); - - // tolua_begin + enum CommandResult { crExecuted, @@ -70,6 +47,29 @@ public: crNoPermission, } ; + + /** Defines the status of a single plugin - whether it is loaded, disabled or errored. */ + enum ePluginStatus + { + /** The plugin has been loaded successfully. */ + psLoaded, + + /** The plugin is disabled in settings.ini. */ + psDisabled, + + /** The plugin is enabled in settings.ini but has been unloaded (by a command). */ + psUnloaded, + + /** The plugin is enabled in settings.ini but has failed to load. + m_LoadError is the description of the error. */ + psError, + + /** The plugin has been loaded before, but after a folder refresh it is no longer present. + The plugin will be unloaded in the next call to ReloadPlugins(). */ + psNotFound, + }; + + enum PluginHook { HOOK_BLOCK_SPREAD, @@ -160,24 +160,31 @@ public: /** The interface used for enumerating and extern-calling plugins */ typedef cItemCallback<cPlugin> cPluginCallback; + typedef std::list<cPlugin *> PluginList; + + + /** Called each tick, calls the plugins' OnTick hook, as well as processes plugin events (addition, removal) */ + void Tick(float a_Dt); /** Returns the instance of the Plugin Manager (there is only ever one) */ static cPluginManager * Get(void); // tolua_export - typedef std::map< AString, cPlugin * > PluginMap; - typedef std::list< cPlugin * > PluginList; - cPlugin * GetPlugin( const AString & a_Plugin) const; // tolua_export - const PluginMap & GetAllPlugins() const; // >> EXPORTED IN MANUALBINDINGS << + /** Refreshes the m_Plugins list based on the current contents of the Plugins folder. + If an active plugin's folder is not found anymore, the plugin is set as psNotFound, but not yet unloaded. */ + void RefreshPluginList(); // tolua_export - // tolua_begin - void FindPlugins(); - void ReloadPlugins(); - // tolua_end + /** Schedules a reload of the plugins to happen within the next call to Tick(). */ + void ReloadPlugins(); // tolua_export - /** Adds the plugin to the list of plugins called for the specified hook type. Handles multiple adds as a single add */ + /** Adds the plugin to the list of plugins called for the specified hook type. + If a plugin adds multiple handlers for a single hook, it is added only once (ignore-duplicates). */ void AddHook(cPlugin * a_Plugin, int a_HookType); + /** Returns the number of all plugins in m_Plugins (includes disabled, unloaded and errored plugins). */ size_t GetNumPlugins() const; // tolua_export + + /** Returns the number of plugins that are psLoaded. */ + size_t GetNumLoadedPlugins(void) const; // tolua_export // Calls for individual hooks. Each returns false if the action is to continue or true if the plugin wants to abort bool CallHookBlockSpread (cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ, eSpreadSource a_Source); @@ -242,14 +249,19 @@ public: bool CallHookWorldStarted (cWorld & a_World); bool CallHookWorldTick (cWorld & a_World, std::chrono::milliseconds a_Dt, std::chrono::milliseconds a_LastTickDurationMSec); - bool DisablePlugin(const AString & a_PluginName); // tolua_export - bool LoadPlugin (const AString & a_PluginName); // tolua_export + /** Queues the specified plugin to be unloaded in the next call to Tick(). + Note that this function returns before the plugin is unloaded, to avoid deadlocks. */ + void UnloadPlugin(const AString & a_PluginFolder); // tolua_export + + /** Loads the plugin from the specified plugin folder. + Returns true if the plugin was loaded successfully or was already loaded before, false otherwise. */ + bool LoadPlugin(const AString & a_PluginFolder); // tolua_export /** Removes all hooks the specified plugin has registered */ void RemoveHooks(cPlugin * a_Plugin); - /** Removes the plugin from the internal structures and deletes its object. */ - void RemovePlugin(cPlugin * a_Plugin); + /** Removes the plugin of the specified name from the internal structures and deletes its object. */ + void RemovePlugin(const AString & a_PluginName); /** Removes all command bindings that the specified plugin has made */ void RemovePluginCommands(cPlugin * a_Plugin); @@ -296,8 +308,12 @@ public: static bool IsValidHookType(int a_HookType); /** Calls the specified callback with the plugin object of the specified plugin. - Returns false if plugin not found, and the value that the callback has returned otherwise. */ + Returns false if plugin not found, otherwise returns the value that the callback has returned. */ bool DoWithPlugin(const AString & a_PluginName, cPluginCallback & a_Callback); + + /** Calls the specified callback for each plugin in m_Plugins. + 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 path where individual plugins' folders are expected. The path doesn't end in a slash. */ @@ -317,14 +333,26 @@ private: typedef std::map<int, cPluginManager::PluginList> HookMap; typedef std::map<AString, cCommandReg> CommandMap; - PluginList m_DisablePluginList; - PluginMap m_Plugins; + + /** FolderNames of plugins that should be unloaded. + The plugins will be unloaded within the next call to Tick(), to avoid multithreading issues. + Protected against multithreaded access by m_CSPluginsToUnload. */ + AStringVector m_PluginsToUnload; + + /** Protects m_PluginsToUnload against multithreaded access. */ + mutable cCriticalSection m_CSPluginsToUnload; + + /** All plugins that have been found in the Plugins folder. */ + cPluginPtrs m_Plugins; + HookMap m_Hooks; CommandMap m_Commands; CommandMap m_ConsoleCommands; + /** If set to true, all the plugins will be reloaded within the next call to Tick(). */ bool m_bReloadPlugins; + cPluginManager(); virtual ~cPluginManager(); @@ -340,11 +368,11 @@ private: /** Handles writing default plugins if 'Plugins' key not found using a cIniFile object expected to be intialised to settings.ini */ void InsertDefaultPlugins(cIniFile & a_SettingsIni); - /** Adds the plugin into the internal list of plugins and initializes it. If initialization fails, the plugin is removed again. */ - bool AddPlugin(cPlugin * a_Plugin); - /** Tries to match a_Command to the internal table of commands, if a match is found, the corresponding plugin is called. Returns crExecuted if the command is executed. */ CommandResult HandleCommand(cPlayer & a_Player, const AString & a_Command, bool a_ShouldCheckPermissions); + + /** Returns the folders that are specified in the settings ini to load plugins from. */ + AStringVector GetFoldersToLoad(cIniFile & a_SettingsIni); } ; // tolua_export |