summaryrefslogtreecommitdiffstats
path: root/src/Bindings/PluginManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Bindings/PluginManager.cpp435
1 files changed, 253 insertions, 182 deletions
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 8935f7dd3..5b6bec728 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[]
}
@@ -109,60 +118,26 @@ void cPluginManager::ReloadPluginsNow(void)
-void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
+void cPluginManager::ReloadPluginsNow(cSettingsRepositoryInterface & a_Settings)
{
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");
-
- if (KeyNum == -1)
- {
- 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);
- }
- }
- }
- }
-
+ // Refresh the list of plugins to load new ones from disk / remove the deleted ones:
+ RefreshPluginList();
- // Remove invalid plugins from the PluginMap.
- for (PluginMap::iterator itr = m_Plugins.begin(); itr != m_Plugins.end();)
+ // Load the plugins:
+ AStringVector ToLoad = GetFoldersToLoad(a_Settings);
+ for (auto & pluginFolder: ToLoad)
{
- 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();
}
@@ -182,16 +157,16 @@ void cPluginManager::ReloadPluginsNow(cIniFile & a_SettingsIni)
-void cPluginManager::InsertDefaultPlugins(cIniFile & a_SettingsIni)
+void cPluginManager::InsertDefaultPlugins(cSettingsRepositoryInterface & a_Settings)
{
- a_SettingsIni.AddKeyName("Plugins");
- a_SettingsIni.AddKeyComment("Plugins", " Plugin=Debuggers");
- a_SettingsIni.AddKeyComment("Plugins", " Plugin=HookNotify");
- a_SettingsIni.AddKeyComment("Plugins", " Plugin=ChunkWorx");
- a_SettingsIni.AddKeyComment("Plugins", " Plugin=APIDump");
- a_SettingsIni.AddValue("Plugins", "Plugin", "Core");
- a_SettingsIni.AddValue("Plugins", "Plugin", "TransAPI");
- a_SettingsIni.AddValue("Plugins", "Plugin", "ChatLog");
+ a_Settings.AddKeyName("Plugins");
+ a_Settings.AddKeyComment("Plugins", " Plugin=Debuggers");
+ a_Settings.AddKeyComment("Plugins", " Plugin=HookNotify");
+ a_Settings.AddKeyComment("Plugins", " Plugin=ChunkWorx");
+ a_Settings.AddKeyComment("Plugins", " Plugin=APIDump");
+ a_Settings.AddValue("Plugins", "Plugin", "Core");
+ a_Settings.AddValue("Plugins", "Plugin", "TransAPI");
+ a_Settings.AddValue("Plugins", "Plugin", "ChatLog");
}
@@ -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();
@@ -523,14 +525,50 @@ bool cPluginManager::CallHookEntityTeleport(cEntity & a_Entity, const Vector3d &
-bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split)
+bool cPluginManager::CallHookEntityChangingWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ FIND_HOOK(HOOK_ENTITY_CHANGING_WORLD);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnEntityChangingWorld(a_Entity, a_World))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+bool cPluginManager::CallHookEntityChangedWorld(cEntity & a_Entity, cWorld & a_World)
+{
+ FIND_HOOK(HOOK_ENTITY_CHANGED_WORLD);
+ VERIFY_HOOK;
+
+ for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
+ {
+ if ((*itr)->OnEntityChangedWorld(a_Entity, a_World))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result)
{
FIND_HOOK(HOOK_EXECUTE_COMMAND);
VERIFY_HOOK;
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
{
- if ((*itr)->OnExecuteCommand(a_Player, a_Split))
+ if ((*itr)->OnExecuteCommand(a_Player, a_Split, a_EntireCommand, a_Result))
{
return true;
}
@@ -1443,14 +1481,25 @@ cPluginManager::CommandResult cPluginManager::HandleCommand(cPlayer & a_Player,
if (cmd == m_Commands.end())
{
// Command not found
+ // If it started with a slash, ask the plugins if they still want to handle it:
+ if (!a_Command.empty() && (a_Command[0] == '/'))
+ {
+ CommandResult Result = crUnknownCommand;
+ CallHookExecuteCommand(&a_Player, Split, a_Command, Result);
+ return Result;
+ }
return crUnknownCommand;
}
// Ask plugins first if a command is okay to execute the command:
- if (CallHookExecuteCommand(&a_Player, Split))
+ CommandResult Result = crBlocked;
+ if (CallHookExecuteCommand(&a_Player, Split, a_Command, Result))
{
- LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player.GetName().c_str(), Split[0].c_str());
- return crBlocked;
+ if (Result == crBlocked)
+ {
+ LOGINFO("Player %s tried executing command \"%s\" that was stopped by the HOOK_EXECUTE_COMMAND hook", a_Player.GetName().c_str(), Split[0].c_str());
+ }
+ return Result;
}
if (
@@ -1477,68 +1526,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 +1583,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 +1595,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 +1621,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);
@@ -1779,7 +1797,10 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
if (cmd == m_ConsoleCommands.end())
{
// Command not found
- return false;
+ // Still notify the plugins (so that plugins such as Aliases can intercept unknown commands).
+ CommandResult res = crBlocked;
+ CallHookExecuteCommand(nullptr, a_Split, a_Command, res);
+ return (res == crExecuted);
}
if (cmd->second.m_Plugin == nullptr)
@@ -1789,10 +1810,10 @@ bool cPluginManager::ExecuteConsoleCommand(const AStringVector & a_Split, cComma
}
// Ask plugins first if a command is okay to execute the console command:
- if (CallHookExecuteCommand(nullptr, a_Split))
+ CommandResult res = crBlocked;
+ if (CallHookExecuteCommand(nullptr, a_Split, a_Command, res))
{
- a_Output.Out("Command \"%s\" was stopped by the HOOK_EXECUTE_COMMAND hook", a_Split[0].c_str());
- return false;
+ return (res == crExecuted);
}
return cmd->second.m_Plugin->HandleConsoleCommand(a_Split, a_Output, a_Command);
@@ -1836,31 +1857,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 +1890,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 +1914,51 @@ 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(cSettingsRepositoryInterface & a_Settings)
+{
+ // Check if the Plugins section exists.
+ if (!a_Settings.KeyExists("Plugins"))
+ {
+ InsertDefaultPlugins(a_Settings);
+ }
+
+ // Get the list of plugins to load:
+ AStringVector res;
+ auto Values = a_Settings.GetValues("Plugins");
+ for (auto NameValue : Values)
+ {
+ AString ValueName = NameValue.first;
+ if (ValueName.compare("Plugin") == 0)
+ {
+ AString PluginFile = NameValue.second;
+ if (!PluginFile.empty())
+ {
+ res.push_back(PluginFile);
+ }
+ }
+ } // for i - ini values
+
+ return res;
+}
+
+
+
+