From f2f9e11fdd876821107cff0c1c5b9d8ecf66691f Mon Sep 17 00:00:00 2001 From: Tannin Date: Fri, 21 Nov 2014 14:45:30 +0100 Subject: - started on a refactoring moving functionality out of the MainWindow class - started on support for game-plugins --- src/plugincontainer.cpp | 329 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 329 insertions(+) create mode 100644 src/plugincontainer.cpp (limited to 'src/plugincontainer.cpp') diff --git a/src/plugincontainer.cpp b/src/plugincontainer.cpp new file mode 100644 index 00000000..bd96828e --- /dev/null +++ b/src/plugincontainer.cpp @@ -0,0 +1,329 @@ +#include "plugincontainer.h" +#include "organizerproxy.h" +#include "report.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace MOBase; +using namespace MOShared; + +namespace bf = boost::fusion; + + +PluginContainer::PluginContainer(OrganizerCore *organizer) + : m_Organizer(organizer) +{ +} + + +void PluginContainer::setUserInterface(IUserInterface *userInterface, QWidget *widget) +{ + for (IPluginProxy *proxy : bf::at_key(m_Plugins)) { + proxy->setParentWidget(widget); + } + m_UserInterface = userInterface; +} + + +QStringList PluginContainer::pluginFileNames() const +{ + QStringList result; + for (QPluginLoader *loader : m_PluginLoaders) { + result.append(loader->fileName()); + } + return result; +} + + +bool PluginContainer::verifyPlugin(IPlugin *plugin) +{ + if (plugin == NULL) { + return false; + } else if (!plugin->init(new OrganizerProxy(m_Organizer, plugin->name()))) { + qWarning("plugin failed to initialize"); + return false; + } + return true; +} + + +void PluginContainer::registerGame(IPluginGame *game) +{ + m_SupportedGames.insert({ game->gameName(), game }); +} + + +bool PluginContainer::registerPlugin(QObject *plugin, const QString &fileName) +{ + { // generic treatment for all plugins + IPlugin *pluginObj = qobject_cast(plugin); + if (pluginObj == NULL) { + qDebug("not an IPlugin"); + return false; + } + plugin->setProperty("filename", fileName); + m_Organizer->settings().registerPlugin(pluginObj); + } + + { // diagnosis plugins + IPluginDiagnose *diagnose = qobject_cast(plugin); + if (diagnose != NULL) { + bf::at_key(m_Plugins).push_back(diagnose); + m_DiagnosisConnections.push_back( + diagnose->onInvalidated([&] () { emit diagnosisUpdate(); }) + ); + } + } + { // mod page plugin + IPluginModPage *modPage = qobject_cast(plugin); + if (verifyPlugin(modPage)) { + bf::at_key(m_Plugins).push_back(modPage); + registerModPage(modPage); + return true; + } + } + { // game plugin + IPluginGame *game = qobject_cast(plugin); + if (verifyPlugin(game)) { + bf::at_key(m_Plugins).push_back(game); + registerGame(game); + return true; + } + } + { // tool plugins + IPluginTool *tool = qobject_cast(plugin); + if (verifyPlugin(tool)) { + bf::at_key(m_Plugins).push_back(tool); + registerPluginTool(tool); + return true; + } + } + { // installer plugins + IPluginInstaller *installer = qobject_cast(plugin); + if (verifyPlugin(installer)) { + bf::at_key(m_Plugins).push_back(installer); + m_Organizer->installationManager()->registerInstaller(installer); + return true; + } + } + { // preview plugins + IPluginPreview *preview = qobject_cast(plugin); + if (verifyPlugin(preview)) { + bf::at_key(m_Plugins).push_back(preview); + m_PreviewGenerator.registerPlugin(preview); + return true; + } + } + { // proxy plugins + IPluginProxy *proxy = qobject_cast(plugin); + if (verifyPlugin(proxy)) { + bf::at_key(m_Plugins).push_back(proxy); + QStringList pluginNames = proxy->pluginList(QCoreApplication::applicationDirPath() + "/" + ToQString(AppConfig::pluginPath())); + foreach (const QString &pluginName, pluginNames) { + try { + QObject *proxiedPlugin = proxy->instantiate(pluginName); + if (proxiedPlugin != NULL) { + if (registerPlugin(proxiedPlugin, pluginName)) { + qDebug("loaded plugin \"%s\"", qPrintable(pluginName)); + } else { + qWarning("plugin \"%s\" failed to load", qPrintable(pluginName)); + } + } + } catch (const std::exception &e) { + reportError(QObject::tr("failed to init plugin %1: %2").arg(pluginName).arg(e.what())); + } + } + return true; + } + } + + { // dummy plugins + // only initialize these, no processing otherwise + IPlugin *dummy = qobject_cast(plugin); + if (verifyPlugin(dummy)) { + bf::at_key(m_Plugins).push_back(dummy); + return true; + } + } + + qDebug("no matching plugin interface"); + + return false; +} + +struct clearPlugins +{ + template + void operator()(T& t) const + { + t.second.clear(); + } +}; + +void PluginContainer::unloadPlugins() +{ + if (m_UserInterface != nullptr) { + m_UserInterface->disconnectPlugins(); + } + + // disconnect all slots before unloading plugins so plugins don't have to take care of that + m_Organizer->disconnectPlugins(); + + bf::for_each(m_Plugins, clearPlugins()); + + foreach (const boost::signals2::connection &connection, m_DiagnosisConnections) { + connection.disconnect(); + } + m_DiagnosisConnections.clear(); + + while (!m_PluginLoaders.empty()) { + QPluginLoader *loader = m_PluginLoaders.back(); + m_PluginLoaders.pop_back(); + if (!loader->unload()) { + qDebug("failed to unload %s: %s", qPrintable(loader->fileName()), qPrintable(loader->errorString())); + } + delete loader; + } +} + +IPluginGame *PluginContainer::managedGame(const QString &name) const +{ + auto iter = m_SupportedGames.find(name); + if (iter != m_SupportedGames.end()) { + return iter->second; + } else { + return nullptr; + } +} + +const PreviewGenerator &PluginContainer::previewGenerator() const +{ + return m_PreviewGenerator; +} + +void PluginContainer::loadPlugins() +{ + unloadPlugins(); + + foreach (QObject *plugin, QPluginLoader::staticInstances()) { + registerPlugin(plugin, ""); + } + + QFile loadCheck(qApp->property("dataPath").toString() + "/plugin_loadcheck.tmp"); + if (loadCheck.exists() && loadCheck.open(QIODevice::ReadOnly)) { + // oh, there was a failed plugin load last time. Find out which plugin was loaded last + QString fileName; + while (!loadCheck.atEnd()) { + fileName = QString::fromUtf8(loadCheck.readLine().constData()).trimmed(); + } + if (QMessageBox::question(nullptr, QObject::tr("Plugin error"), + QObject::tr("It appears the plugin \"%1\" failed to load last startup and caused MO to crash. Do you want to disable it?\n" + "(Please note: If this is the first time you see this message for this plugin you may want to give it another try. " + "The plugin may be able to recover from the problem)").arg(fileName), + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes) == QMessageBox::Yes) { + m_Organizer->settings().addBlacklistPlugin(fileName); + } + loadCheck.close(); + } + + loadCheck.open(QIODevice::WriteOnly); + + QString pluginPath = QDir::fromNativeSeparators(ToQString(GameInfo::instance().getOrganizerDirectory())) + "/" + ToQString(AppConfig::pluginPath()); + qDebug("looking for plugins in %s", QDir::toNativeSeparators(pluginPath).toUtf8().constData()); + QDirIterator iter(pluginPath, QDir::Files | QDir::NoDotAndDotDot); + + while (iter.hasNext()) { + iter.next(); + if (m_Organizer->settings().pluginBlacklisted(iter.fileName())) { + qDebug("plugin \"%s\" blacklisted", qPrintable(iter.fileName())); + continue; + } + loadCheck.write(iter.fileName().toUtf8()); + loadCheck.write("\n"); + loadCheck.flush(); + QString pluginName = iter.filePath(); + if (QLibrary::isLibrary(pluginName)) { + QPluginLoader *pluginLoader = new QPluginLoader(pluginName, this); + if (pluginLoader->instance() == NULL) { + m_FailedPlugins.push_back(pluginName); + qCritical("failed to load plugin %s: %s", + qPrintable(pluginName), qPrintable(pluginLoader->errorString())); + } else { + if (registerPlugin(pluginLoader->instance(), pluginName)) { + qDebug("loaded plugin \"%s\"", qPrintable(pluginName)); + m_PluginLoaders.push_back(pluginLoader); + } else { + m_FailedPlugins.push_back(pluginName); + qWarning("plugin \"%s\" failed to load", qPrintable(pluginName)); + } + } + } + } + + // remove the load check file on success + loadCheck.remove(); + + bf::at_key(m_Plugins).push_back(this); + + m_Organizer->connectPlugins(this); +} + + +std::vector PluginContainer::activeProblems() const +{ + std::vector problems; + if (m_FailedPlugins.size()) { + problems.push_back(PROBLEM_PLUGINSNOTLOADED); + } + return problems; +} + +QString PluginContainer::shortDescription(unsigned int key) const +{ + switch (key) { + case PROBLEM_PLUGINSNOTLOADED: { + return tr("Some plugins could not be loaded"); + } break; + default: { + return tr("Description missing"); + } break; + } +} + +QString PluginContainer::fullDescription(unsigned int key) const +{ + switch (key) { + case PROBLEM_PLUGINSNOTLOADED: { + QString result = tr("The following plugins could not be loaded. The reason may be missing dependencies (i.e. python) or an outdated version:") + "
    "; + for (const QString &plugin : m_FailedPlugins) { + result += "
  • " + plugin + "
  • "; + } + result += "
      "; + return result; + } break; + default: { + return tr("Description missing"); + } break; + } +} + +bool PluginContainer::hasGuidedFix(unsigned int) const +{ + return false; +} + +void PluginContainer::startGuidedFix(unsigned int) const +{ +} -- cgit v1.3.1