diff options
Diffstat (limited to '')
-rw-r--r-- | src/Bindings/PluginManager.cpp | 435 |
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; +} + + + + |