123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- /*
- * Copyright (c) Contributors to the Open 3D Engine Project.
- * For complete copyright and license terms please see the LICENSE at the root of this distribution.
- *
- * SPDX-License-Identifier: Apache-2.0 OR MIT
- *
- */
- #include "EditorDefs.h"
- #include "PluginManager.h"
- // Qt
- #include <QLibrary>
- // Editor
- #include "Include/IPlugin.h"
- using TPfnCreatePluginInstance = IPlugin* (*)(PLUGIN_INIT_PARAM* pInitParam);
- using TPfnQueryPluginSettings = void (*)(SPluginSettings&);
- CPluginManager::CPluginManager()
- {
- m_currentUUID = 0;
- }
- CPluginManager::~CPluginManager()
- {
- ReleaseAllPlugins();
- UnloadAllPlugins();
- }
- void CPluginManager::ReleaseAllPlugins()
- {
- CLogFile::WriteLine("[Plugin Manager] Releasing all previous plugins");
- for (auto it = m_plugins.begin(); it != m_plugins.end(); ++it)
- {
- if (it->pPlugin)
- {
- it->pPlugin->Release();
- it->pPlugin = nullptr;
- }
- }
- m_pluginEventMap.clear();
- m_uuidPluginMap.clear();
- }
- void CPluginManager::UnloadAllPlugins()
- {
- CLogFile::WriteLine("[Plugin Manager] Unloading all previous plugins");
- for (auto it = m_plugins.begin(); it != m_plugins.end(); ++it)
- {
- if (it->pPlugin)
- {
- it->pPlugin->Release();
- it->pPlugin = nullptr;
- }
- if (it->hLibrary)
- {
- it->hLibrary->unload();
- delete it->hLibrary;
- }
- }
- m_plugins.clear();
- m_pluginEventMap.clear();
- m_uuidPluginMap.clear();
- }
- namespace
- {
- IPlugin* SafeCallFactory(
- TPfnCreatePluginInstance pfnFactory,
- PLUGIN_INIT_PARAM* pInitParam,
- const char* szFilePath)
- {
- IPlugin* pIPlugin = pfnFactory(pInitParam);
- if (!pIPlugin)
- {
- if (AZ::Debug::Trace::Instance().IsDebuggerPresent())
- {
- AZ::Debug::Trace::Instance().Break();
- }
- CLogFile::FormatLine("Can't initialize plugin '%s'! Possible binary version incompatibility. Please reinstall this plugin.", szFilePath);
- }
- return pIPlugin;
- }
- }
- namespace
- {
- struct SPlugin
- {
- QString m_path;
- QString m_name;
- };
- // This does a topological sort on the plugin list. It will also remove plugins that have
- // missing dependencies or there is a cycle in the dependency tree.
- void SortPluginsByDependency(std::list<SPlugin>& plugins)
- {
- std::list<SPlugin> finalList;
- std::set<QString, stl::less_stricmp<QString> > loadedPlugins;
- while (!plugins.empty())
- {
- for (auto iter = plugins.begin(); iter != plugins.end(); )
- {
- finalList.push_back(*iter);
- loadedPlugins.insert(iter->m_name);
- iter = plugins.erase(iter);
- }
- }
- plugins = finalList;
- }
- }
- bool CPluginManager::LoadPlugins(const char* pluginsPath)
- {
- QString strPath{ pluginsPath };
- CLogFile::WriteLine("[Plugin Manager] Loading plugins...");
- if (!QFileInfo::exists(strPath))
- {
- CLogFile::FormatLine("[Plugin Manager] Cannot find plugin directory '%s'", strPath.toUtf8().data());
- return false;
- }
- std::list<SPlugin> plugins;
- {
- // LY_EDITOR_PLUGINS is defined by the CMakeLists.txt. The editor plugins add themselves to a variable that
- // the editor uses to pass it to the build. Once a plugin is deleted, it will stop being in such variable producing
- // the editor to not load that plugin anymore, even if it is in the output folder.
- #if defined(LY_EDITOR_PLUGINS)
- QDir qPath(strPath);
- AZStd::vector<AZStd::string> tokens;
- AZ::StringFunc::Tokenize(AZStd::string_view(LY_EDITOR_PLUGINS), tokens, ',');
- for (const AZStd::string& token : tokens)
- {
- SPlugin plugin;
- plugin.m_name = QString(token.c_str());
- plugin.m_path = qPath.absoluteFilePath(plugin.m_name);
- plugins.push_back(plugin);
- }
- #endif
- }
- if (plugins.empty())
- {
- CLogFile::FormatLine("[Plugin Manager] Cannot find any plugins in plugin directory '%s'", strPath.toUtf8().data());
- return false;
- }
- // Sort plugins by dependency
- SortPluginsByDependency(plugins);
- for (auto iter = plugins.begin(); iter != plugins.end(); ++iter)
- {
- // Load the plugin's DLL
- QLibrary *hPlugin = new QLibrary(iter->m_path);
- hPlugin->setLoadHints(QLibrary::DeepBindHint);
- AZStd::string pathUtf8(iter->m_path.toUtf8().constData());
- if (!hPlugin->load())
- {
- AZStd::string errorMsg(hPlugin->errorString().toUtf8().constData());
- CLogFile::FormatLine("[Plugin Manager] Can't load plugin DLL '%s' message '%s' !", pathUtf8.c_str(), errorMsg.c_str());
- delete hPlugin;
- continue;
- }
- // Open 3D Engine:
- // Query the plugin settings, check for manual load...
- TPfnQueryPluginSettings pfnQuerySettings = reinterpret_cast<TPfnQueryPluginSettings>(hPlugin->resolve("QueryPluginSettings"));
- if (pfnQuerySettings != nullptr)
- {
- SPluginSettings settings {
- 0
- };
- pfnQuerySettings(settings);
- if (!settings.autoLoad)
- {
- CLogFile::FormatLine("[Plugin Manager] Skipping plugin DLL '%s' because it is marked as non-autoLoad!", pathUtf8.c_str());
- hPlugin->unload();
- delete hPlugin;
- continue;
- }
- }
- // Query the factory pointer
- TPfnCreatePluginInstance pfnFactory = reinterpret_cast<TPfnCreatePluginInstance>(hPlugin->resolve("CreatePluginInstance"));
- if (!pfnFactory)
- {
- CLogFile::FormatLine("[Plugin Manager] Cannot query plugin DLL '%s' factory pointer (is it a Sandbox plugin?)", pathUtf8.c_str());
- hPlugin->unload();
- delete hPlugin;
- continue;
- }
- IPlugin* pPlugin = nullptr;
- PLUGIN_INIT_PARAM sInitParam =
- {
- GetIEditor(),
- SANDBOX_PLUGIN_SYSTEM_VERSION,
- IPlugin::eError_None
- };
- // Create an instance of the plugin
- pPlugin = SafeCallFactory(pfnFactory, &sInitParam, iter->m_path.toUtf8().data());
- if (!pPlugin)
- {
- CLogFile::FormatLine("[Plugin Manager] Cannot initialize plugin '%s'! Possible binary version incompatibility. Please reinstall this plugin.", pathUtf8.c_str());
- assert(pPlugin);
- hPlugin->unload();
- delete hPlugin;
- continue;
- }
- if (!pPlugin)
- {
- if (sInitParam.outErrorCode == IPlugin::eError_VersionMismatch)
- {
- CLogFile::FormatLine("[Plugin Manager] Cannot create instance of plugin DLL '%s'! Version mismatch. Please update the plugin.", pathUtf8.c_str());
- }
- else
- {
- CLogFile::FormatLine("[Plugin Manager] Cannot create instance of plugin DLL '%s'! Error code %u.", pathUtf8.c_str(), sInitParam.outErrorCode);
- }
- hPlugin->unload();
- delete hPlugin;
- continue;
- }
- RegisterPlugin(hPlugin, pPlugin);
- // Write log string about plugin
- CLogFile::FormatLine("[Plugin Manager] Successfully loaded plugin '%s', version '%i' (GUID: %s)",
- pPlugin->GetPluginName(), pPlugin->GetPluginVersion(), pPlugin->GetPluginGUID());
- }
- return true;
- }
- void CPluginManager::RegisterPlugin(QLibrary* dllHandle, IPlugin* pPlugin)
- {
- SPluginEntry entry;
- entry.hLibrary = dllHandle;
- entry.pPlugin = pPlugin;
- m_plugins.push_back(entry);
- m_uuidPluginMap[static_cast<unsigned char>(m_currentUUID)] = pPlugin;
- ++m_currentUUID;
- }
- IPlugin* CPluginManager::GetPluginByGUID(const char* pGUID)
- {
- for (const SPluginEntry& pluginEntry : m_plugins)
- {
- const char* pPluginGuid = pluginEntry.pPlugin->GetPluginGUID();
- if (pPluginGuid && !strcmp(pPluginGuid, pGUID))
- {
- return pluginEntry.pPlugin;
- }
- }
- return nullptr;
- }
- IPlugin* CPluginManager::GetPluginByUIID(uint8 iUserInterfaceID)
- {
- TUIIDPluginIt it;
- it = m_uuidPluginMap.find(iUserInterfaceID);
- if (it == m_uuidPluginMap.end())
- {
- return nullptr;
- }
- return (*it).second;
- }
- IUIEvent* CPluginManager::GetEventByIDAndPluginID(uint8 aPluginID, uint8 aEventID)
- {
- // Return the event interface of a user interface element which is
- // specified by its ID and the user interface ID of the plugin which
- // created the UI element
- IPlugin* pPlugin = nullptr;
- TEventHandlerIt eventIt;
- TPluginEventIt pluginIt;
- pPlugin = GetPluginByUIID(aPluginID);
- if (!pPlugin)
- {
- return nullptr;
- }
- pluginIt = m_pluginEventMap.find(pPlugin);
- if (pluginIt == m_pluginEventMap.end())
- {
- return nullptr;
- }
- eventIt = (*pluginIt).second.find(aEventID);
- if (eventIt == (*pluginIt).second.end())
- {
- return nullptr;
- }
- return (*eventIt).second;
- }
- bool CPluginManager::CanAllPluginsExitNow()
- {
- for (const SPluginEntry& pluginEntry : m_plugins)
- {
- if (pluginEntry.pPlugin && !pluginEntry.pPlugin->CanExitNow())
- {
- return false;
- }
- }
- return true;
- }
- void CPluginManager::AddHandlerForCmdID(IPlugin* pPlugin, uint8 aCmdID, IUIEvent* pEvent)
- {
- m_pluginEventMap[pPlugin][aCmdID] = pEvent;
- }
- void CPluginManager::NotifyPlugins(EEditorNotifyEvent aEventId)
- {
- for (auto it = m_plugins.begin(); it != m_plugins.end(); ++it)
- {
- if (it->pPlugin)
- {
- it->pPlugin->OnEditorNotify(aEventId);
- }
- }
- }
|