summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2015-04-19 10:57:41 +0200
committerMattes D <github@xoft.cz>2015-04-19 10:57:41 +0200
commita9b5a6c3a63d8500f3c512574fd802d40b562661 (patch)
treee6d0d362ed42116d6c9deaaa789287f464569514 /src
parentMerge pull request #1869 from mathias-gh/master (diff)
downloadcuberite-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 '')
-rw-r--r--src/Bindings/ManualBindings.cpp212
-rw-r--r--src/Bindings/Plugin.cpp34
-rw-r--r--src/Bindings/Plugin.h95
-rw-r--r--src/Bindings/PluginLua.cpp19
-rw-r--r--src/Bindings/PluginLua.h5
-rw-r--r--src/Bindings/PluginManager.cpp315
-rw-r--r--src/Bindings/PluginManager.h142
-rw-r--r--src/Server.cpp19
-rw-r--r--src/WebAdmin.cpp28
9 files changed, 482 insertions, 387 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
diff --git a/src/Server.cpp b/src/Server.cpp
index df2c7deef..9c5c6b6eb 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -464,22 +464,11 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
{
if (split.size() > 1)
{
- cPluginManager::PluginMap map = cPluginManager::Get()->GetAllPlugins();
-
- for (auto plugin_entry : map)
- {
- if (plugin_entry.first == split[1])
- {
- a_Output.Out("Error! Plugin is already loaded!");
- a_Output.Finished();
- return;
- }
- }
a_Output.Out(cPluginManager::Get()->LoadPlugin(split[1]) ? "Plugin loaded" : "Error occurred loading plugin");
}
else
{
- a_Output.Out("Usage: load <pluginname>");
+ a_Output.Out("Usage: load <PluginFolder>");
}
a_Output.Finished();
return;
@@ -488,12 +477,12 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac
{
if (split.size() > 1)
{
- cPluginManager::Get()->RemovePlugin(cPluginManager::Get()->GetPlugin(split[1]));
- a_Output.Out("Plugin unloaded");
+ cPluginManager::Get()->UnloadPlugin(split[1]);
+ a_Output.Out("Plugin unload scheduled");
}
else
{
- a_Output.Out("Usage: unload <pluginname>");
+ a_Output.Out("Usage: unload <PluginFolder>");
}
a_Output.Finished();
return;
diff --git a/src/WebAdmin.cpp b/src/WebAdmin.cpp
index 13cf3cc41..ec265ea5b 100644
--- a/src/WebAdmin.cpp
+++ b/src/WebAdmin.cpp
@@ -489,20 +489,32 @@ AString cWebAdmin::GetDefaultPage(void)
Content += "<h4>Server Name:</h4>";
Content += "<p>" + AString( cRoot::Get()->GetServer()->GetServerID()) + "</p>";
+ // Display a list of all plugins:
Content += "<h4>Plugins:</h4><ul>";
- cPluginManager * PM = cPluginManager::Get();
- const cPluginManager::PluginMap & List = PM->GetAllPlugins();
- for (cPluginManager::PluginMap::const_iterator itr = List.begin(); itr != List.end(); ++itr)
+ struct cPluginCallback:
+ public cPluginManager::cPluginCallback
{
- if (itr->second == nullptr)
+ AString & m_Content;
+
+ cPluginCallback(AString & a_Content):
+ m_Content(a_Content)
{
- continue;
}
- AppendPrintf(Content, "<li>%s V.%i</li>", itr->second->GetName().c_str(), itr->second->GetVersion());
- }
+
+ virtual bool Item(cPlugin * a_Plugin) override
+ {
+ if (a_Plugin->IsLoaded())
+ {
+ AppendPrintf(m_Content, "<li>%s V.%i</li>", a_Plugin->GetName().c_str(), a_Plugin->GetVersion());
+ }
+ return false;
+ }
+ } Callback(Content);
+ cPluginManager::Get()->ForEachPlugin(Callback);
Content += "</ul>";
- Content += "<h4>Players:</h4><ul>";
+ // Display a list of all players:
+ Content += "<h4>Players:</h4><ul>";
cPlayerAccum PlayerAccum;
cWorld * World = cRoot::Get()->GetDefaultWorld(); // TODO - Create a list of worlds and players
if (World != nullptr)