summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2015-04-19 19:53:14 +0200
committerMattes D <github@xoft.cz>2015-04-19 19:53:14 +0200
commitba6f5aea4c088b92365b6636d96873a82706c8c8 (patch)
treed8c6f3a3eda7c0ed1f70bc5a01b97b90d940eab4 /src
parentClientHandle: Fixed re-sending refused right-clicks. (diff)
parentRefresh plugin list before trying to load. (diff)
downloadcuberite-ba6f5aea4c088b92365b6636d96873a82706c8c8.tar
cuberite-ba6f5aea4c088b92365b6636d96873a82706c8c8.tar.gz
cuberite-ba6f5aea4c088b92365b6636d96873a82706c8c8.tar.bz2
cuberite-ba6f5aea4c088b92365b6636d96873a82706c8c8.tar.lz
cuberite-ba6f5aea4c088b92365b6636d96873a82706c8c8.tar.xz
cuberite-ba6f5aea4c088b92365b6636d96873a82706c8c8.tar.zst
cuberite-ba6f5aea4c088b92365b6636d96873a82706c8c8.zip
Diffstat (limited to 'src')
-rw-r--r--src/Bindings/ManualBindings.cpp228
-rw-r--r--src/Bindings/Plugin.cpp34
-rw-r--r--src/Bindings/Plugin.h95
-rw-r--r--src/Bindings/PluginLua.cpp67
-rw-r--r--src/Bindings/PluginLua.h14
-rw-r--r--src/Bindings/PluginManager.cpp349
-rw-r--r--src/Bindings/PluginManager.h147
-rw-r--r--src/Bindings/WebPlugin.cpp110
-rw-r--r--src/Bindings/WebPlugin.h61
-rw-r--r--src/Server.cpp20
-rw-r--r--src/WebAdmin.cpp32
11 files changed, 668 insertions, 489 deletions
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index 6e579b364..4c8d9ff96 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;
}
@@ -2628,22 +2647,16 @@ static int tolua_AllToLua_cWebAdmin_GetURLEncodedString(lua_State * tolua_S)
static int tolua_cWebPlugin_GetTabNames(lua_State * tolua_S)
{
- cWebPlugin* self = (cWebPlugin*) tolua_tousertype(tolua_S, 1, nullptr);
-
- const cWebPlugin::TabNameList & TabNames = self->GetTabNames();
-
+ // Returns a map of (SafeTitle -> Title) for the plugin's web tabs.
+ auto self = reinterpret_cast<cWebPlugin *>(tolua_tousertype(tolua_S, 1, nullptr));
+ auto TabNames = self->GetTabNames();
lua_newtable(tolua_S);
int index = 1;
- cWebPlugin::TabNameList::const_iterator iter = TabNames.begin();
- while (iter != TabNames.end())
- {
- 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());
- //
+ for (auto itr = TabNames.cbegin(), end = TabNames.cend(); itr != end; ++itr)
+ {
+ tolua_pushstring(tolua_S, itr->second.c_str()); // Because the SafeTitle is supposed to be unique, use it as key
+ tolua_pushstring(tolua_S, itr->first.c_str());
lua_rawset(tolua_S, -3);
- ++iter;
++index;
}
return 1;
@@ -3792,7 +3805,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 +3814,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 +3836,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..ddd3398a5 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,17 @@ bool cPluginLua::Initialize(void)
+void cPluginLua::Unload(void)
+{
+ ClearTabs();
+ super::Unload();
+ Close();
+}
+
+
+
+
+
void cPluginLua::OnDisable(void)
{
cCSLock Lock(m_CriticalSection);
@@ -1983,40 +2001,29 @@ void cPluginLua::AddResettable(cPluginLua::cResettablePtr a_Resettable)
-AString cPluginLua::HandleWebRequest(const HTTPRequest * a_Request)
+AString cPluginLua::HandleWebRequest(const HTTPRequest & a_Request)
{
- cCSLock Lock(m_CriticalSection);
- std::string RetVal = "";
-
- std::pair< std::string, std::string > TabName = GetTabNameForRequest(a_Request);
- std::string SafeTabName = TabName.second;
- if (SafeTabName.empty())
+ // Find the tab to use for the request:
+ auto TabName = GetTabNameForRequest(a_Request);
+ AString SafeTabTitle = TabName.second;
+ if (SafeTabTitle.empty())
{
return "";
}
-
- sWebPluginTab * Tab = 0;
- for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr)
+ auto Tab = GetTabBySafeTitle(SafeTabTitle);
+ if (Tab == nullptr)
{
- if ((*itr)->SafeTitle.compare(SafeTabName) == 0) // This is the one! Rawr
- {
- Tab = *itr;
- break;
- }
+ return "";
}
- if (Tab != nullptr)
+ // Get the page content from the plugin:
+ cCSLock Lock(m_CriticalSection);
+ AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->m_Title.c_str());
+ if (!m_LuaState.Call(Tab->m_UserData, &a_Request, cLuaState::Return, Contents))
{
- AString Contents = Printf("WARNING: WebPlugin tab '%s' did not return a string!", Tab->Title.c_str());
- if (!m_LuaState.Call(Tab->UserData, a_Request, cLuaState::Return, Contents))
- {
- return "Lua encountered error while processing the page request";
- }
-
- RetVal += Contents;
+ return "Lua encountered error while processing the page request";
}
-
- return RetVal;
+ return Contents;
}
@@ -2031,13 +2038,7 @@ bool cPluginLua::AddWebTab(const AString & a_Title, lua_State * a_LuaState, int
LOGERROR("Only allowed to add a tab to a WebPlugin of your own Plugin!");
return false;
}
- sWebPluginTab * Tab = new sWebPluginTab();
- Tab->Title = a_Title;
- Tab->SafeTitle = SafeString(a_Title);
-
- Tab->UserData = a_FunctionReference;
-
- GetTabs().push_back(Tab);
+ AddNewWebTab(a_Title, a_FunctionReference);
return true;
}
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index c14b02687..393737b34 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;
@@ -173,12 +176,13 @@ public:
/** Returns true if the plugin contains the function for the specified hook type, using the old-style registration (#121) */
bool CanAddOldStyleHook(int a_HookType);
- // cWebPlugin override
+ // cWebPlugin overrides
virtual const AString GetWebTitle(void) const {return GetName(); }
+ virtual AString HandleWebRequest(const HTTPRequest & a_Request) override;
- // cWebPlugin and WebAdmin stuff
- virtual AString HandleWebRequest(const HTTPRequest * a_Request) override;
- bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // >> EXPORTED IN MANUALBINDINGS <<
+ /** Adds a new web tab to webadmin.
+ Displaying the tab calls the referenced function. */
+ bool AddWebTab(const AString & a_Title, lua_State * a_LuaState, int a_FunctionReference); // Exported in ManualBindings.cpp
/** Binds the command to call the function specified by a Lua function reference. Simply adds to CommandMap. */
void BindCommand(const AString & a_Command, int a_FnRef);
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 8935f7dd3..003996802 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();
+ // Refresh the list of plugins to load new ones from disk / remove the deleted ones:
+ RefreshPluginList();
- cServer::BindBuiltInConsoleCommands();
-
- // Check if the Plugins section exists.
- int KeyNum = a_SettingsIni.FindKey("Plugins");
-
- if (KeyNum == -1)
+ // Load the plugins:
+ AStringVector ToLoad = GetFoldersToLoad(a_SettingsIni);
+ for (auto & pluginFolder: ToLoad)
{
- InsertDefaultPlugins(a_SettingsIni);
- KeyNum = a_SettingsIni.FindKey("Plugins");
- }
+ LoadPlugin(pluginFolder);
+ } // for pluginFolder - ToLoad[]
- // 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;
- }
-
- 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,39 @@ 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 & folder: PluginsToUnload)
+ {
+ bool HasUnloaded = false;
+ bool HasFound = false;
+ for (auto & plugin: m_Plugins)
+ {
+ if (plugin->GetFolderName() == folder)
+ {
+ HasFound = true;
+ if (plugin->IsLoaded())
+ {
+ plugin->Unload();
+ HasUnloaded = true;
+ }
+ }
+ }
+ if (!HasFound)
+ {
+ LOG("Cannot unload plugin in folder \"%s\", there's no such plugin folder", folder.c_str());
+ }
+ else if (!HasUnloaded)
+ {
+ LOG("Cannot unload plugin in folder \"%s\", it has not been loaded.", folder.c_str());
+ }
+ } // for plugin - m_Plugins[]
+ // If a plugin reload has been scheduled, reload now:
if (m_bReloadPlugins)
{
ReloadPluginsNow();
@@ -1477,68 +1479,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();
- if (itr->second->GetName().compare(a_Plugin) == 0)
+ // Re-bind built-in console commands:
+ cServer::BindBuiltInConsoleCommands();
+
+ // 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
+ LOG("Cannot load plugin, folder \"%s\" not found.", a_FolderName.c_str());
return false;
}
@@ -1546,15 +1536,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 +1548,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)
@@ -1619,6 +1574,22 @@ void cPluginManager::RemovePluginCommands(cPlugin * a_Plugin)
+bool cPluginManager::IsPluginLoaded(const AString & a_PluginName)
+{
+ for (auto & plugin: m_Plugins)
+ {
+ if (plugin->GetName() == a_PluginName)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+
bool cPluginManager::BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString)
{
CommandMap::iterator cmd = m_Commands.find(a_Command);
@@ -1836,31 +1807,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 +1840,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 +1864,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..994f19943 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;
-
-// fwd: BlockEntities/HopperEntity.h
-class cHopperEntity;
-
-// fwd: BlockEntities/BlockEntityWithItems.h
-class cBlockEntityWithItems;
-
+typedef SharedPtr<cPlugin> cPluginPtr;
+typedef std::vector<cPluginPtr> cPluginPtrs;
-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,18 +249,26 @@ 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);
-
+
+ /** Returns true if the specified plugin is loaded. */
+ bool IsPluginLoaded(const AString & a_PluginName); // tolua_export
+
/** Binds a command to the specified plugin. Returns true if successful, false if command already bound. */
bool BindCommand(const AString & a_Command, cPlugin * a_Plugin, const AString & a_Permission, const AString & a_HelpString); // Exported in ManualBindings.cpp, without the a_Plugin param
@@ -296,8 +311,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 +336,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 +371,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/Bindings/WebPlugin.cpp b/src/Bindings/WebPlugin.cpp
index 5759b20e7..1eca7de93 100644
--- a/src/Bindings/WebPlugin.cpp
+++ b/src/Bindings/WebPlugin.cpp
@@ -24,75 +24,82 @@ cWebPlugin::cWebPlugin()
cWebPlugin::~cWebPlugin()
{
+ ASSERT(m_Tabs.empty()); // Has ClearTabs() been called?
+
+ // Remove from WebAdmin:
cWebAdmin * WebAdmin = cRoot::Get()->GetWebAdmin();
if (WebAdmin != nullptr)
{
WebAdmin->RemovePlugin(this);
}
+}
+
+
+
- for (TabList::iterator itr = m_Tabs.begin(); itr != m_Tabs.end(); ++itr)
+
+cWebPlugin::cTabNames cWebPlugin::GetTabNames(void) const
+{
+ std::list< std::pair<AString, AString>> NameList;
+ for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
{
- delete *itr;
+ NameList.push_back(std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle));
}
- m_Tabs.clear();
+ return NameList;
}
-std::list<std::pair<AString, AString> > cWebPlugin::GetTabNames(void)
+cWebPlugin::cTabPtr cWebPlugin::GetTabBySafeTitle(const AString & a_SafeTitle) const
{
- std::list< std::pair< AString, AString > > NameList;
- for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr)
+ cCSLock Lock(m_CSTabs);
+ for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
{
- std::pair< AString, AString > StringPair;
- StringPair.first = (*itr)->Title;
- StringPair.second = (*itr)->SafeTitle;
- NameList.push_back( StringPair);
+ if ((*itr)->m_SafeTitle == a_SafeTitle)
+ {
+ return *itr;
+ }
}
- return NameList;
+ return nullptr;
}
-std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest * a_Request)
+std::pair<AString, AString> cWebPlugin::GetTabNameForRequest(const HTTPRequest & a_Request)
{
- std::pair< AString, AString > Names;
- AStringVector Split = StringSplit(a_Request->Path, "/");
+ AStringVector Split = StringSplit(a_Request.Path, "/");
+ if (Split.empty())
+ {
+ return std::make_pair(AString(), AString());
+ }
- if (Split.size() > 1)
+ cCSLock Lock(m_CSTabs);
+ cTabPtr Tab;
+ if (Split.size() > 2) // If we got the tab name, show that page
{
- sWebPluginTab * Tab = nullptr;
- if (Split.size() > 2) // If we got the tab name, show that page
+ for (auto itr = m_Tabs.cbegin(), end = m_Tabs.cend(); itr != end; ++itr)
{
- for (TabList::iterator itr = GetTabs().begin(); itr != GetTabs().end(); ++itr)
+ if ((*itr)->m_SafeTitle.compare(Split[2]) == 0) // This is the one!
{
- if ((*itr)->SafeTitle.compare(Split[2]) == 0) // This is the one!
- {
- Tab = *itr;
- break;
- }
- }
- }
- else // Otherwise show the first tab
- {
- if (GetTabs().size() > 0)
- {
- Tab = *GetTabs().begin();
+ return std::make_pair((*itr)->m_Title, (*itr)->m_SafeTitle);
}
}
+ // Tab name not found, display an "empty" page:
+ return std::make_pair(AString(), AString());
+ }
- if (Tab != nullptr)
- {
- Names.first = Tab->Title;
- Names.second = Tab->SafeTitle;
- }
+ // Show the first tab:
+ if (!m_Tabs.empty())
+ {
+ return std::make_pair(m_Tabs.front()->m_SafeTitle, m_Tabs.front()->m_SafeTitle);
}
- return Names;
+ // No tabs at all:
+ return std::make_pair(AString(), AString());
}
@@ -101,14 +108,16 @@ std::pair< AString, AString > cWebPlugin::GetTabNameForRequest(const HTTPRequest
AString cWebPlugin::SafeString(const AString & a_String)
{
AString RetVal;
- for (unsigned int i = 0; i < a_String.size(); ++i)
+ auto len = a_String.size();
+ RetVal.reserve(len);
+ for (size_t i = 0; i < len; ++i)
{
char c = a_String[i];
if (c == ' ')
{
c = '_';
}
- RetVal.push_back( c);
+ RetVal.push_back(c);
}
return RetVal;
}
@@ -116,3 +125,28 @@ AString cWebPlugin::SafeString(const AString & a_String)
+
+void cWebPlugin::AddNewWebTab(const AString & a_Title, int a_UserData)
+{
+ auto Tab = std::make_shared<cTab>(a_Title, a_UserData);
+ cCSLock Lock(m_CSTabs);
+ m_Tabs.push_back(Tab);
+}
+
+
+
+
+
+void cWebPlugin::ClearTabs(void)
+{
+ // Remove the webadmin tabs:
+ cTabPtrs Tabs;
+ {
+ cCSLock Lock(m_CSTabs);
+ std::swap(Tabs, m_Tabs);
+ }
+}
+
+
+
+
diff --git a/src/Bindings/WebPlugin.h b/src/Bindings/WebPlugin.h
index 9b825b918..ade4f4359 100644
--- a/src/Bindings/WebPlugin.h
+++ b/src/Bindings/WebPlugin.h
@@ -12,34 +12,67 @@ class cWebPlugin
{
public:
// tolua_end
+
+ struct cTab
+ {
+ AString m_Title;
+ AString m_SafeTitle;
+ int m_UserData;
+
+ cTab(const AString & a_Title, int a_UserData):
+ m_Title(a_Title),
+ m_SafeTitle(cWebPlugin::SafeString(a_Title)),
+ m_UserData(a_UserData)
+ {
+ }
+ };
+
+ typedef SharedPtr<cTab> cTabPtr;
+ typedef std::list<cTabPtr> cTabPtrs;
+ typedef std::list<std::pair<AString, AString>> cTabNames;
+
+
cWebPlugin();
+
virtual ~cWebPlugin();
// tolua_begin
+
+ /** Returns the title of the plugin, as it should be presented in the webadmin's pages tree. */
virtual const AString GetWebTitle(void) const = 0;
- virtual AString HandleWebRequest(const HTTPRequest * a_Request) = 0;
+ /** Sanitizes the input string, replacing spaces with underscores. */
+ static AString SafeString(const AString & a_String);
- static AString SafeString( const AString & a_String);
// tolua_end
- struct sWebPluginTab
- {
- std::string Title;
- std::string SafeTitle;
+ virtual AString HandleWebRequest(const HTTPRequest & a_Request) = 0;
- int UserData;
- };
+ /** Adds a new web tab with the specified contents. */
+ void AddNewWebTab(const AString & a_Title, int a_UserData);
+
+ /** Removes all the tabs. */
+ void ClearTabs(void);
- typedef std::list< sWebPluginTab* > TabList;
- TabList & GetTabs() { return m_Tabs; }
+ /** Returns all the tabs that this plugin has registered. */
+ const cTabPtrs & GetTabs(void) const { return m_Tabs; }
- typedef std::list< std::pair<AString, AString> > TabNameList;
- TabNameList GetTabNames(); // >> EXPORTED IN MANUALBINDINGS <<
- std::pair< AString, AString > GetTabNameForRequest(const HTTPRequest* a_Request);
+ /** Returns all of the tabs that this plugin has registered. */
+ cTabNames GetTabNames(void) const; // Exported in ManualBindings.cpp
+
+ /** Returns the tab that has the specified SafeTitle.
+ Returns nullptr if no such tab. */
+ cTabPtr GetTabBySafeTitle(const AString & a_SafeTitle) const;
+
+ std::pair<AString, AString> GetTabNameForRequest(const HTTPRequest & a_Request);
private:
- TabList m_Tabs;
+ /** All tabs that this plugin has registered.
+ Protected against multithreaded access by m_CSTabs. */
+ cTabPtrs m_Tabs;
+
+ /** Protects m_Tabs against multithreaded access. */
+ mutable cCriticalSection m_CSTabs;
}; // tolua_export
diff --git a/src/Server.cpp b/src/Server.cpp
index df2c7deef..8b6a2e769 100644
--- a/src/Server.cpp
+++ b/src/Server.cpp
@@ -464,22 +464,12 @@ 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;
- }
- }
+ cPluginManager::Get()->RefreshPluginList(); // Refresh the plugin list, so that if the plugin was added just now, it is loadable
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 +478,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..aff3b4710 100644
--- a/src/WebAdmin.cpp
+++ b/src/WebAdmin.cpp
@@ -464,10 +464,10 @@ sWebAdminPage cWebAdmin::GetPage(const HTTPRequest & a_Request)
{
if ((*itr)->GetWebTitle() == Split[1])
{
- Page.Content = (*itr)->HandleWebRequest(&a_Request);
+ Page.Content = (*itr)->HandleWebRequest(a_Request);
cWebPlugin * WebPlugin = *itr;
FoundPlugin = WebPlugin->GetWebTitle();
- AString TabName = WebPlugin->GetTabNameForRequest(&a_Request).first;
+ AString TabName = WebPlugin->GetTabNameForRequest(a_Request).first;
Page.PluginName = FoundPlugin;
Page.TabName = TabName;
break;
@@ -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)