#ifndef PYTHON_WRAPPERS_PYPLUGINS_H #define PYTHON_WRAPPERS_PYPLUGINS_H #include "../pybind11_all.h" #include #include #include #include #include #include #include #include #include #include #include #include // these needs to be defined in a header file for automoc - this file is included only // in pyplugins.cpp namespace mo2::python { using namespace MOBase; // we need two base trampoline because IPluginGame has some final methods. template class PyPluginBaseNoFinal : public PluginBase { public: using PluginBase::PluginBase; PyPluginBaseNoFinal(PyPluginBaseNoFinal const&) = delete; PyPluginBaseNoFinal(PyPluginBaseNoFinal&&) = delete; PyPluginBaseNoFinal& operator=(PyPluginBaseNoFinal const&) = delete; PyPluginBaseNoFinal& operator=(PyPluginBaseNoFinal&&) = delete; bool init(IOrganizer* organizer) override { // Wrap the override manually so we can surface diagnostics when a // Python plugin's init() silently returns False (the common case // when a plugin's own try/except eats the exception) or raises // an exception that would otherwise bubble up as a cryptic // "plugin failed to initialize" in plugincontainer. try { pybind11::gil_scoped_acquire gil; pybind11::function override_fn = pybind11::get_override( static_cast(this), "init"); if (!override_fn) { pybind11::pybind11_fail( "Tried to call pure virtual function \"PluginBase::init\""); } pybind11::object result = override_fn(organizer); const bool ok = pybind11::cast(result); if (!ok) { QString pluginName; try { pluginName = this->name(); } catch (...) { } MOBase::log::warn( "Python plugin init() returned False (plugin: '{}'). " "The plugin is probably swallowing an exception in " "its own try/except — check the plugin source for " "details.", pluginName.isEmpty() ? std::string("") : pluginName.toStdString()); } return ok; } catch (const pybind11::error_already_set& e) { QString pluginName; try { pluginName = this->name(); } catch (...) { } MOBase::log::error( "Python plugin init() raised an exception (plugin: '{}'): " "{}", pluginName.isEmpty() ? std::string("") : pluginName.toStdString(), e.what()); return false; } } QString name() const override { PYBIND11_OVERRIDE_PURE(QString, PluginBase, name, ); } QString localizedName() const override { PYBIND11_OVERRIDE(QString, PluginBase, localizedName, ); } QString master() const override { PYBIND11_OVERRIDE(QString, PluginBase, master, ); } QString author() const override { PYBIND11_OVERRIDE_PURE(QString, PluginBase, author, ); } QString description() const override { PYBIND11_OVERRIDE_PURE(QString, PluginBase, description, ); } VersionInfo version() const override { PYBIND11_OVERRIDE_PURE(VersionInfo, PluginBase, version, ); } QList settings() const override { PYBIND11_OVERRIDE_PURE(QList, PluginBase, settings, ); } }; template class PyPluginBase : public PyPluginBaseNoFinal { public: using PyPluginBaseNoFinal::PyPluginBaseNoFinal; std::vector> requirements() const { PYBIND11_OVERRIDE(std::vector>, PluginBase, requirements, ); } bool enabledByDefault() const override { PYBIND11_OVERRIDE(bool, PluginBase, enabledByDefault, ); } }; // these classes do not inherit IPlugin or QObject so we need intermediate class to // get proper bindings class IPyPlugin : public QObject, public IPlugin {}; class IPyPluginFileMapper : public IPyPlugin, public IPluginFileMapper {}; class IPyPluginDiagnose : public IPyPlugin, public IPluginDiagnose {}; // PyXXX classes - trampoline classes for the plugins class PyPlugin : public PyPluginBase { Q_OBJECT Q_INTERFACES(MOBase::IPlugin) }; class PyPluginFileMapper : public PyPluginBase { Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginFileMapper) public: MappingType mappings() const override { PYBIND11_OVERRIDE_PURE(MappingType, IPyPluginFileMapper, mappings, ); } }; class PyPluginDiagnose : public PyPluginBase { Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose) public: std::vector activeProblems() const { PYBIND11_OVERRIDE_PURE(std::vector, IPyPluginDiagnose, activeProblems, ); } QString shortDescription(unsigned int key) const { PYBIND11_OVERRIDE_PURE(QString, IPyPluginDiagnose, shortDescription, key); } QString fullDescription(unsigned int key) const { PYBIND11_OVERRIDE_PURE(QString, IPyPluginDiagnose, fullDescription, key); } bool hasGuidedFix(unsigned int key) const { PYBIND11_OVERRIDE_PURE(bool, IPyPluginDiagnose, hasGuidedFix, key); } void startGuidedFix(unsigned int key) const { PYBIND11_OVERRIDE_PURE(void, IPyPluginDiagnose, startGuidedFix, key); } // we need to bring this in public scope using IPluginDiagnose::invalidate; }; class PyPluginTool : public PyPluginBase { Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) public: QString displayName() const override { PYBIND11_OVERRIDE_PURE(QString, IPluginTool, displayName, ); } QString tooltip() const override { PYBIND11_OVERRIDE_PURE(QString, IPluginTool, tooltip, ); } QIcon icon() const override { PYBIND11_OVERRIDE_PURE(QIcon, IPluginTool, icon, ); } void setParentWidget(QWidget* widget) override { PYBIND11_OVERRIDE(void, IPluginTool, setParentWidget, widget); } void display() const override { PYBIND11_OVERRIDE_PURE(void, IPluginTool, display, ); } // we need to bring this in public scope using IPluginTool::parentWidget; }; class PyPluginPreview : public PyPluginBase { Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview) public: std::set supportedExtensions() const override { PYBIND11_OVERRIDE_PURE(std::set, IPluginPreview, supportedExtensions, ); } bool supportsArchives() const override { PYBIND11_OVERRIDE(bool, IPluginPreview, supportsArchives, ); } QWidget* genFilePreview(const QString& fileName, const QSize& maxSize) const override { PYBIND11_OVERRIDE_PURE(QWidget*, IPluginPreview, genFilePreview, fileName, maxSize); } QWidget* genDataPreview(const QByteArray& fileData, const QString& fileName, const QSize& maxSize) const override { PYBIND11_OVERRIDE(QWidget*, IPluginPreview, genDataPreview, fileData, fileName, maxSize); } }; class PyPluginModPage : public PyPluginBase { Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginModPage) public: QString displayName() const override { PYBIND11_OVERRIDE_PURE(QString, IPluginModPage, displayName, ); } QIcon icon() const override { PYBIND11_OVERRIDE_PURE(QIcon, IPluginModPage, icon, ); } QUrl pageURL() const override { PYBIND11_OVERRIDE_PURE(QUrl, IPluginModPage, pageURL, ); } bool useIntegratedBrowser() const override { PYBIND11_OVERRIDE_PURE(bool, IPluginModPage, useIntegratedBrowser, ); } bool handlesDownload(const QUrl& pageURL, const QUrl& downloadURL, ModRepositoryFileInfo& fileInfo) const override { // TODO: cannot modify fileInfo from Python PYBIND11_OVERRIDE_PURE(bool, IPluginModPage, handlesDownload, pageURL, downloadURL, &fileInfo); } void setParentWidget(QWidget* widget) override { PYBIND11_OVERRIDE(void, IPluginModPage, setParentWidget, widget); } // we need to bring this in public scope using IPluginModPage::parentWidget; }; // installers template class PyPluginInstallerBase : public PyPluginBase { public: using PyPluginBase::PyPluginBase; unsigned int priority() const override { PYBIND11_OVERRIDE_PURE(unsigned int, PluginInstallerBase, priority); } bool isManualInstaller() const override { PYBIND11_OVERRIDE_PURE(bool, PluginInstallerBase, isManualInstaller, ); } void onInstallationStart(QString const& archive, bool reinstallation, IModInterface* currentMod) { PYBIND11_OVERRIDE(void, PluginInstallerBase, onInstallationStart, archive, reinstallation, currentMod); } void onInstallationEnd(IPluginInstaller::EInstallResult result, IModInterface* newMod) { PYBIND11_OVERRIDE(void, PluginInstallerBase, onInstallationEnd, result, newMod); } bool isArchiveSupported(std::shared_ptr tree) const override { PYBIND11_OVERRIDE_PURE(bool, PluginInstallerBase, isArchiveSupported, tree); } // we need to bring these in public scope using PluginInstallerBase::manager; using PluginInstallerBase::parentWidget; }; class PyPluginInstallerCustom : public PyPluginInstallerBase { Q_OBJECT Q_INTERFACES( MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) public: bool isArchiveSupported(const QString& archiveName) const { PYBIND11_OVERRIDE_PURE(bool, IPluginInstallerCustom, isArchiveSupported, archiveName); } std::set supportedExtensions() const { PYBIND11_OVERRIDE_PURE(std::set, IPluginInstallerCustom, supportedExtensions, ); } EInstallResult install(GuessedValue& modName, QString gameName, const QString& archiveName, const QString& version, int nexusID) override { PYBIND11_OVERRIDE_PURE(EInstallResult, IPluginInstallerCustom, install, &modName, gameName, archiveName, version, nexusID); } }; class PyPluginInstallerSimple : public PyPluginInstallerBase { Q_OBJECT Q_INTERFACES( MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple) public: using py_install_return_type = std::variant, std::tuple, QString, int>>; EInstallResult install(GuessedValue& modName, std::shared_ptr& tree, QString& version, int& nexusID) override { const auto result = [&, this]() { PYBIND11_OVERRIDE_PURE(py_install_return_type, IPluginInstallerSimple, install, &modName, tree, version, nexusID); }(); return std::visit( [&tree, &version, &nexusID](auto const& t) { using type = std::decay_t; if constexpr (std::is_same_v) { return t; } else if constexpr (std::is_same_v>) { tree = t; return RESULT_SUCCESS; } else if constexpr (std::is_same_v< type, std::tuple, QString, int>>) { tree = std::get<1>(t); version = std::get<2>(t); nexusID = std::get<3>(t); return std::get<0>(t); } }, result); } }; // game class PyPluginGame : public PyPluginBaseNoFinal { Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) public: void detectGame() override { PYBIND11_OVERRIDE_PURE(void, IPluginGame, detectGame, ); } QString gameName() const override { PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameName, ); } QString displayGameName() const override { PYBIND11_OVERRIDE(QString, IPluginGame, displayGameName, ); } void initializeProfile(const QDir& directory, ProfileSettings settings) const override { PYBIND11_OVERRIDE_PURE(void, IPluginGame, initializeProfile, directory, settings); } std::vector> listSaves(QDir folder) const override { PYBIND11_OVERRIDE_PURE(std::vector>, IPluginGame, listSaves, folder); } bool isInstalled() const override { PYBIND11_OVERRIDE_PURE(bool, IPluginGame, isInstalled, ); } bool isNativeLinux() const override { PYBIND11_OVERRIDE(bool, IPluginGame, isNativeLinux, ); } bool usesVFS() const override { PYBIND11_OVERRIDE(bool, IPluginGame, usesVFS, ); } QIcon gameIcon() const override { PYBIND11_OVERRIDE_PURE(QIcon, IPluginGame, gameIcon, ); } QDir gameDirectory() const override { PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, gameDirectory, ); } QDir dataDirectory() const override { PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, dataDirectory, ); } QString modDataDirectory() const override { PYBIND11_OVERRIDE(QString, IPluginGame, modDataDirectory, ); } QMap secondaryDataDirectories() const override { using string_dir_map = QMap; PYBIND11_OVERRIDE(string_dir_map, IPluginGame, secondaryDataDirectories, ); } void setGamePath(const QString& path) override { PYBIND11_OVERRIDE_PURE(void, IPluginGame, setGamePath, path); } QDir documentsDirectory() const override { PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, documentsDirectory, ); } QDir savesDirectory() const override { PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, savesDirectory, ); } QList executables() const override { PYBIND11_OVERRIDE(QList, IPluginGame, executables, ); } QList executableForcedLoads() const override { PYBIND11_OVERRIDE_PURE(QList, IPluginGame, executableForcedLoads, ); } QString steamAPPId() const override { PYBIND11_OVERRIDE(QString, IPluginGame, steamAPPId, ); } QStringList primaryPlugins() const override { PYBIND11_OVERRIDE(QStringList, IPluginGame, primaryPlugins, ); } QStringList enabledPlugins() const override { PYBIND11_OVERRIDE(QStringList, IPluginGame, enabledPlugins, ); } QStringList gameVariants() const override { PYBIND11_OVERRIDE(QStringList, IPluginGame, gameVariants, ); } void setGameVariant(const QString& variant) override { PYBIND11_OVERRIDE_PURE(void, IPluginGame, setGameVariant, variant); } QString binaryName() const override { PYBIND11_OVERRIDE_PURE(QString, IPluginGame, binaryName, ); } QString gameShortName() const override { PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameShortName, ); } QString lootGameName() const override { PYBIND11_OVERRIDE(QString, IPluginGame, lootGameName, ); } QStringList primarySources() const override { PYBIND11_OVERRIDE(QStringList, IPluginGame, primarySources, ); } QStringList validShortNames() const override { PYBIND11_OVERRIDE(QStringList, IPluginGame, validShortNames, ); } QString gameNexusName() const override { PYBIND11_OVERRIDE(QString, IPluginGame, gameNexusName, ); } QStringList iniFiles() const override { PYBIND11_OVERRIDE(QStringList, IPluginGame, iniFiles, ); } QStringList DLCPlugins() const override { PYBIND11_OVERRIDE(QStringList, IPluginGame, DLCPlugins, ); } QStringList CCPlugins() const override { PYBIND11_OVERRIDE(QStringList, IPluginGame, CCPlugins, ); } LoadOrderMechanism loadOrderMechanism() const override { PYBIND11_OVERRIDE(LoadOrderMechanism, IPluginGame, loadOrderMechanism, ); } SortMechanism sortMechanism() const override { PYBIND11_OVERRIDE(SortMechanism, IPluginGame, sortMechanism, ); } int nexusModOrganizerID() const override { PYBIND11_OVERRIDE(int, IPluginGame, nexusModOrganizerID, ); } int nexusGameID() const override { PYBIND11_OVERRIDE_PURE(int, IPluginGame, nexusGameID, ); } bool looksValid(QDir const& dir) const override { PYBIND11_OVERRIDE_PURE(bool, IPluginGame, looksValid, dir); } QString gameVersion() const override { PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameVersion, ); } QString getLauncherName() const override { PYBIND11_OVERRIDE_PURE(QString, IPluginGame, getLauncherName, ); } QString getSupportURL() const override { PYBIND11_OVERRIDE(QString, IPluginGame, getSupportURL, ); } QMap getModMappings() const override { using vfs_map = QMap; PYBIND11_OVERRIDE(vfs_map, IPluginGame, getModMappings, ); } }; } // namespace mo2::python #endif