From 377199f233f4e0417f61737963af10b6a0ab6748 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 21:07:42 +0100 Subject: [PATCH 01/27] Put plugin wrappers in alphabetical order --- src/runner/proxypluginwrappers.cpp | 186 +++++++++++++++-------------- src/runner/proxypluginwrappers.h | 52 ++++---- src/runner/pythonrunner.cpp | 43 ++++--- 3 files changed, 141 insertions(+), 140 deletions(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 104575b..5a6693f 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -27,6 +27,8 @@ using namespace MOBase; ///////////////////////////// /// IPlugin Wrapper + + bool IPluginWrapper::init(MOBase::IOrganizer *moInfo) { try { @@ -76,97 +78,6 @@ QList IPluginWrapper::settings() const } PYCATCH; } /// end IPlugin Wrapper -///////////////////////////// -/// IPluginTool Wrapper - - -bool IPluginToolWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return this->get_override("init")(boost::python::ptr(moInfo)); - } PYCATCH; -} - -QString IPluginToolWrapper::name() const -{ - try { - return this->get_override("name")().as(); - } PYCATCH; -} - -QString IPluginToolWrapper::author() const -{ - try { - return this->get_override("author")().as(); - } PYCATCH; -} - -QString IPluginToolWrapper::description() const -{ - try { - return this->get_override("description")().as(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginToolWrapper::version() const -{ - try { - return this->get_override("version")().as(); - } PYCATCH; -} - -bool IPluginToolWrapper::isActive() const -{ - try { - return this->get_override("isActive")().as(); - } PYCATCH; -} - -QList IPluginToolWrapper::settings() const -{ - try { - return this->get_override("settings")().as>(); - } PYCATCH; -} - -QString IPluginToolWrapper::displayName() const -{ - try { - return this->get_override("displayName")().as(); - } PYCATCH; -} - -QString IPluginToolWrapper::tooltip() const -{ - try { - return this->get_override("tooltip")().as(); - } PYCATCH; -} - -QIcon IPluginToolWrapper::icon() const -{ - try { - return this->get_override("icon")().as(); - } PYCATCH; -} - -void IPluginToolWrapper::setParentWidget(QWidget *parent) -{ - try { - this->get_override("setParentWidget")(parent); - } PYCATCH; -} - -void IPluginToolWrapper::display() const -{ - try { - GILock lock; - - this->get_override("display")(); - } PYCATCH; -} - -/// end IPluginTool Wrapper ///////////////////////////////////// /// IPluginInstallerCustom Wrapper @@ -258,7 +169,7 @@ std::set IPluginInstallerCustomWrapper::supportedExtensions() const IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(GuessedValue &modName, const QString &archiveName, - const QString &version, int modID) + const QString &version, int modID) { try { return this->get_override("install")(modName, archiveName, version, modID); @@ -272,5 +183,96 @@ void IPluginInstallerCustomWrapper::setParentWidget(QWidget *parent) this->get_override("setParentWidget")(parent); } PYCATCH; } +/// end IPluginInstallerCustom Wrapper +///////////////////////////// +/// IPluginTool Wrapper +bool IPluginToolWrapper::init(MOBase::IOrganizer *moInfo) +{ + try { + return this->get_override("init")(boost::python::ptr(moInfo)); + } PYCATCH; +} + +QString IPluginToolWrapper::name() const +{ + try { + return this->get_override("name")().as(); + } PYCATCH; +} + +QString IPluginToolWrapper::author() const +{ + try { + return this->get_override("author")().as(); + } PYCATCH; +} + +QString IPluginToolWrapper::description() const +{ + try { + return this->get_override("description")().as(); + } PYCATCH; +} + +MOBase::VersionInfo IPluginToolWrapper::version() const +{ + try { + return this->get_override("version")().as(); + } PYCATCH; +} + +bool IPluginToolWrapper::isActive() const +{ + try { + return this->get_override("isActive")().as(); + } PYCATCH; +} + +QList IPluginToolWrapper::settings() const +{ + try { + return this->get_override("settings")().as>(); + } PYCATCH; +} + +QString IPluginToolWrapper::displayName() const +{ + try { + return this->get_override("displayName")().as(); + } PYCATCH; +} + +QString IPluginToolWrapper::tooltip() const +{ + try { + return this->get_override("tooltip")().as(); + } PYCATCH; +} + +QIcon IPluginToolWrapper::icon() const +{ + try { + return this->get_override("icon")().as(); + } PYCATCH; +} + +void IPluginToolWrapper::setParentWidget(QWidget *parent) +{ + try { + this->get_override("setParentWidget")(parent); + } PYCATCH; +} + +void IPluginToolWrapper::display() const +{ + try { + GILock lock; + + this->get_override("display")(); + } PYCATCH; +} + +/// end IPluginTool Wrapper + diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 6131a24..6d724d1 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -26,6 +26,32 @@ public: }; +class IPluginInstallerCustomWrapper : public MOBase::IPluginInstallerCustom, public boost::python::wrapper +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) + +public: + virtual bool init(MOBase::IOrganizer *moInfo); + virtual QString name() const; + virtual QString author() const; + virtual QString description() const; + virtual MOBase::VersionInfo version() const; + virtual bool isActive() const; + virtual QList settings() const; + + virtual unsigned int priority() const; + virtual bool isManualInstaller() const; + virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const; + virtual bool isArchiveSupported(const QString &archiveName) const; + virtual std::set supportedExtensions() const; + virtual EInstallResult install(MOBase::GuessedValue &modName, const QString &archiveName, + const QString &version, int modID); + virtual void setParentWidget(QWidget *parent); + +}; + + class IPluginToolWrapper: public MOBase::IPluginTool, public boost::python::wrapper { Q_OBJECT @@ -50,32 +76,6 @@ public slots: }; -class IPluginInstallerCustomWrapper: public MOBase::IPluginInstallerCustom, public boost::python::wrapper -{ - Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) - -public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; - - virtual unsigned int priority() const; - virtual bool isManualInstaller() const; - virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const; - virtual bool isArchiveSupported(const QString &archiveName) const; - virtual std::set supportedExtensions() const; - virtual EInstallResult install(MOBase::GuessedValue &modName, const QString &archiveName, - const QString &version, int modID); - virtual void setParentWidget(QWidget *parent); - -}; - - #endif // PROXYPLUGINWRAPPERS_H diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 79ad1c7..8af8e86 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -3,11 +3,10 @@ #pragma warning( disable : 4100 ) #pragma warning( disable : 4996 ) -#include "iplugingame.h" +#include +#include #include #include -#include -#include #include "uibasewrappers.h" #include "proxypluginwrappers.h" @@ -713,14 +712,6 @@ BOOST_PYTHON_MODULE(mobase) .value("literal", MOBase::VersionInfo::SCHEME_LITERAL) ; - bpy::enum_("InstallResult") - .value("success", MOBase::IPluginInstaller::RESULT_SUCCESS) - .value("failed", MOBase::IPluginInstaller::RESULT_FAILED) - .value("canceled", MOBase::IPluginInstaller::RESULT_CANCELED) - .value("manualRequested", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED) - .value("notAttempted", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) - ; - bpy::class_("VersionInfo") .def(bpy::init()) .def(bpy::init()) @@ -847,16 +838,6 @@ BOOST_PYTHON_MODULE(mobase) .def("variants", &MOBase::GuessedValue::variants, bpy::return_value_policy()) ; - bpy::class_("IPlugin"); - - bpy::class_, boost::noncopyable>("IPluginTool") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) - ; - - bpy::class_("IPluginInstallerCustom") - .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)) - ; - bpy::to_python_converter>(); QFlags_from_python_obj(); Functor0_converter(); // converter for the onRefreshed-callback @@ -890,6 +871,8 @@ BOOST_PYTHON_MODULE(mobase) .def("onModMoved", bpy::pure_virtual(&MOBase::IModList::onModMoved)) ; + bpy::class_("IPlugin"); + bpy::enum_("LoadOrderMechanism") .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime) .value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) @@ -956,6 +939,22 @@ BOOST_PYTHON_MODULE(mobase) ; + bpy::enum_("InstallResult") + .value("success", MOBase::IPluginInstaller::RESULT_SUCCESS) + .value("failed", MOBase::IPluginInstaller::RESULT_FAILED) + .value("canceled", MOBase::IPluginInstaller::RESULT_CANCELED) + .value("manualRequested", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED) + .value("notAttempted", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED) + ; + + bpy::class_("IPluginInstallerCustom") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)) + ; + + bpy::class_, boost::noncopyable>("IPluginTool") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) + ; + bpy::class_("QDir") .def("absolutePath", &QDir::absolutePath) ; @@ -1082,9 +1081,9 @@ QObject *PythonRunner::instantiate(const QString &pluginName) m_PythonObjects[pluginName] = moduleNamespace["createPlugin"](); bpy::object pluginObj = m_PythonObjects[pluginName]; + TRY_PLUGIN_TYPE(IPluginGame, pluginObj); TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); TRY_PLUGIN_TYPE(IPluginTool, pluginObj); - TRY_PLUGIN_TYPE(IPluginGame, pluginObj); } catch (const bpy::error_already_set&) { qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); reportPythonError(); From 8dd9a710b4a963d850a8b60a6b7919bc0af0aa8f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 21:12:32 +0100 Subject: [PATCH 02/27] Replace wrong QDir converter --- src/runner/pythonrunner.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 8af8e86..6423533 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -423,6 +423,7 @@ template <> struct MetaData { static const char *className template <> struct MetaData { static const char *className() { return "QObject"; } }; template <> struct MetaData { static const char *className() { return "QObject"; } }; template <> struct MetaData { static const char *className() { return "QWidget"; } }; +template <> struct MetaData { static const char *className() { return "QDir"; } }; template <> struct MetaData { static const char *className() { return "QIcon"; } }; template <> struct MetaData { static const char *className() { return "QStringList"; } }; template <> struct MetaData { static const char *className() { return "QVariant"; } }; @@ -686,6 +687,7 @@ BOOST_PYTHON_MODULE(mobase) QString_from_python_str(); //QClass_converters(); + QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); @@ -955,10 +957,6 @@ BOOST_PYTHON_MODULE(mobase) .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) ; - bpy::class_("QDir") - .def("absolutePath", &QDir::absolutePath) - ; - GuessedValue_converters(); bpy::to_python_converter(); From 4a558d4caf96b2f52e2eb2b6814a1047f881285c Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 22:02:55 +0100 Subject: [PATCH 03/27] Implement from_python handling for QStringList --- src/runner/pythonrunner.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 6423533..175886c 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -496,7 +496,14 @@ struct QClass_converters static void *QClass_from_PyQt(PyObject *objPtr) { - if (!PyObject_TypeCheck(objPtr, sipAPI()->api_wrapper_type)) { + if (!PyObject_TypeCheck(objPtr, sipAPI()->api_simplewrapper_type)) { + if (std::is_same_v) + { + // QStringLists aren't wrapped by PyQt - regular Python string/unicode lists are used instead + bpy::extract> extractor(objPtr); + if (extractor.check()) + return new QStringList(extractor()); + } PyErr_SetString(PyExc_TypeError, "type not wrapped"); bpy::throw_error_already_set(); } @@ -964,6 +971,7 @@ BOOST_PYTHON_MODULE(mobase) QList_from_python_obj(); bpy::to_python_converter, QList_to_python_list >(); + QList_from_python_obj(); stdset_from_python_list(); } From 52417d64e4c44c74752d4cec612b3df67b63a9cd Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 22:11:09 +0100 Subject: [PATCH 04/27] Implement missing handling for QFileInfo --- src/runner/pythonrunner.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 175886c..5cd2501 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -424,6 +424,7 @@ template <> struct MetaData { static const char *className() { template <> struct MetaData { static const char *className() { return "QObject"; } }; template <> struct MetaData { static const char *className() { return "QWidget"; } }; template <> struct MetaData { static const char *className() { return "QDir"; } }; +template <> struct MetaData { static const char *className() { return "QFileInfo"; } }; template <> struct MetaData { static const char *className() { return "QIcon"; } }; template <> struct MetaData { static const char *className() { return "QStringList"; } }; template <> struct MetaData { static const char *className() { return "QVariant"; } }; @@ -695,6 +696,7 @@ BOOST_PYTHON_MODULE(mobase) //QClass_converters(); QClass_converters(); + QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); @@ -972,6 +974,8 @@ BOOST_PYTHON_MODULE(mobase) bpy::to_python_converter, QList_to_python_list >(); QList_from_python_obj(); + bpy::to_python_converter, + QList_to_python_list>(); stdset_from_python_list(); } From 2df983e22cd8ab223227d2fb5dda07edb0522ee9 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 22:20:50 +0100 Subject: [PATCH 05/27] Create ExecutableInfo interface --- src/runner/pythonrunner.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 5cd2501..c4ec847 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -746,6 +746,20 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_("PluginSetting", bpy::init()); + bpy::class_("ExecutableInfo", bpy::init()) + .def("withArgument", &ExecutableInfo::withArgument, bpy::return_value_policy()) + .def("withWorkingDirectory", &ExecutableInfo::withWorkingDirectory, bpy::return_value_policy()) + .def("withSteamAppId", &ExecutableInfo::withSteamAppId, bpy::return_value_policy()) + .def("asCustom", &ExecutableInfo::asCustom, bpy::return_value_policy()) + .def("isValid", &ExecutableInfo::isValid) + .def("title", &ExecutableInfo::title) + .def("binary", &ExecutableInfo::binary) + .def("arguments", &ExecutableInfo::arguments) + .def("workingDirectory", &ExecutableInfo::workingDirectory) + .def("steamAppID", &ExecutableInfo::steamAppID) + .def("isCustom", &ExecutableInfo::isCustom) + ; + bpy::class_("IOrganizer") .def("createNexusBridge", bpy::pure_virtual(&IOrganizer::createNexusBridge), bpy::return_value_policy()) From 9722d5b09351c62e89df86a87723a9dcbec82a3f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 22:28:26 +0100 Subject: [PATCH 06/27] Move IPluginGameWrapper to proxypluginwrappers.h as it's a wrapper for a plugin. --- src/runner/proxypluginwrappers.h | 51 ++++++++++++++++++++++++++++++++ src/runner/uibasewrappers.h | 49 ------------------------------ 2 files changed, 51 insertions(+), 49 deletions(-) diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 6d724d1..4f104fe 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -3,6 +3,7 @@ #include +#include #include #include @@ -26,6 +27,56 @@ public: }; +struct IPluginGameWrapper : MOBase::IPluginGame, boost::python::wrapper { + virtual QString gameName() const override { return this->get_override("gameName")(); } + virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override { + this->get_override("initializeProfile")(directory, settings); + } + virtual QString savegameExtension() const override { return this->get_override("savegameExtension")(); } + virtual QString savegameSEExtension() const override { return this->get_override("savegameSEExtension")(); } + virtual bool isInstalled() const override { return this->get_override("isInstalled")(); } + virtual QIcon gameIcon() const override { return this->get_override("gameIcon")(); } + virtual QDir gameDirectory() const override { return this->get_override("gameDirectory")(); } + virtual QDir dataDirectory() const override { return this->get_override("dataDirectory")(); } + virtual void setGamePath(const QString &path) override { this->get_override("setGamePath")(path); } + virtual QDir documentsDirectory() const override { return this->get_override("documentsDirectory")(); } + virtual QDir savesDirectory() const override { return this->get_override("savesDirectory")(); } + virtual QList executables() const override { return this->get_override("executables")(); } + virtual QString steamAPPId() const override { return this->get_override("steamAPPId")(); } + virtual QStringList primaryPlugins() const override { return this->get_override("primaryPlugins")(); } + virtual QStringList gameVariants() const override { return this->get_override("gameVariants")(); } + virtual void setGameVariant(const QString &variant) override { this->get_override("setGameVariant")(variant); } + virtual QString binaryName() const override { return this->get_override("binaryName")(); } + virtual QString gameShortName() const override { return this->get_override("gameShortName")(); } + virtual QStringList validShortNames() const override { return this->get_override("validShortNames")(); } + virtual QString gameNexusName() const override { return this->get_override("gameNexusName")(); } + virtual QStringList iniFiles() const override { return this->get_override("iniFiles")(); } + virtual QStringList DLCPlugins() const override { return this->get_override("DLCPlugins")(); } + virtual QStringList CCPlugins() const override { return this->get_override("CCPlugins")(); } + virtual LoadOrderMechanism loadOrderMechanism() const override { return this->get_override("loadorderMechanism")(); } + virtual SortMechanism sortMechanism() const override { return this->get_override("sortMechanism")(); } + virtual int nexusModOrganizerID() const override { return this->get_override("nexusModOrganizerID")(); } + virtual int nexusGameID() const override { return this->get_override("nexusGameID")(); } + virtual bool looksValid(QDir const &dir) const override { return this->get_override("looksValid")(dir); } + virtual QString gameVersion() const override { return this->get_override("gameVersion")(); } + virtual QString getLauncherName() const override { return this->get_override("getLauncherName")(); } + + //Plugin interface. Could this bit be implemented just once? + virtual bool init(MOBase::IOrganizer *moInfo) override { return this->get_override("init")(moInfo); } + virtual QString name() const override { return this->get_override("name")(); } + virtual QString author() const override { return this->get_override("author")(); } + virtual QString description() const override { return this->get_override("description")(); } + virtual MOBase::VersionInfo version() const override { return this->get_override("version")(); } + virtual bool isActive() const override { return this->get_override("isActive")(); } + virtual QList settings() const override { return this->get_override("settings")(); } + +protected: + + virtual std::map featureList() const override { return this->get_override("featureList")(); } + +}; + + class IPluginInstallerCustomWrapper : public MOBase::IPluginInstallerCustom, public boost::python::wrapper { Q_OBJECT diff --git a/src/runner/uibasewrappers.h b/src/runner/uibasewrappers.h index 780a506..75db982 100644 --- a/src/runner/uibasewrappers.h +++ b/src/runner/uibasewrappers.h @@ -434,53 +434,4 @@ struct IModListWrapper: MOBase::IModList, boost::python::wrapper { - virtual QString gameName() const override { return this->get_override("gameName")(); } - virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override { - this->get_override("initializeProfile")(directory, settings); - } - virtual QString savegameExtension() const override { return this->get_override("savegameExtension")(); } - virtual QString savegameSEExtension() const override { return this->get_override("savegameSEExtension")(); } - virtual bool isInstalled() const override { return this->get_override("isInstalled")(); } - virtual QIcon gameIcon() const override { return this->get_override("gameIcon")(); } - virtual QDir gameDirectory() const override { return this->get_override("gameDirectory")(); } - virtual QDir dataDirectory() const override { return this->get_override("dataDirectory")(); } - virtual void setGamePath(const QString &path) override { this->get_override("setGamePath")(path); } - virtual QDir documentsDirectory() const override { return this->get_override("documentsDirectory")(); } - virtual QDir savesDirectory() const override { return this->get_override("savesDirectory")(); } - virtual QList executables() const override { return this->get_override("executables")(); } - virtual QString steamAPPId() const override { return this->get_override("steamAPPId")(); } - virtual QStringList primaryPlugins() const override { return this->get_override("primaryPlugins")(); } - virtual QStringList gameVariants() const override { return this->get_override("gameVariants")(); } - virtual void setGameVariant(const QString &variant) override { this->get_override("setGameVariant")(variant); } - virtual QString binaryName() const override { return this->get_override("binaryName")(); } - virtual QString gameShortName() const override { return this->get_override("gameShortName")(); } - virtual QStringList validShortNames() const override { return this->get_override("validShortNames")(); } - virtual QString gameNexusName() const override { return this->get_override("gameNexusName")(); } - virtual QStringList iniFiles() const override { return this->get_override("iniFiles")(); } - virtual QStringList DLCPlugins() const override { return this->get_override("DLCPlugins")(); } - virtual QStringList CCPlugins() const override { return this->get_override("CCPlugins")(); } - virtual LoadOrderMechanism loadOrderMechanism() const override { return this->get_override("loadorderMechanism")(); } - virtual SortMechanism sortMechanism() const override { return this->get_override("sortMechanism")(); } - virtual int nexusModOrganizerID() const override { return this->get_override("nexusModOrganizerID")(); } - virtual int nexusGameID() const override { return this->get_override("nexusGameID")(); } - virtual bool looksValid(QDir const &dir) const override { return this->get_override("looksValid")(dir); } - virtual QString gameVersion() const override { return this->get_override("gameVersion")(); } - virtual QString getLauncherName() const override { return this->get_override("getLauncherName")(); } - - //Plugin interface. Could this bit be implemented just once? - virtual bool init(MOBase::IOrganizer *moInfo) override { return this->get_override("init")(moInfo); } - virtual QString name() const override { return this->get_override("name")(); } - virtual QString author() const override { return this->get_override("author")(); } - virtual QString description() const override { return this->get_override("description")(); } - virtual MOBase::VersionInfo version() const override { return this->get_override("version")(); } - virtual bool isActive() const override { return this->get_override("isActive")(); } - virtual QList settings() const override { return this->get_override("settings")(); } - -protected: - - virtual std::map featureList() const override { return this->get_override("featureList")(); } - -}; - #endif // UIBASEWRAPPERS_H From ca51a53d4100e83f6f954d5623f0c29af3db7716 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 22:29:51 +0100 Subject: [PATCH 07/27] Make IPluginGameWrapper into a class and include the necessary Qt annotations --- src/runner/proxypluginwrappers.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 4f104fe..fc1e4ec 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -27,7 +27,11 @@ public: }; -struct IPluginGameWrapper : MOBase::IPluginGame, boost::python::wrapper { +class IPluginGameWrapper : public MOBase::IPluginGame, public boost::python::wrapper { + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) + +public: virtual QString gameName() const override { return this->get_override("gameName")(); } virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override { this->get_override("initializeProfile")(directory, settings); From c47b94d5349d15e91773c3f054df5c3272c76bd7 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 22:31:53 +0100 Subject: [PATCH 08/27] Restore indentation which rearraging things broke --- src/runner/proxypluginwrappers.cpp | 2 +- src/runner/proxypluginwrappers.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 5a6693f..9a08b08 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -169,7 +169,7 @@ std::set IPluginInstallerCustomWrapper::supportedExtensions() const IPluginInstaller::EInstallResult IPluginInstallerCustomWrapper::install(GuessedValue &modName, const QString &archiveName, - const QString &version, int modID) + const QString &version, int modID) { try { return this->get_override("install")(modName, archiveName, version, modID); diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index fc1e4ec..ebb82fc 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -84,7 +84,7 @@ protected: class IPluginInstallerCustomWrapper : public MOBase::IPluginInstallerCustom, public boost::python::wrapper { Q_OBJECT - Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) public: virtual bool init(MOBase::IOrganizer *moInfo); @@ -101,7 +101,7 @@ public: virtual bool isArchiveSupported(const QString &archiveName) const; virtual std::set supportedExtensions() const; virtual EInstallResult install(MOBase::GuessedValue &modName, const QString &archiveName, - const QString &version, int modID); + const QString &version, int modID); virtual void setParentWidget(QWidget *parent); }; From 317f13177ef5a33a3494f13783110843d556ba08 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 23:03:08 +0100 Subject: [PATCH 09/27] Move IPluginGameWrapper's mentod implementations into the cpp file --- src/runner/proxypluginwrappers.cpp | 194 +++++++++++++++++++++++++++++ src/runner/proxypluginwrappers.h | 80 ++++++------ 2 files changed, 234 insertions(+), 40 deletions(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 9a08b08..4ec0b79 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -79,6 +79,200 @@ QList IPluginWrapper::settings() const } /// end IPlugin Wrapper ///////////////////////////////////// +/// IPluginGame Wrapper + + +QString IPluginGameWrapper::gameName() const +{ + return this->get_override("gameName")(); +} + +void IPluginGameWrapper::initializeProfile(const QDir & directory, ProfileSettings settings) const +{ + this->get_override("initializeProfile")(directory, settings); +} + +QString IPluginGameWrapper::savegameExtension() const +{ + return this->get_override("savegameExtension")(); +} + +QString IPluginGameWrapper::savegameSEExtension() const +{ + return this->get_override("savegameSEExtension")(); +} + +bool IPluginGameWrapper::isInstalled() const +{ + return this->get_override("isInstalled")(); +} + +QIcon IPluginGameWrapper::gameIcon() const +{ + return this->get_override("gameIcon")(); +} + +QDir IPluginGameWrapper::gameDirectory() const +{ + return this->get_override("gameDirectory")(); +} + +QDir IPluginGameWrapper::dataDirectory() const +{ + return this->get_override("dataDirectory")(); +} + +void IPluginGameWrapper::setGamePath(const QString & path) +{ + this->get_override("setGamePath")(path); +} + +QDir IPluginGameWrapper::documentsDirectory() const +{ + return this->get_override("documentsDirectory")(); +} + +QDir IPluginGameWrapper::savesDirectory() const +{ + return this->get_override("savesDirectory")(); +} + +QList IPluginGameWrapper::executables() const +{ + return this->get_override("executables")(); +} + +QString IPluginGameWrapper::steamAPPId() const +{ + return this->get_override("steamAPPId")(); +} + +QStringList IPluginGameWrapper::primaryPlugins() const +{ + return this->get_override("primaryPlugins")(); +} + +QStringList IPluginGameWrapper::gameVariants() const +{ + return this->get_override("gameVariants")(); +} + +void IPluginGameWrapper::setGameVariant(const QString & variant) +{ + this->get_override("setGameVariant")(variant); +} + +QString IPluginGameWrapper::binaryName() const +{ + return this->get_override("binaryName")(); +} + +QString IPluginGameWrapper::gameShortName() const +{ + return this->get_override("gameShortName")(); +} + +QStringList IPluginGameWrapper::validShortNames() const +{ + return this->get_override("validShortNames")(); +} + +QString IPluginGameWrapper::gameNexusName() const +{ + return this->get_override("gameNexusName")(); +} + +QStringList IPluginGameWrapper::iniFiles() const +{ + return this->get_override("iniFiles")(); +} + +QStringList IPluginGameWrapper::DLCPlugins() const +{ + return this->get_override("DLCPlugins")(); +} + +QStringList IPluginGameWrapper::CCPlugins() const +{ + return this->get_override("CCPlugins")(); +} + +IPluginGame::LoadOrderMechanism IPluginGameWrapper::loadOrderMechanism() const +{ + return this->get_override("loadorderMechanism")(); +} + +IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const +{ + return this->get_override("sortMechanism")(); +} + +int IPluginGameWrapper::nexusModOrganizerID() const +{ + return this->get_override("nexusModOrganizerID")(); +} + +int IPluginGameWrapper::nexusGameID() const +{ + return this->get_override("nexusGameID")(); +} + +bool IPluginGameWrapper::looksValid(QDir const & dir) const +{ + return this->get_override("looksValid")(dir); +} + +QString IPluginGameWrapper::gameVersion() const +{ + return this->get_override("gameVersion")(); +} + +QString IPluginGameWrapper::getLauncherName() const +{ + return this->get_override("getLauncherName")(); +} + +bool IPluginGameWrapper::init(MOBase::IOrganizer * moInfo) +{ + return this->get_override("init")(moInfo); +} + +QString IPluginGameWrapper::name() const +{ + return this->get_override("name")(); +} + +QString IPluginGameWrapper::author() const +{ + return this->get_override("author")(); +} + +QString IPluginGameWrapper::description() const +{ + return this->get_override("description")(); +} + +MOBase::VersionInfo IPluginGameWrapper::version() const +{ + return this->get_override("version")(); +} + +bool IPluginGameWrapper::isActive() const +{ + return this->get_override("isActive")(); +} + +QList IPluginGameWrapper::settings() const +{ + return this->get_override("settings")(); +} + +std::map IPluginGameWrapper::featureList() const +{ + return this->get_override("_featureList")(); +} +/// end IPluginGame Wrapper +///////////////////////////////////// /// IPluginInstallerCustom Wrapper diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index ebb82fc..01677e3 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -32,51 +32,51 @@ class IPluginGameWrapper : public MOBase::IPluginGame, public boost::python::wra Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) public: - virtual QString gameName() const override { return this->get_override("gameName")(); } - virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override { - this->get_override("initializeProfile")(directory, settings); - } - virtual QString savegameExtension() const override { return this->get_override("savegameExtension")(); } - virtual QString savegameSEExtension() const override { return this->get_override("savegameSEExtension")(); } - virtual bool isInstalled() const override { return this->get_override("isInstalled")(); } - virtual QIcon gameIcon() const override { return this->get_override("gameIcon")(); } - virtual QDir gameDirectory() const override { return this->get_override("gameDirectory")(); } - virtual QDir dataDirectory() const override { return this->get_override("dataDirectory")(); } - virtual void setGamePath(const QString &path) override { this->get_override("setGamePath")(path); } - virtual QDir documentsDirectory() const override { return this->get_override("documentsDirectory")(); } - virtual QDir savesDirectory() const override { return this->get_override("savesDirectory")(); } - virtual QList executables() const override { return this->get_override("executables")(); } - virtual QString steamAPPId() const override { return this->get_override("steamAPPId")(); } - virtual QStringList primaryPlugins() const override { return this->get_override("primaryPlugins")(); } - virtual QStringList gameVariants() const override { return this->get_override("gameVariants")(); } - virtual void setGameVariant(const QString &variant) override { this->get_override("setGameVariant")(variant); } - virtual QString binaryName() const override { return this->get_override("binaryName")(); } - virtual QString gameShortName() const override { return this->get_override("gameShortName")(); } - virtual QStringList validShortNames() const override { return this->get_override("validShortNames")(); } - virtual QString gameNexusName() const override { return this->get_override("gameNexusName")(); } - virtual QStringList iniFiles() const override { return this->get_override("iniFiles")(); } - virtual QStringList DLCPlugins() const override { return this->get_override("DLCPlugins")(); } - virtual QStringList CCPlugins() const override { return this->get_override("CCPlugins")(); } - virtual LoadOrderMechanism loadOrderMechanism() const override { return this->get_override("loadorderMechanism")(); } - virtual SortMechanism sortMechanism() const override { return this->get_override("sortMechanism")(); } - virtual int nexusModOrganizerID() const override { return this->get_override("nexusModOrganizerID")(); } - virtual int nexusGameID() const override { return this->get_override("nexusGameID")(); } - virtual bool looksValid(QDir const &dir) const override { return this->get_override("looksValid")(dir); } - virtual QString gameVersion() const override { return this->get_override("gameVersion")(); } - virtual QString getLauncherName() const override { return this->get_override("getLauncherName")(); } + virtual QString gameName() const override; + virtual void initializeProfile(const QDir &directory, ProfileSettings settings) const override; + virtual QString savegameExtension() const override; + virtual QString savegameSEExtension() const override; + virtual bool isInstalled() const override; + virtual QIcon gameIcon() const override; + virtual QDir gameDirectory() const override; + virtual QDir dataDirectory() const override; + virtual void setGamePath(const QString &path) override; + virtual QDir documentsDirectory() const override; + virtual QDir savesDirectory() const override; + virtual QList executables() const override; + virtual QString steamAPPId() const override; + virtual QStringList primaryPlugins() const override; + virtual QStringList gameVariants() const override; + virtual void setGameVariant(const QString &variant) override; + virtual QString binaryName() const override; + virtual QString gameShortName() const override; + virtual QStringList validShortNames() const override; + virtual QString gameNexusName() const override; + virtual QStringList iniFiles() const override; + virtual QStringList DLCPlugins() const override; + virtual QStringList CCPlugins() const override; + virtual LoadOrderMechanism loadOrderMechanism() const override; + virtual SortMechanism sortMechanism() const override; + virtual int nexusModOrganizerID() const override; + virtual int nexusGameID() const override; + virtual bool looksValid(QDir const &dir) const override; + virtual QString gameVersion() const override; + virtual QString getLauncherName() const override; //Plugin interface. Could this bit be implemented just once? - virtual bool init(MOBase::IOrganizer *moInfo) override { return this->get_override("init")(moInfo); } - virtual QString name() const override { return this->get_override("name")(); } - virtual QString author() const override { return this->get_override("author")(); } - virtual QString description() const override { return this->get_override("description")(); } - virtual MOBase::VersionInfo version() const override { return this->get_override("version")(); } - virtual bool isActive() const override { return this->get_override("isActive")(); } - virtual QList settings() const override { return this->get_override("settings")(); } + virtual bool init(MOBase::IOrganizer *moInfo) override; + virtual QString name() const override; + virtual QString author() const override; + virtual QString description() const override; + virtual MOBase::VersionInfo version() const override; + virtual bool isActive() const override; + virtual QList settings() const override; protected: - virtual std::map featureList() const override { return this->get_override("featureList")(); } + // TODO: implementing converters for this is required as otherwise Mod Organizer will crash on load when the game is being managed by this plugin + // Apparently, Python developers interpret an underscore in a function name as it being protected + virtual std::map featureList() const override; }; From 01168483c506af201040f28caf54d2b6b5b5cae5 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Tue, 17 Apr 2018 23:08:16 +0100 Subject: [PATCH 10/27] handle errors in Python game plugins --- src/runner/proxypluginwrappers.cpp | 148 +++++++++++++++++++++-------- 1 file changed, 111 insertions(+), 37 deletions(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 4ec0b79..a4594ed 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -84,122 +84,170 @@ QList IPluginWrapper::settings() const QString IPluginGameWrapper::gameName() const { - return this->get_override("gameName")(); + try { + return this->get_override("gameName")(); + } PYCATCH; } void IPluginGameWrapper::initializeProfile(const QDir & directory, ProfileSettings settings) const { - this->get_override("initializeProfile")(directory, settings); + try { + this->get_override("initializeProfile")(directory, settings); + } PYCATCH; } QString IPluginGameWrapper::savegameExtension() const { - return this->get_override("savegameExtension")(); + try { + return this->get_override("savegameExtension")(); + } PYCATCH; } QString IPluginGameWrapper::savegameSEExtension() const { - return this->get_override("savegameSEExtension")(); + try { + return this->get_override("savegameSEExtension")(); + } PYCATCH; } bool IPluginGameWrapper::isInstalled() const { - return this->get_override("isInstalled")(); + try { + return this->get_override("isInstalled")(); + } PYCATCH; } QIcon IPluginGameWrapper::gameIcon() const { - return this->get_override("gameIcon")(); + try { + return this->get_override("gameIcon")(); + } PYCATCH; } QDir IPluginGameWrapper::gameDirectory() const { - return this->get_override("gameDirectory")(); + try { + return this->get_override("gameDirectory")(); + } PYCATCH; } QDir IPluginGameWrapper::dataDirectory() const { - return this->get_override("dataDirectory")(); + try { + return this->get_override("dataDirectory")(); + } PYCATCH; } void IPluginGameWrapper::setGamePath(const QString & path) { - this->get_override("setGamePath")(path); + try { + this->get_override("setGamePath")(path); + } PYCATCH; } QDir IPluginGameWrapper::documentsDirectory() const { - return this->get_override("documentsDirectory")(); + try { + return this->get_override("documentsDirectory")(); + } PYCATCH; } QDir IPluginGameWrapper::savesDirectory() const { - return this->get_override("savesDirectory")(); + try { + return this->get_override("savesDirectory")(); + } PYCATCH; } QList IPluginGameWrapper::executables() const { - return this->get_override("executables")(); + try { + return this->get_override("executables")(); + } PYCATCH; } QString IPluginGameWrapper::steamAPPId() const { - return this->get_override("steamAPPId")(); + try { + return this->get_override("steamAPPId")(); + } PYCATCH; } QStringList IPluginGameWrapper::primaryPlugins() const { - return this->get_override("primaryPlugins")(); + try { + return this->get_override("primaryPlugins")(); + } PYCATCH; } QStringList IPluginGameWrapper::gameVariants() const { - return this->get_override("gameVariants")(); + try { + return this->get_override("gameVariants")(); + } PYCATCH; } void IPluginGameWrapper::setGameVariant(const QString & variant) { - this->get_override("setGameVariant")(variant); + try { + this->get_override("setGameVariant")(variant); + } PYCATCH; } QString IPluginGameWrapper::binaryName() const { - return this->get_override("binaryName")(); + try { + return this->get_override("binaryName")(); + } PYCATCH; } QString IPluginGameWrapper::gameShortName() const { - return this->get_override("gameShortName")(); + try { + return this->get_override("gameShortName")(); + } PYCATCH; } QStringList IPluginGameWrapper::validShortNames() const { - return this->get_override("validShortNames")(); + try { + return this->get_override("validShortNames")(); + } PYCATCH; } QString IPluginGameWrapper::gameNexusName() const { - return this->get_override("gameNexusName")(); + try { + return this->get_override("gameNexusName")(); + } PYCATCH; } QStringList IPluginGameWrapper::iniFiles() const { - return this->get_override("iniFiles")(); + try { + return this->get_override("iniFiles")(); + } PYCATCH; } QStringList IPluginGameWrapper::DLCPlugins() const { - return this->get_override("DLCPlugins")(); + try { + return this->get_override("DLCPlugins")(); + } PYCATCH; } QStringList IPluginGameWrapper::CCPlugins() const { - return this->get_override("CCPlugins")(); + try { + return this->get_override("CCPlugins")(); + } PYCATCH; } IPluginGame::LoadOrderMechanism IPluginGameWrapper::loadOrderMechanism() const { - return this->get_override("loadorderMechanism")(); + try { + return this->get_override("loadorderMechanism")(); + } PYCATCH; } IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const @@ -209,67 +257,93 @@ IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const int IPluginGameWrapper::nexusModOrganizerID() const { - return this->get_override("nexusModOrganizerID")(); + try { + return this->get_override("nexusModOrganizerID")(); + } PYCATCH; } int IPluginGameWrapper::nexusGameID() const { - return this->get_override("nexusGameID")(); + try { + return this->get_override("nexusGameID")(); + } PYCATCH; } bool IPluginGameWrapper::looksValid(QDir const & dir) const { - return this->get_override("looksValid")(dir); + try { + return this->get_override("looksValid")(dir); + } PYCATCH; } QString IPluginGameWrapper::gameVersion() const { - return this->get_override("gameVersion")(); + try { + return this->get_override("gameVersion")(); + } PYCATCH; } QString IPluginGameWrapper::getLauncherName() const { - return this->get_override("getLauncherName")(); + try { + return this->get_override("getLauncherName")(); + } PYCATCH; } bool IPluginGameWrapper::init(MOBase::IOrganizer * moInfo) { - return this->get_override("init")(moInfo); + try { + return this->get_override("init")(moInfo); + } PYCATCH; } QString IPluginGameWrapper::name() const { - return this->get_override("name")(); + try { + return this->get_override("name")(); + } PYCATCH; } QString IPluginGameWrapper::author() const { - return this->get_override("author")(); + try { + return this->get_override("author")(); + } PYCATCH; } QString IPluginGameWrapper::description() const { - return this->get_override("description")(); + try { + return this->get_override("description")(); + } PYCATCH; } MOBase::VersionInfo IPluginGameWrapper::version() const { - return this->get_override("version")(); + try { + return this->get_override("version")(); + } PYCATCH; } bool IPluginGameWrapper::isActive() const { - return this->get_override("isActive")(); + try { + return this->get_override("isActive")(); + } PYCATCH; } QList IPluginGameWrapper::settings() const { - return this->get_override("settings")(); + try { + return this->get_override("settings")(); + } PYCATCH; } std::map IPluginGameWrapper::featureList() const { - return this->get_override("_featureList")(); + try { + return this->get_override("_featureList")(); + } PYCATCH; } /// end IPluginGame Wrapper ///////////////////////////////////// From de809c6d57b6010b6f4e63cc320b93542a0fc01a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 18 Apr 2018 00:06:12 +0100 Subject: [PATCH 11/27] Fix simple errors with IPluginGame proxy and add warning when the unimplemented part is encountered --- src/runner/proxypluginwrappers.cpp | 3 ++- src/runner/pythonrunner.cpp | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index a4594ed..63748dc 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -293,7 +293,7 @@ QString IPluginGameWrapper::getLauncherName() const bool IPluginGameWrapper::init(MOBase::IOrganizer * moInfo) { try { - return this->get_override("init")(moInfo); + return this->get_override("init")(boost::python::ptr(moInfo)); } PYCATCH; } @@ -341,6 +341,7 @@ QList IPluginGameWrapper::settings() const std::map IPluginGameWrapper::featureList() const { + qCritical("Calling unproxied method IPluginGameWrapper::featureList()"); try { return this->get_override("_featureList")(); } PYCATCH; diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index c4ec847..777ac77 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -984,6 +984,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::to_python_converter(); + QList_from_python_obj(); QList_from_python_obj(); bpy::to_python_converter, QList_to_python_list >(); From e8af8819df6c43ac0ef362c587aea234d68d385d Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 18 Apr 2018 12:31:16 +0100 Subject: [PATCH 12/27] Add PYCATCH missed when rebasing --- src/runner/proxypluginwrappers.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 63748dc..4e4853a 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -252,7 +252,9 @@ IPluginGame::LoadOrderMechanism IPluginGameWrapper::loadOrderMechanism() const IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const { - return this->get_override("sortMechanism")(); + try { + return this->get_override("sortMechanism")(); + } PYCATCH; } int IPluginGameWrapper::nexusModOrganizerID() const From c70f78adfef13c38bb69b00f5b07ae07b9d2d4bf Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 19 Apr 2018 00:42:24 +0100 Subject: [PATCH 13/27] Reduce code duplication by moving common implementation to macros --- src/runner/proxypluginwrappers.cpp | 247 +++++++---------------------- src/runner/proxypluginwrappers.h | 46 ++---- 2 files changed, 69 insertions(+), 224 deletions(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 4e4853a..6528458 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -25,58 +25,62 @@ using namespace MOBase; catch (...) { throw MyException("An unknown exception was thrown in python code"); } +#define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) \ +bool class_name::init(MOBase::IOrganizer *moInfo) \ +{ \ + try { \ + return this->get_override("init")(boost::python::ptr(moInfo)); \ + } PYCATCH; \ +} \ + \ +QString class_name::name() const \ +{ \ + try { \ + return this->get_override("name")().as(); \ + } PYCATCH; \ +} \ + \ +QString class_name::author() const \ +{ \ + try { \ + return this->get_override("author")().as(); \ + } PYCATCH; \ +} \ + \ +QString class_name::description() const \ +{ \ + try { \ + return this->get_override("description")().as(); \ + } PYCATCH; \ +} \ + \ +MOBase::VersionInfo class_name::version() const \ +{ \ + try { \ + return this->get_override("version")().as(); \ + } PYCATCH; \ +} \ + \ +bool class_name::isActive() const \ +{ \ + try { \ + return this->get_override("isActive")().as(); \ + } PYCATCH; \ +} \ + \ +QList class_name::settings() const \ +{ \ + try { \ + return this->get_override("settings")().as>(); \ + } PYCATCH; \ +} + +/// end COMMON_I_PLUGIN_WRAPPER_DEFINITIONS ///////////////////////////// /// IPlugin Wrapper -bool IPluginWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return this->get_override("init")(boost::python::ptr(moInfo)); - } PYCATCH; -} - -QString IPluginWrapper::name() const -{ - try { - return this->get_override("name")().as(); - } PYCATCH; -} - -QString IPluginWrapper::author() const -{ - try { - return this->get_override("author")().as(); - } PYCATCH; -} - -QString IPluginWrapper::description() const -{ - try { - return this->get_override("description")().as(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginWrapper::version() const -{ - try { - return this->get_override("version")().as(); - } PYCATCH; -} - -bool IPluginWrapper::isActive() const -{ - try { - return this->get_override("isActive")().as(); - } PYCATCH; -} - -QList IPluginWrapper::settings() const -{ - try { - return this->get_override("settings")().as>(); - } PYCATCH; -} +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginWrapper) /// end IPlugin Wrapper ///////////////////////////////////// /// IPluginGame Wrapper @@ -292,54 +296,7 @@ QString IPluginGameWrapper::getLauncherName() const } PYCATCH; } -bool IPluginGameWrapper::init(MOBase::IOrganizer * moInfo) -{ - try { - return this->get_override("init")(boost::python::ptr(moInfo)); - } PYCATCH; -} - -QString IPluginGameWrapper::name() const -{ - try { - return this->get_override("name")(); - } PYCATCH; -} - -QString IPluginGameWrapper::author() const -{ - try { - return this->get_override("author")(); - } PYCATCH; -} - -QString IPluginGameWrapper::description() const -{ - try { - return this->get_override("description")(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginGameWrapper::version() const -{ - try { - return this->get_override("version")(); - } PYCATCH; -} - -bool IPluginGameWrapper::isActive() const -{ - try { - return this->get_override("isActive")(); - } PYCATCH; -} - -QList IPluginGameWrapper::settings() const -{ - try { - return this->get_override("settings")(); - } PYCATCH; -} +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginGameWrapper) std::map IPluginGameWrapper::featureList() const { @@ -353,54 +310,7 @@ std::map IPluginGameWrapper::featureList() const /// IPluginInstallerCustom Wrapper -bool IPluginInstallerCustomWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return this->get_override("init")(boost::python::ptr(moInfo)); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::name() const -{ - try { - return this->get_override("name")().as(); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::author() const -{ - try { - return this->get_override("author")().as(); - } PYCATCH; -} - -QString IPluginInstallerCustomWrapper::description() const -{ - try { - return this->get_override("description")().as(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginInstallerCustomWrapper::version() const -{ - try { - return this->get_override("version")(); - } PYCATCH; -} - -bool IPluginInstallerCustomWrapper::isActive() const -{ - try { - return this->get_override("isActive")(); - } PYCATCH; -} - -QList IPluginInstallerCustomWrapper::settings() const -{ - try { - return this->get_override("settings")().as>(); - } PYCATCH; -} +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginInstallerCustomWrapper) unsigned int IPluginInstallerCustomWrapper::priority() const { @@ -459,54 +369,7 @@ void IPluginInstallerCustomWrapper::setParentWidget(QWidget *parent) /// IPluginTool Wrapper -bool IPluginToolWrapper::init(MOBase::IOrganizer *moInfo) -{ - try { - return this->get_override("init")(boost::python::ptr(moInfo)); - } PYCATCH; -} - -QString IPluginToolWrapper::name() const -{ - try { - return this->get_override("name")().as(); - } PYCATCH; -} - -QString IPluginToolWrapper::author() const -{ - try { - return this->get_override("author")().as(); - } PYCATCH; -} - -QString IPluginToolWrapper::description() const -{ - try { - return this->get_override("description")().as(); - } PYCATCH; -} - -MOBase::VersionInfo IPluginToolWrapper::version() const -{ - try { - return this->get_override("version")().as(); - } PYCATCH; -} - -bool IPluginToolWrapper::isActive() const -{ - try { - return this->get_override("isActive")().as(); - } PYCATCH; -} - -QList IPluginToolWrapper::settings() const -{ - try { - return this->get_override("settings")().as>(); - } PYCATCH; -} +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginToolWrapper) QString IPluginToolWrapper::displayName() const { diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 01677e3..1060c12 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -12,18 +12,21 @@ #endif +#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: \ +virtual bool init(MOBase::IOrganizer *moInfo) override; \ +virtual QString name() const override; \ +virtual QString author() const override; \ +virtual QString description() const override; \ +virtual MOBase::VersionInfo version() const override; \ +virtual bool isActive() const override; \ +virtual QList settings() const override; + + class IPluginWrapper : public MOBase::IPlugin, public boost::python::wrapper { Q_INTERFACES(MOBase::IPlugin) -public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS }; @@ -63,14 +66,7 @@ public: virtual QString gameVersion() const override; virtual QString getLauncherName() const override; - //Plugin interface. Could this bit be implemented just once? - virtual bool init(MOBase::IOrganizer *moInfo) override; - virtual QString name() const override; - virtual QString author() const override; - virtual QString description() const override; - virtual MOBase::VersionInfo version() const override; - virtual bool isActive() const override; - virtual QList settings() const override; + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS protected: @@ -86,15 +82,8 @@ class IPluginInstallerCustomWrapper : public MOBase::IPluginInstallerCustom, pub Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom) + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; - virtual unsigned int priority() const; virtual bool isManualInstaller() const; virtual bool isArchiveSupported(const MOBase::DirectoryTree &tree) const; @@ -112,15 +101,8 @@ class IPluginToolWrapper: public MOBase::IPluginTool, public boost::python::wrap Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool) + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS public: - virtual bool init(MOBase::IOrganizer *moInfo); - virtual QString name() const; - virtual QString author() const; - virtual QString description() const; - virtual MOBase::VersionInfo version() const; - virtual bool isActive() const; - virtual QList settings() const; - virtual QString displayName() const; virtual QString tooltip() const; virtual QIcon icon() const; From 5a730941dbddee8d97a6c3e96c5613a6353bc15f Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 19 Apr 2018 00:50:28 +0100 Subject: [PATCH 14/27] Implement wrapper and bindings for IPluginDiagnose --- src/runner/proxypluginwrappers.cpp | 50 ++++++++++++++++++++++++++++++ src/runner/proxypluginwrappers.h | 24 +++++++++++++- src/runner/pythonrunner.cpp | 42 +++++++++++++++++++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 6528458..a54d11e 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -83,6 +83,56 @@ QList class_name::settings() const \ COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginWrapper) /// end IPlugin Wrapper ///////////////////////////////////// +/// IPluginDiagnose Wrapper + + +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginDiagnoseWrapper) + +std::vector IPluginDiagnoseWrapper::activeProblems() const +{ + try { + GILock lock; + + return this->get_override("activeProblems")(); + } PYCATCH; +} + +QString IPluginDiagnoseWrapper::shortDescription(unsigned int key) const +{ + try { + return this->get_override("shortDescription")(key); + } PYCATCH; +} + +QString IPluginDiagnoseWrapper::fullDescription(unsigned int key) const +{ + try { + return this->get_override("fullDescription")(key); + } PYCATCH; +} + +bool IPluginDiagnoseWrapper::hasGuidedFix(unsigned int key) const +{ + try { + return this->get_override("hasGuidedFix")(key); + } PYCATCH; +} + +void IPluginDiagnoseWrapper::startGuidedFix(unsigned int key) const +{ + try { + GILock lock; + + this->get_override("startGuidedFix")(key); + } PYCATCH; +} + +void IPluginDiagnoseWrapper::invalidate() +{ + IPluginDiagnose::invalidate(); +} +/// end IPluginDiagnose Wrapper +///////////////////////////////////// /// IPluginGame Wrapper diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 1060c12..f76e7a1 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -2,10 +2,11 @@ #define PROXYPLUGINWRAPPERS_H -#include +#include #include #include #include +#include #ifndef Q_MOC_RUN #include @@ -30,6 +31,27 @@ class IPluginWrapper : public MOBase::IPlugin, public boost::python::wrapper +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose) + +public: + virtual std::vector activeProblems() const override; + virtual QString shortDescription(unsigned int key) const override; + virtual QString fullDescription(unsigned int key) const override; + virtual bool hasGuidedFix(unsigned int key) const override; + virtual void startGuidedFix(unsigned int key) const override; + // Other functions exist, but shouldn't need wrapping as a default implementation exists + // This was protected, but Python doesn't have that, so it needs making public + virtual void invalidate(); + + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS +}; + + class IPluginGameWrapper : public MOBase::IPluginGame, public boost::python::wrapper { Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 777ac77..5c2e12c 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -339,6 +339,35 @@ struct QList_from_python_obj }; +template +struct std_vector_from_python_obj +{ + std_vector_from_python_obj() { + bpy::converter::registry::push_back( + &convertible, + &construct, + bpy::type_id >()); + } + + static void* convertible(PyObject *objPtr) { + if (PyList_Check(objPtr)) return objPtr; + return nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + void *storage = ((bpy::converter::rvalue_from_python_storage >*)data)->storage.bytes; + std::vector *result = new (storage) std::vector(); + bpy::list source(bpy::handle<>(bpy::borrowed(objPtr))); + int length = bpy::len(source); + for (int i = 0; i < length; ++i) { + result->push_back(bpy::extract(source[i])); + } + + data->convertible = storage; + } +}; + + template struct QFlags_from_python_obj { @@ -898,6 +927,15 @@ BOOST_PYTHON_MODULE(mobase) bpy::class_("IPlugin"); + bpy::class_("IPluginDiagnose") + .def("activeProblems", bpy::pure_virtual(&MOBase::IPluginDiagnose::activeProblems)) + .def("shortDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::shortDescription)) + .def("fullDescription", bpy::pure_virtual(&MOBase::IPluginDiagnose::fullDescription)) + .def("hasGuidedFix", bpy::pure_virtual(&MOBase::IPluginDiagnose::hasGuidedFix)) + .def("startGuidedFix", bpy::pure_virtual(&MOBase::IPluginDiagnose::startGuidedFix)) + .def("_invalidate", &IPluginDiagnoseWrapper::invalidate) + ; + bpy::enum_("LoadOrderMechanism") .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime) .value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) @@ -992,6 +1030,8 @@ BOOST_PYTHON_MODULE(mobase) bpy::to_python_converter, QList_to_python_list>(); + std_vector_from_python_obj(); + stdset_from_python_list(); } @@ -1107,6 +1147,8 @@ QObject *PythonRunner::instantiate(const QString &pluginName) bpy::object pluginObj = m_PythonObjects[pluginName]; TRY_PLUGIN_TYPE(IPluginGame, pluginObj); + // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject + TRY_PLUGIN_TYPE(IPluginDiagnoseWrapper, pluginObj); TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); TRY_PLUGIN_TYPE(IPluginTool, pluginObj); } catch (const bpy::error_already_set&) { From 86d859d4300aeb3e0a917a1e7266ca5665f89e09 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 19 Apr 2018 01:13:40 +0100 Subject: [PATCH 15/27] Allow anonymous Python functions to return values --- src/runner/pythonrunner.cpp | 68 ++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 5c2e12c..1de2878 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -630,6 +630,7 @@ int getArgCount(PyObject *object) { return result; } +template struct Functor0_converter { @@ -638,9 +639,9 @@ struct Functor0_converter FunctorWrapper(boost::python::object callable) : m_Callable(callable) { } - void operator()() { + RET operator()() { GILock lock; - m_Callable(); + return (RET) m_Callable(); } boost::python::object m_Callable; @@ -648,7 +649,7 @@ struct Functor0_converter Functor0_converter() { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); } static void *convertible(PyObject *object) @@ -663,14 +664,55 @@ struct Functor0_converter static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) { bpy::object callable(bpy::handle<>(bpy::borrowed(object))); - void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; - new (storage) std::function(FunctorWrapper(callable)); + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); data->convertible = storage; } }; -template +template +struct Functor1_converter +{ + + struct FunctorWrapper + { + FunctorWrapper(boost::python::object callable) : m_Callable(callable) { + } + + RET operator()(const PAR1 ¶m1) { + GILock lock; + return (RET) m_Callable(param1); + } + + boost::python::object m_Callable; + }; + + Functor1_converter() + { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + } + + static void *convertible(PyObject *object) + { + if (!PyCallable_Check(object) + || (getArgCount(object) != 1)) { + return nullptr; + } + return object; + } + + static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) + { + bpy::object callable(bpy::handle<>(bpy::borrowed(object))); + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); + data->convertible = storage; + } +}; + + +template struct Functor2_converter { @@ -679,9 +721,9 @@ struct Functor2_converter FunctorWrapper(boost::python::object callable) : m_Callable(callable) { } - void operator()(const PAR1 ¶m1, const PAR2 ¶m2) { + RET operator()(const PAR1 ¶m1, const PAR2 ¶m2) { GILock lock; - m_Callable(param1, param2); + return (RET) m_Callable(param1, param2); } boost::python::object m_Callable; @@ -689,7 +731,7 @@ struct Functor2_converter Functor2_converter() { - bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); } static void *convertible(PyObject *object) @@ -704,8 +746,8 @@ struct Functor2_converter static void construct(PyObject *object, bpy::converter::rvalue_from_python_stage1_data *data) { bpy::object callable(bpy::handle<>(bpy::borrowed(object))); - void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; - new (storage) std::function(FunctorWrapper(callable)); + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + new (storage) std::function(FunctorWrapper(callable)); data->convertible = storage; } }; @@ -894,7 +936,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::to_python_converter>(); QFlags_from_python_obj(); - Functor0_converter(); // converter for the onRefreshed-callback + Functor0_converter(); // converter for the onRefreshed-callback bpy::class_("IPluginList") .def("state", bpy::pure_virtual(&MOBase::IPluginList::state)) @@ -912,7 +954,7 @@ BOOST_PYTHON_MODULE(mobase) bpy::to_python_converter>(); QFlags_from_python_obj(); - Functor2_converter(); // converter for the onModStateChanged-callback + Functor2_converter(); // converter for the onModStateChanged-callback bpy::class_("IModList") .def("displayName", bpy::pure_virtual(&MOBase::IModList::displayName)) From 0abbab9f8c527c17e6e89f1e6fbefd33ac4fc59d Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 19 Apr 2018 01:17:11 +0100 Subject: [PATCH 16/27] Add missing parts of IOrganizer bindings --- src/runner/pythonrunner.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 1de2878..eb0ad3d 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -831,12 +831,18 @@ BOOST_PYTHON_MODULE(mobase) .def("isCustom", &ExecutableInfo::isCustom) ; + Functor1_converter(); + Functor1_converter(); + Functor1_converter(); + Functor2_converter(); bpy::class_("IOrganizer") .def("createNexusBridge", bpy::pure_virtual(&IOrganizer::createNexusBridge), bpy::return_value_policy()) .def("profileName", bpy::pure_virtual(&IOrganizer::profileName)) .def("profilePath", bpy::pure_virtual(&IOrganizer::profilePath)) .def("downloadsPath", bpy::pure_virtual(&IOrganizer::downloadsPath)) + .def("overwritePath", bpy::pure_virtual(&IOrganizer::overwritePath)) + .def("basePath", bpy::pure_virtual(&IOrganizer::basePath)) .def("appVersion", bpy::pure_virtual(&IOrganizer::appVersion)) .def("getMod", bpy::pure_virtual(&IOrganizer::getMod), bpy::return_value_policy()) .def("createMod", bpy::pure_virtual(&IOrganizer::createMod), bpy::return_value_policy()) @@ -848,16 +854,21 @@ BOOST_PYTHON_MODULE(mobase) .def("setPersistent", bpy::pure_virtual(&IOrganizer::setPersistent)) .def("pluginDataPath", bpy::pure_virtual(&IOrganizer::pluginDataPath)) .def("installMod", bpy::pure_virtual(&IOrganizer::installMod),(bpy::arg("nameSuggestion")=""), bpy::return_value_policy()) + .def("resolvePath", bpy::pure_virtual(&IOrganizer::resolvePath)) + .def("listDirectories", bpy::pure_virtual(&IOrganizer::listDirectories)) + .def("findFiles", bpy::pure_virtual(&IOrganizer::findFiles)) + .def("getFileOrigins", bpy::pure_virtual(&IOrganizer::getFileOrigins)) + .def("findFileInfos", bpy::pure_virtual(&IOrganizer::findFileInfos)) .def("downloadManager", bpy::pure_virtual(&IOrganizer::downloadManager), bpy::return_value_policy()) .def("pluginList", bpy::pure_virtual(&IOrganizer::pluginList), bpy::return_value_policy()) .def("modList", bpy::pure_virtual(&IOrganizer::modList), bpy::return_value_policy()) - .def("startApplication", bpy::pure_virtual(&IOrganizer::startApplication), bpy::return_value_policy()) + .def("profile", bpy::pure_virtual(&IOrganizer::profile), bpy::return_value_policy()) + .def("startApplication", bpy::pure_virtual(&IOrganizer::startApplication), ((bpy::arg("args")=QStringList()), (bpy::arg("cwd")=""), (bpy::arg("profile")="")), bpy::return_value_policy()) //.def("waitForApplication", bpy::pure_virtual(&IOrganizer::waitForApplication), (bpy::arg("exitCode")=nullptr), bpy::return_value_policy()) + .def("onModInstalled", bpy::pure_virtual(&IOrganizer::onModInstalled)) .def("onAboutToRun", bpy::pure_virtual(&IOrganizer::onAboutToRun)) .def("onFinishedRun", bpy::pure_virtual(&IOrganizer::onFinishedRun)) - .def("onModInstalled", bpy::pure_virtual(&IOrganizer::onModInstalled)) - .def("refreshModList", bpy::pure_virtual(&IOrganizer::refreshModList)) - .def("profile", bpy::pure_virtual(&IOrganizer::profile), bpy::return_value_policy()) + .def("refreshModList", bpy::pure_virtual(&IOrganizer::refreshModList), (bpy::arg("saveChanges")=true)) .def("managedGame", bpy::pure_virtual(&IOrganizer::managedGame), bpy::return_value_policy()) .def("modsSortedByProfilePriority", bpy::pure_virtual(&IOrganizer::modsSortedByProfilePriority)) ; From 4feb06a099f177fd03f617603ecf2230d3462cc4 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 19 Apr 2018 02:09:27 +0100 Subject: [PATCH 17/27] Add IPluginFileMapper Wrapper and bindings. --- src/runner/proxypluginwrappers.cpp | 13 +++++++++++++ src/runner/proxypluginwrappers.h | 15 +++++++++++++++ src/runner/pythonrunner.cpp | 15 +++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index a54d11e..76106af 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -133,6 +133,19 @@ void IPluginDiagnoseWrapper::invalidate() } /// end IPluginDiagnose Wrapper ///////////////////////////////////// +/// IPluginFileMapper Wrapper + + +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginFileMapperWrapper) + +MappingType IPluginFileMapperWrapper::mappings() const +{ + try { + return this->get_override("mappings")(); + } PYCATCH; +} +/// end IPluginFileMapper Wrapper +///////////////////////////////////// /// IPluginGame Wrapper diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index f76e7a1..0c01c3a 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -3,6 +3,7 @@ #include +#include #include #include #include @@ -52,6 +53,20 @@ public: }; +// Even though the base interface is not an IPlugin or QObject, this has to be because we have no way to pass Mod Organizer a plugin that implements multiple interfaces. +// QObject must be the first base class because moc assumes the first base class is a QObject +class IPluginFileMapperWrapper : public QObject, public MOBase::IPluginFileMapper, public MOBase::IPlugin, public boost::python::wrapper +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginFileMapper) + +public: + virtual MappingType mappings() const override; + + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS +}; + + class IPluginGameWrapper : public MOBase::IPluginGame, public boost::python::wrapper { Q_OBJECT Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index eb0ad3d..ee1de22 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -989,6 +989,19 @@ BOOST_PYTHON_MODULE(mobase) .def("_invalidate", &IPluginDiagnoseWrapper::invalidate) ; + bpy::class_("Mapping") + .def_readwrite("source", &Mapping::source) + .def_readwrite("destination", &Mapping::destination) + .def_readwrite("isDirectory", &Mapping::isDirectory) + .def_readwrite("createTarget", &Mapping::createTarget) + ; + + std_vector_from_python_obj(); + + bpy::class_("IPluginFileMapper") + .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) + ; + bpy::enum_("LoadOrderMechanism") .value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime) .value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt) @@ -1202,6 +1215,8 @@ QObject *PythonRunner::instantiate(const QString &pluginName) TRY_PLUGIN_TYPE(IPluginGame, pluginObj); // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject TRY_PLUGIN_TYPE(IPluginDiagnoseWrapper, pluginObj); + // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject + TRY_PLUGIN_TYPE(IPluginFileMapperWrapper, pluginObj); TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); TRY_PLUGIN_TYPE(IPluginTool, pluginObj); } catch (const bpy::error_already_set&) { From a35208d9026ed91acbc12dd65b18d790a22eace0 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 19 Apr 2018 23:33:29 +0100 Subject: [PATCH 18/27] Improve QVariant conversion and generalise QMap conversion --- src/runner/pythonrunner.cpp | 113 ++++++++++++++++++++---------------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index ee1de22..7fad240 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -181,6 +181,55 @@ struct GuessedValue_converters }; +template +struct QMap_converters +{ + struct QMap_to_python + { + static PyObject *convert(const QMap &map) { + bpy::dict result; + QMapIterator iter(map); + while (iter.hasNext()) { + iter.next(); + result[bpy::object(iter.key())] = bpy::object(iter.value()); + } + return bpy::incref(result.ptr()); + } + }; + + struct QMap_from_python + { + QMap_from_python() { + bpy::converter::registry::push_back(&convertible, &construct, bpy::type_id>()); + } + + static void *convertible(PyObject *objPtr) { + return PyDict_Check(objPtr) ? objPtr : nullptr; + } + + static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { + void *storage = ((bpy::converter::rvalue_from_python_storage>*)data)->storage.bytes; + QMap *result = new (storage) QMap(); + bpy::dict source(bpy::handle<>(bpy::borrowed(objPtr))); + bpy::list keys = source.keys(); + int len = bpy::len(keys); + for (int i = 0; i < len; ++i) { + bpy::object pyKey = keys[i]; + (*result)[bpy::extract(pyKey)] = bpy::extract(source[pyKey]); + } + + data->convertible = storage; + } + }; + + QMap_converters() + { + QMap_from_python(); + bpy::to_python_converter, QMap_to_python >(); + } +}; + + struct QVariant_to_python_obj { static PyObject *convert(const QVariant &var) { @@ -188,24 +237,12 @@ struct QVariant_to_python_obj case QVariant::Int: return PyLong_FromLong(var.toInt()); case QVariant::UInt: return PyLong_FromUnsignedLong(var.toUInt()); case QVariant::Bool: return PyBool_FromLong(var.toBool()); - case QVariant::String: return bpy::incref(bpy::object(var.toString().toUtf8().constData()).ptr()); + case QVariant::String: return bpy::incref(bpy::object(var.toString()).ptr()); case QVariant::List: { - QVariantList list = var.toList(); - PyObject *result = PyList_New(list.count()); - for (QVariant var : list) { - PyList_Append(result, convert(var)); - } - return result; + return bpy::incref(bpy::object(var.toList()).ptr()); } break; case QVariant::Map: { - QVariantMap map = var.toMap(); - PyObject *result = PyDict_New(); - QMapIterator iter(map); - while (iter.hasNext()) { - iter.next(); - PyDict_SetItem(result, convert(iter.key()), convert(iter.value())); - } - return result; + return bpy::incref(bpy::object(var.toMap()).ptr()); } break; default: { PyErr_Format(PyExc_TypeError, "type unsupported: %d", var.type()); @@ -239,49 +276,21 @@ struct QVariant_from_python_obj data->convertible = storage; } - static QVariant variantFromPyObject(PyObject *objPtr) { - QVariant result; - if (PyList_Check(objPtr)) { - QVariantList resultList; - for (int i = 0; i < PyList_Size(objPtr); ++i) { - resultList.append(variantFromPyObject(PyList_GetItem(objPtr, i))); - } - result = resultList; - } else if (PyString_Check(objPtr)) { - result = PyString_AsString(objPtr); - } else if (PyBool_Check(objPtr)) { - result = (objPtr == Py_True); - } else if (PyInt_Check(objPtr)) { - //QVariant doesn't have long. It has int or long long. Given that on m/s, - //long is 32 bits for 32- and 64- bit code... - result = static_cast(PyInt_AsLong(objPtr)); - } else { - PyErr_SetString(PyExc_TypeError, "type unsupported"); - throw bpy::error_already_set(); - } - return result; - } - static void construct(PyObject *objPtr, bpy::converter::rvalue_from_python_stage1_data *data) { // PyBools will also return true for PyInt_Check but not the other way around, so the order // here is relevant if (PyList_Check(objPtr)) { - QVariantList result; - for (int i = 0; i < PyList_Size(objPtr); ++i) { - result.append(variantFromPyObject(PyList_GetItem(objPtr, i))); - } - constructVariant(result, data); - } else if (PyString_Check(objPtr)) { - const char *value = PyString_AsString(objPtr); - constructVariant(value, data); + constructVariant(bpy::extract(objPtr)(), data); + } else if (PyDict_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); + } else if (PyString_Check(objPtr) || PyUnicode_Check(objPtr)) { + constructVariant(bpy::extract(objPtr)(), data); } else if (PyBool_Check(objPtr)) { - bool value = (objPtr == Py_True); - constructVariant(value, data); + constructVariant(bpy::extract(objPtr)(), data); } else if (PyInt_Check(objPtr)) { //QVariant doesn't have long. It has int or long long. Given that on m/s, //long is 32 bits for 32- and 64- bit code... - int value = static_cast(PyInt_AsLong(objPtr)); - constructVariant(value, data); + constructVariant(bpy::extract(objPtr)(), data); } else { PyErr_SetString(PyExc_TypeError, "type unsupported"); throw bpy::error_already_set(); @@ -1095,6 +1104,10 @@ BOOST_PYTHON_MODULE(mobase) QList_from_python_obj(); bpy::to_python_converter, QList_to_python_list>(); + QList_from_python_obj(); + QList_to_python_list(); + + QMap_converters(); std_vector_from_python_obj(); From 7b6ca76deca6c101e116b10e9e3fe72f1c3583a4 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 19 Apr 2018 23:34:48 +0100 Subject: [PATCH 19/27] Handle conversion for types IPluginModPage depends on --- src/runner/pythonrunner.cpp | 44 +++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 7fad240..44ddbcd 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -67,21 +67,6 @@ using namespace MOBase; namespace bpy = boost::python; -struct ModRepositoryFileInfo_to_python_dict -{ - static PyObject *convert(const ModRepositoryFileInfo &info) { - PyObject *res = PyDict_New(); - PyDict_SetItemString(res, "uri", bpy::incref(bpy::object(info.uri).ptr())); - PyDict_SetItemString(res, "name", bpy::incref(bpy::object(info.name).ptr())); - PyDict_SetItemString(res, "description", bpy::incref(bpy::object(info.description.toUtf8().constData()).ptr())); - PyDict_SetItemString(res, "categoryID", PyLong_FromLong(info.categoryID)); - PyDict_SetItemString(res, "fileID", PyLong_FromLong(info.fileID)); - PyDict_SetItemString(res, "fileSize", PyLong_FromLong(static_cast(info.fileSize))); - PyDict_SetItemString(res, "version", bpy::incref(bpy::object(info.version).ptr())); - return bpy::incref(res); - } -}; - struct QString_to_python_str { @@ -461,10 +446,12 @@ template <> struct MetaData { static const char *className template <> struct MetaData { static const char *className() { return "QObject"; } }; template <> struct MetaData { static const char *className() { return "QObject"; } }; template <> struct MetaData { static const char *className() { return "QWidget"; } }; +template <> struct MetaData { static const char *className() { return "QDateTime"; } }; template <> struct MetaData { static const char *className() { return "QDir"; } }; template <> struct MetaData { static const char *className() { return "QFileInfo"; } }; template <> struct MetaData { static const char *className() { return "QIcon"; } }; template <> struct MetaData { static const char *className() { return "QStringList"; } }; +template <> struct MetaData { static const char *className() { return "QUrl"; } }; template <> struct MetaData { static const char *className() { return "QVariant"; } }; @@ -775,11 +762,13 @@ BOOST_PYTHON_MODULE(mobase) QString_from_python_str(); //QClass_converters(); + QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); QClass_converters(); + QClass_converters(); QInterface_converters(); @@ -910,6 +899,29 @@ BOOST_PYTHON_MODULE(mobase) .def("requestToggleEndorsement", bpy::pure_virtual(&IModRepositoryBridge::requestToggleEndorsement)) ; + bpy::class_("ModRepositoryFileInfo") + .def(bpy::init()) + .def(bpy::init>()) + .def("toString", &ModRepositoryFileInfo::toString) + .def("createFromJson", &ModRepositoryFileInfo::createFromJson).staticmethod("createFromJson") + .def_readwrite("name", &ModRepositoryFileInfo::name) + .def_readwrite("uri", &ModRepositoryFileInfo::uri) + .def_readwrite("description", &ModRepositoryFileInfo::description) + .def_readwrite("version", &ModRepositoryFileInfo::version) + .def_readwrite("newestVersion", &ModRepositoryFileInfo::newestVersion) + .def_readwrite("categoryID", &ModRepositoryFileInfo::categoryID) + .def_readwrite("modName", &ModRepositoryFileInfo::modName) + .def_readwrite("gameName", &ModRepositoryFileInfo::gameName) + .def_readwrite("modID", &ModRepositoryFileInfo::modID) + .def_readwrite("fileID", &ModRepositoryFileInfo::fileID) + .def_readwrite("fileSize", &ModRepositoryFileInfo::fileSize) + .def_readwrite("fileName", &ModRepositoryFileInfo::fileName) + .def_readwrite("fileCategory", &ModRepositoryFileInfo::fileCategory) + .def_readwrite("fileTime", &ModRepositoryFileInfo::fileTime) + .def_readwrite("repository", &ModRepositoryFileInfo::repository) + .def_readwrite("userData", &ModRepositoryFileInfo::userData) + ; + bpy::class_("IDownloadManager") .def("startDownloadURLs", bpy::pure_virtual(&IDownloadManager::startDownloadURLs)) .def("startDownloadNexusFile", bpy::pure_virtual(&IDownloadManager::startDownloadNexusFile)) @@ -1095,7 +1107,7 @@ BOOST_PYTHON_MODULE(mobase) GuessedValue_converters(); - bpy::to_python_converter(); + //bpy::to_python_converter(); QList_from_python_obj(); QList_from_python_obj(); From f13a5961dfae94df5b75aa52b64cb33f096bc2b9 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Thu, 19 Apr 2018 23:36:24 +0100 Subject: [PATCH 20/27] Align ModRepositoryFileInfo indentation --- src/runner/pythonrunner.cpp | 42 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 44ddbcd..3566de1 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -900,27 +900,27 @@ BOOST_PYTHON_MODULE(mobase) ; bpy::class_("ModRepositoryFileInfo") - .def(bpy::init()) - .def(bpy::init>()) - .def("toString", &ModRepositoryFileInfo::toString) - .def("createFromJson", &ModRepositoryFileInfo::createFromJson).staticmethod("createFromJson") - .def_readwrite("name", &ModRepositoryFileInfo::name) - .def_readwrite("uri", &ModRepositoryFileInfo::uri) - .def_readwrite("description", &ModRepositoryFileInfo::description) - .def_readwrite("version", &ModRepositoryFileInfo::version) - .def_readwrite("newestVersion", &ModRepositoryFileInfo::newestVersion) - .def_readwrite("categoryID", &ModRepositoryFileInfo::categoryID) - .def_readwrite("modName", &ModRepositoryFileInfo::modName) - .def_readwrite("gameName", &ModRepositoryFileInfo::gameName) - .def_readwrite("modID", &ModRepositoryFileInfo::modID) - .def_readwrite("fileID", &ModRepositoryFileInfo::fileID) - .def_readwrite("fileSize", &ModRepositoryFileInfo::fileSize) - .def_readwrite("fileName", &ModRepositoryFileInfo::fileName) - .def_readwrite("fileCategory", &ModRepositoryFileInfo::fileCategory) - .def_readwrite("fileTime", &ModRepositoryFileInfo::fileTime) - .def_readwrite("repository", &ModRepositoryFileInfo::repository) - .def_readwrite("userData", &ModRepositoryFileInfo::userData) - ; + .def(bpy::init()) + .def(bpy::init>()) + .def("toString", &ModRepositoryFileInfo::toString) + .def("createFromJson", &ModRepositoryFileInfo::createFromJson).staticmethod("createFromJson") + .def_readwrite("name", &ModRepositoryFileInfo::name) + .def_readwrite("uri", &ModRepositoryFileInfo::uri) + .def_readwrite("description", &ModRepositoryFileInfo::description) + .def_readwrite("version", &ModRepositoryFileInfo::version) + .def_readwrite("newestVersion", &ModRepositoryFileInfo::newestVersion) + .def_readwrite("categoryID", &ModRepositoryFileInfo::categoryID) + .def_readwrite("modName", &ModRepositoryFileInfo::modName) + .def_readwrite("gameName", &ModRepositoryFileInfo::gameName) + .def_readwrite("modID", &ModRepositoryFileInfo::modID) + .def_readwrite("fileID", &ModRepositoryFileInfo::fileID) + .def_readwrite("fileSize", &ModRepositoryFileInfo::fileSize) + .def_readwrite("fileName", &ModRepositoryFileInfo::fileName) + .def_readwrite("fileCategory", &ModRepositoryFileInfo::fileCategory) + .def_readwrite("fileTime", &ModRepositoryFileInfo::fileTime) + .def_readwrite("repository", &ModRepositoryFileInfo::repository) + .def_readwrite("userData", &ModRepositoryFileInfo::userData) + ; bpy::class_("IDownloadManager") .def("startDownloadURLs", bpy::pure_virtual(&IDownloadManager::startDownloadURLs)) From 8877fee4fabfab5f9cfa3f3421fd7704eed7070c Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Fri, 20 Apr 2018 16:39:58 +0100 Subject: [PATCH 21/27] Implement IpluginModPage bindings. --- src/runner/proxypluginwrappers.cpp | 49 ++++++++++++++++++++++++++++++ src/runner/proxypluginwrappers.h | 17 +++++++++++ src/runner/pythonrunner.cpp | 5 +++ 3 files changed, 71 insertions(+) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 76106af..207199e 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -2,6 +2,7 @@ #include #include "error.h" #include "gilock.h" +#include #include namespace boost @@ -429,6 +430,54 @@ void IPluginInstallerCustomWrapper::setParentWidget(QWidget *parent) } /// end IPluginInstallerCustom Wrapper ///////////////////////////// +/// IPluginModPage Wrapper + + +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginModPageWrapper) + +QString IPluginModPageWrapper::displayName() const +{ + try { + return this->get_override("displayName")(); + } PYCATCH; +} + +QIcon IPluginModPageWrapper::icon() const +{ + try { + return this->get_override("icon")(); + } PYCATCH; +} + +QUrl IPluginModPageWrapper::pageURL() const +{ + try { + return this->get_override("pageURL")(); + } PYCATCH; +} + +bool IPluginModPageWrapper::useIntegratedBrowser() const +{ + try { + return this->get_override("useIntegratedBrowser")(); + } PYCATCH; +} + +bool IPluginModPageWrapper::handlesDownload(const QUrl & pageURL, const QUrl & downloadURL, MOBase::ModRepositoryFileInfo & fileInfo) const +{ + try { + return this->get_override("handlesDownload")(pageURL, downloadURL, fileInfo); + } PYCATCH; +} + +void IPluginModPageWrapper::setParentWidget(QWidget * widget) +{ + try { + this->get_override("setParentWidget")(widget); + } PYCATCH; +} +/// end IPluginModPage Wrapper +///////////////////////////// /// IPluginTool Wrapper diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 0c01c3a..e0ce858 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #ifndef Q_MOC_RUN @@ -133,6 +134,22 @@ public: }; +class IPluginModPageWrapper : public MOBase::IPluginModPage, public boost::python::wrapper +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginModPage) + + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS +public: + virtual QString displayName() const override; + virtual QIcon icon() const override; + virtual QUrl pageURL() const override; + virtual bool useIntegratedBrowser() const override; + virtual bool handlesDownload(const QUrl &pageURL, const QUrl &downloadURL, MOBase::ModRepositoryFileInfo &fileInfo) const override; + virtual void setParentWidget(QWidget *widget) override; +}; + + class IPluginToolWrapper: public MOBase::IPluginTool, public boost::python::wrapper { Q_OBJECT diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 3566de1..2461674 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -1101,6 +1101,10 @@ BOOST_PYTHON_MODULE(mobase) .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginInstallerCustom::setParentWidget)) ; + bpy::class_("IPluginModPage") + .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginModPage::setParentWidget)) + ; + bpy::class_, boost::noncopyable>("IPluginTool") .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) ; @@ -1243,6 +1247,7 @@ QObject *PythonRunner::instantiate(const QString &pluginName) // Must try the wrapper because it's only a plugin extension interface in C++, so doesn't extend QObject TRY_PLUGIN_TYPE(IPluginFileMapperWrapper, pluginObj); TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); + TRY_PLUGIN_TYPE(IPluginModPage, pluginObj); TRY_PLUGIN_TYPE(IPluginTool, pluginObj); } catch (const bpy::error_already_set&) { qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); From 8fe091ab48f53428bf9e6aa74d942f8881c3c086 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 22 Apr 2018 00:34:59 +0100 Subject: [PATCH 22/27] Implement IPluginProxy bindings. --- src/runner/proxypluginwrappers.cpp | 36 +++++++++++++++++++++++++++++- src/runner/proxypluginwrappers.h | 13 +++++++++++ src/runner/pythonrunner.cpp | 14 ++++++++++-- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 207199e..38edfc0 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -4,6 +4,7 @@ #include "gilock.h" #include #include +#include namespace boost { @@ -478,6 +479,40 @@ void IPluginModPageWrapper::setParentWidget(QWidget * widget) } /// end IPluginModPage Wrapper ///////////////////////////// +/// IPluginPreview Wrapper + + +COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginPreviewWrapper) + +std::set IPluginPreviewWrapper::supportedExtensions() const +{ + try { + return this->get_override("supportedExtensions")(); + } PYCATCH; +} + +// right now, this is copied and pasted from apythonrunner.cpp, but it should probably be moved int oa shared header +static const sipAPIDef *sipAPI() +{ + static const sipAPIDef *sipApi = nullptr; + if (sipApi == nullptr) { + sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); + } + + return sipApi; +} + +QWidget *IPluginPreviewWrapper::genFilePreview(const QString &fileName, const QSize &maxSize) const +{ + try { + boost::python::object pyVersion = this->get_override("genFilePreview")(fileName, maxSize); + // We need responsibility for deleting the QWidget to be transferred to C++ + sipAPI()->api_transfer_to(pyVersion.ptr(), 0); + return boost::python::extract(pyVersion)(); + } PYCATCH; +} +/// end IPluginPreview Wrapper +///////////////////////////// /// IPluginTool Wrapper @@ -521,4 +556,3 @@ void IPluginToolWrapper::display() const } /// end IPluginTool Wrapper - diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index e0ce858..322b316 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #ifndef Q_MOC_RUN @@ -150,6 +151,18 @@ public: }; +class IPluginPreviewWrapper : public MOBase::IPluginPreview, public boost::python::wrapper +{ + Q_OBJECT + Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview) + + COMMON_I_PLUGIN_WRAPPER_DECLARATIONS +public: + virtual std::set supportedExtensions() const override; + virtual QWidget *genFilePreview(const QString &fileName, const QSize &maxSize) const override; +}; + + class IPluginToolWrapper: public MOBase::IPluginTool, public boost::python::wrapper { Q_OBJECT diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 2461674..2cada6b 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -450,6 +450,7 @@ template <> struct MetaData { static const char *className() { return template <> struct MetaData { static const char *className() { return "QDir"; } }; template <> struct MetaData { static const char *className() { return "QFileInfo"; } }; template <> struct MetaData { static const char *className() { return "QIcon"; } }; +template <> struct MetaData { static const char *className() { return "QSize"; } }; template <> struct MetaData { static const char *className() { return "QStringList"; } }; template <> struct MetaData { static const char *className() { return "QUrl"; } }; template <> struct MetaData { static const char *className() { return "QVariant"; } }; @@ -534,7 +535,9 @@ struct QClass_converters bpy::throw_error_already_set(); } - sipAPI()->api_transfer_to(objPtr, 0); + // This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that + // Instead, this should be called within the wrappers for functions which return deletable pointers. + //sipAPI()->api_transfer_to(objPtr, 0); sipSimpleWrapper *wrapper = reinterpret_cast(objPtr); return wrapper->data; @@ -597,7 +600,9 @@ struct QInterface_converters bpy::throw_error_already_set(); } - sipAPI()->api_transfer_to(objPtr, 0); + // This would transfer responsibility for deconstructing the object to C++, but Boost assumes l-value converters (such as this) don't do that + // Instead, this should be called within the wrappers for functions which return deletable pointers. + //sipAPI()->api_transfer_to(objPtr, 0); sipSimpleWrapper *wrapper = reinterpret_cast(objPtr); return wrapper->data; @@ -767,6 +772,7 @@ BOOST_PYTHON_MODULE(mobase) QClass_converters(); QClass_converters(); QClass_converters(); + QClass_converters(); QClass_converters(); QClass_converters(); QInterface_converters(); @@ -1105,6 +1111,9 @@ BOOST_PYTHON_MODULE(mobase) .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginModPage::setParentWidget)) ; + bpy::class_("IPluginPreview") + ; + bpy::class_, boost::noncopyable>("IPluginTool") .def("setParentWidget", bpy::pure_virtual(&MOBase::IPluginTool::setParentWidget)) ; @@ -1248,6 +1257,7 @@ QObject *PythonRunner::instantiate(const QString &pluginName) TRY_PLUGIN_TYPE(IPluginFileMapperWrapper, pluginObj); TRY_PLUGIN_TYPE(IPluginInstallerCustom, pluginObj); TRY_PLUGIN_TYPE(IPluginModPage, pluginObj); + TRY_PLUGIN_TYPE(IPluginPreview, pluginObj); TRY_PLUGIN_TYPE(IPluginTool, pluginObj); } catch (const bpy::error_already_set&) { qWarning("failed to run python script \"%s\"", qPrintable(pluginName)); From bf8f427cd816972f8b42eee05fde30323baed989 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 22 Apr 2018 00:59:42 +0100 Subject: [PATCH 23/27] Move sipAPI getter function to a shared header so it isn't duplicated. --- src/runner/proxypluginwrappers.cpp | 13 +------------ src/runner/pythonrunner.cpp | 12 +----------- src/runner/sipApiAccess.h | 11 +++++++++++ 3 files changed, 13 insertions(+), 23 deletions(-) create mode 100644 src/runner/sipApiAccess.h diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 38edfc0..7518151 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -4,7 +4,7 @@ #include "gilock.h" #include #include -#include +#include "sipApiAccess.h" namespace boost { @@ -491,17 +491,6 @@ std::set IPluginPreviewWrapper::supportedExtensions() const } PYCATCH; } -// right now, this is copied and pasted from apythonrunner.cpp, but it should probably be moved int oa shared header -static const sipAPIDef *sipAPI() -{ - static const sipAPIDef *sipApi = nullptr; - if (sipApi == nullptr) { - sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); - } - - return sipApi; -} - QWidget *IPluginPreviewWrapper::genFilePreview(const QString &fileName, const QSize &maxSize) const { try { diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 2cada6b..1f1cbc2 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -9,6 +9,7 @@ #include #include "uibasewrappers.h" #include "proxypluginwrappers.h" +#include "sipApiAccess.h" #include #include @@ -417,17 +418,6 @@ struct stdset_from_python_list }; -static const sipAPIDef *sipAPI() -{ - static const sipAPIDef *sipApi = nullptr; - if (sipApi == nullptr) { - sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); - } - - return sipApi; -} - - struct IModRepositoryBridge_to_python { static PyObject *convert(IModRepositoryBridge *bridge) diff --git a/src/runner/sipApiAccess.h b/src/runner/sipApiAccess.h new file mode 100644 index 0000000..a5e8954 --- /dev/null +++ b/src/runner/sipApiAccess.h @@ -0,0 +1,11 @@ +#include + +static const sipAPIDef *sipAPI() +{ + static const sipAPIDef *sipApi = nullptr; + if (sipApi == nullptr) { + sipApi = (const sipAPIDef *)PyCapsule_Import("sip._C_API", 0); + } + + return sipApi; +} From 028c14a9516f8cfefbce2cc95f62cce1f2551362 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 22 Apr 2018 15:37:29 +0100 Subject: [PATCH 24/27] Add missing include guard to sipApiAccess.h --- src/runner/sipApiAccess.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/runner/sipApiAccess.h b/src/runner/sipApiAccess.h index a5e8954..c9c95d0 100644 --- a/src/runner/sipApiAccess.h +++ b/src/runner/sipApiAccess.h @@ -1,3 +1,6 @@ +#ifndef SIPAPIACCESS_H +#define SIPAPIACCESS_H + #include static const sipAPIDef *sipAPI() @@ -9,3 +12,5 @@ static const sipAPIDef *sipAPI() return sipApi; } + +#endif // SIPAPIACCESS_H From 0a9b4b9fc440c1a7282ae749ecadac0a0f3e0908 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 22 Apr 2018 15:39:09 +0100 Subject: [PATCH 25/27] Change the capitalisation of sipapiaccess.h to make it consistent with other files --- src/runner/{sipApiAccess.h => sipapiaccess.h} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/runner/{sipApiAccess.h => sipapiaccess.h} (100%) diff --git a/src/runner/sipApiAccess.h b/src/runner/sipapiaccess.h similarity index 100% rename from src/runner/sipApiAccess.h rename to src/runner/sipapiaccess.h From 902024e5089088ab9ff9f958693ac1491c4e098e Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 25 Apr 2018 17:25:12 +0100 Subject: [PATCH 26/27] Add Game Features support --- src/runner/CMakeLists.txt | 1 + src/runner/gamefeatureswrappers.cpp | 391 ++++++++++++++++++++++++++++ src/runner/gamefeatureswrappers.h | 85 ++++++ src/runner/proxypluginwrappers.cpp | 41 ++- src/runner/proxypluginwrappers.h | 3 +- src/runner/pycatch.h | 11 + src/runner/pythonrunner.cpp | 56 +++- src/runner/uibasewrappers.h | 17 ++ 8 files changed, 592 insertions(+), 13 deletions(-) create mode 100644 src/runner/gamefeatureswrappers.cpp create mode 100644 src/runner/gamefeatureswrappers.h create mode 100644 src/runner/pycatch.h diff --git a/src/runner/CMakeLists.txt b/src/runner/CMakeLists.txt index 060055b..8750dd7 100644 --- a/src/runner/CMakeLists.txt +++ b/src/runner/CMakeLists.txt @@ -34,6 +34,7 @@ SET(project_path "${default_project_path}" CACHE PATH "path to the other mo proj SET(lib_path "${project_path}/../../install/libs") INCLUDE_DIRECTORIES(${project_path}/uibase/src + ${project_path}/game_features/src ${PYTHON_ROOT}/Include ${SIP_ROOT} ${PYTHON_ROOT}/PC) diff --git a/src/runner/gamefeatureswrappers.cpp b/src/runner/gamefeatureswrappers.cpp new file mode 100644 index 0000000..b887e02 --- /dev/null +++ b/src/runner/gamefeatureswrappers.cpp @@ -0,0 +1,391 @@ +#include "gamefeatureswrappers.h" + +#include + +#include + +#include +#include +#include +#include + +#include "gilock.h" +#include "pycatch.h" + +///////////////////////////// +/// BSAInvalidation Wrapper + + +bool BSAInvalidationWrapper::isInvalidationBSA(const QString &bsaName) +{ + GILock lock; + + try { + return this->get_override("isInvalidationBSA")(bsaName); + } PYCATCH; +} + +void BSAInvalidationWrapper::deactivate(MOBase::IProfile *profile) +{ + GILock lock; + + try { + this->get_override("deactivate")(boost::python::ptr(profile)); + } PYCATCH; +} + +void BSAInvalidationWrapper::activate(MOBase::IProfile *profile) +{ + GILock lock; + + try { + this->get_override("activate")(boost::python::ptr(profile)); + } PYCATCH; +} +/// end BSAInvalidation Wrapper +///////////////////////////// +/// DataArchives Wrapper + + +QStringList DataArchivesWrapper::vanillaArchives() const +{ + GILock lock; + + try { + return this->get_override("vanillaArchives")(); + } PYCATCH; +} + +QStringList DataArchivesWrapper::archives(const MOBase::IProfile *profile) const +{ + GILock lock; + + try { + return this->get_override("archives")(boost::python::ptr(profile)); + } PYCATCH; +} + +void DataArchivesWrapper::addArchive(MOBase::IProfile *profile, int index, const QString &archiveName) +{ + GILock lock; + + try { + this->get_override("addArchive")(boost::python::ptr(profile), index, archiveName); + } PYCATCH; +} + +void DataArchivesWrapper::removeArchive(MOBase::IProfile * profile, const QString & archiveName) +{ + GILock lock; + + try { + this->get_override("removeArchive")(boost::python::ptr(profile), archiveName); + } PYCATCH; +} +/// end DataArchives Wrapper +///////////////////////////// +/// GamePlugins Wrapper + + +void GamePluginsWrapper::writePluginLists(const MOBase::IPluginList * pluginList) +{ + GILock lock; + + try { + this->get_override("writePluginLists")(boost::python::ptr(pluginList)); + } PYCATCH; +} + +void GamePluginsWrapper::readPluginLists(MOBase::IPluginList * pluginList) +{ + GILock lock; + + try { + this->get_override("readPluginLists")(boost::python::ptr(pluginList)); + } PYCATCH; +} +/// end GamePlugins Wrapper +///////////////////////////// +/// LocalSavegames Wrapper + + +MappingType LocalSavegamesWrapper::mappings(const QDir & profileSaveDir) const +{ + GILock lock; + + try { + return this->get_override("mappings")(profileSaveDir); + } PYCATCH; +} + +void LocalSavegamesWrapper::prepareProfile(MOBase::IProfile * profile) +{ + GILock lock; + + try { + this->get_override("prepareProfile")(boost::python::ptr(profile)); + } PYCATCH; +} +/// end LocalSavegames Wrapper +///////////////////////////// +/// SaveGameInfo Wrapper + + +MOBase::ISaveGame const * SaveGameInfoWrapper::getSaveGameInfo(QString const & file) const +{ + GILock lock; + + try { + return this->get_override("getSaveGameInfo")(file); + } PYCATCH; +} + +SaveGameInfoWrapper::MissingAssets SaveGameInfoWrapper::getMissingAssets(QString const & file) const +{ + GILock lock; + + try { + return this->get_override("getMissingAssets")(file); + } PYCATCH; +} + +MOBase::ISaveGameInfoWidget * SaveGameInfoWrapper::getSaveGameWidget(QWidget * parent) const +{ + qCritical("Calling method with unimplemented from_python converter."); + + GILock lock; + + try { + return this->get_override("getSaveGameWidget")(boost::python::ptr(parent)); + } PYCATCH; +} + +bool SaveGameInfoWrapper::hasScriptExtenderSave(QString const & file) const +{ + GILock lock; + + try { + return this->get_override("hasScriptExtenderSave")(file); + } PYCATCH; +} +/// end SaveGameInfo Wrapper +///////////////////////////// +/// ScriptExtender Wrapper + +QString ScriptExtenderWrapper::BinaryName() const +{ + GILock lock; + + try { + return this->get_override("BinaryName")(); + } PYCATCH; +} + +QString ScriptExtenderWrapper::PluginPath() const +{ + GILock lock; + + try { + return this->get_override("PluginPath")(); + } PYCATCH; +} + +QString ScriptExtenderWrapper::loaderName() const +{ + GILock lock; + + try { + return this->get_override("loaderName")(); + } PYCATCH; +} + +QString ScriptExtenderWrapper::loaderPath() const +{ + GILock lock; + + try { + return this->get_override("loaderPath")(); + } PYCATCH; +} + +QStringList ScriptExtenderWrapper::saveGameAttachmentExtensions() const +{ + GILock lock; + + try { + return this->get_override("saveGameAttachmentExtensions")(); + } PYCATCH; +} + +bool ScriptExtenderWrapper::isInstalled() const +{ + GILock lock; + + try { + return this->get_override("isInstalled")(); + } PYCATCH; +} + +QString ScriptExtenderWrapper::getExtenderVersion() const +{ + GILock lock; + + try { + return this->get_override("getExtenderVersion")(); + } PYCATCH; +} + +WORD ScriptExtenderWrapper::getArch() const +{ + GILock lock; + + try { + return this->get_override("getArch")(); + } PYCATCH; +} + +/// end ScriptExtender Wrapper +///////////////////////////// +/// UnmanagedMods Wrapper + + +QStringList UnmanagedModsWrapper::mods(bool onlyOfficial) const +{ + GILock lock; + + try { + return this->get_override("mods")(onlyOfficial); + } PYCATCH; +} + +QString UnmanagedModsWrapper::displayName(const QString & modName) const +{ + GILock lock; + + try { + return this->get_override("displayName")(modName); + } PYCATCH; +} + +QFileInfo UnmanagedModsWrapper::referenceFile(const QString & modName) const +{ + GILock lock; + + try { + return this->get_override("referenceFile")(modName); + } PYCATCH; +} + +QStringList UnmanagedModsWrapper::secondaryFiles(const QString & modName) const +{ + GILock lock; + + try { + return this->get_override("secondaryFiles")(modName); + } PYCATCH; +} +/// end UnmanagedMods Wrapper +///////////////////////////// + +template +void insertGameFeature(std::map &map, const boost::python::object &pyObject) +{ + map[std::type_index(typeid(T))] = boost::python::extract(pyObject)(); +} + +game_features_map_from_python::game_features_map_from_python() +{ + boost::python::converter::registry::push_back(&convertible, &construct, boost::python::type_id>()); +} + +void * game_features_map_from_python::convertible(PyObject * objPtr) +{ + return PyDict_Check(objPtr) ? objPtr : nullptr; +} + +void game_features_map_from_python::construct(PyObject * objPtr, boost::python::converter::rvalue_from_python_stage1_data * data) +{ + void *storage = ((boost::python::converter::rvalue_from_python_storage>*)data)->storage.bytes; + std::map *result = new (storage) std::map(); + boost::python::dict source(boost::python::handle<>(boost::python::borrowed(objPtr))); + boost::python::list keys = source.keys(); + int len = boost::python::len(keys); + for (int i = 0; i < len; ++i) + { + boost::python::object pyKey = keys[i]; + // pyKey should be a Boost.Python.class corresponding to a game feature. + std::string className = boost::python::extract(pyKey.attr("__name__"))(); + if (className == "BSAInvalidation") + insertGameFeature(*result, source[pyKey]); + else if (className == "DataArchives") + insertGameFeature(*result, source[pyKey]); + else if (className == "GamePlugins") + insertGameFeature(*result, source[pyKey]); + else if (className == "LocalSavegames") + insertGameFeature(*result, source[pyKey]); + else if (className == "SaveGameInfo") + insertGameFeature(*result, source[pyKey]); + else if (className == "ScriptExtender") + insertGameFeature(*result, source[pyKey]); + else if (className == "UnmanagedMods") + insertGameFeature(*result, source[pyKey]); + } + + data->convertible = storage; +} + +void registerGameFeaturesPythonConverters() +{ + namespace bpy = boost::python; + + game_features_map_from_python(); + + // Features require defs for all methods as Python can access C++ features + bpy::class_("BSAInvalidation") + .def("isInvalidationBSA", bpy::pure_virtual(&BSAInvalidation::isInvalidationBSA)) + .def("deactivate", bpy::pure_virtual(&BSAInvalidation::deactivate)) + .def("activate", bpy::pure_virtual(&BSAInvalidation::activate)) + ; + + bpy::class_("DataArchives") + .def("vanillaArchives", bpy::pure_virtual(&DataArchives::vanillaArchives)) + .def("archives", bpy::pure_virtual(&DataArchives::archives)) + .def("addArchive", bpy::pure_virtual(&DataArchives::addArchive)) + .def("removeArchive", bpy::pure_virtual(&DataArchives::removeArchive)) + ; + + bpy::class_("GamePlugins") + .def("writePluginLists", bpy::pure_virtual(&GamePlugins::writePluginLists)) + .def("readPluginLists", bpy::pure_virtual(&GamePlugins::readPluginLists)) + ; + + bpy::class_("LocalSavegames") + .def("mappings", bpy::pure_virtual(&LocalSavegames::mappings)) + .def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile)) + ; + + bpy::class_("SaveGameInfo") + .def("getSaveGameInfo", bpy::pure_virtual(&SaveGameInfo::getSaveGameInfo), bpy::return_value_policy()) + .def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets)) + .def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy()) + .def("hasScriptExtenderSave", bpy::pure_virtual(&SaveGameInfo::hasScriptExtenderSave)) + ; + + bpy::class_("ScriptExtender") + .def("BinaryName", bpy::pure_virtual(&ScriptExtender::BinaryName)) + .def("PluginPath", bpy::pure_virtual(&ScriptExtender::PluginPath)) + .def("loaderName", bpy::pure_virtual(&ScriptExtender::loaderName)) + .def("loaderPath", bpy::pure_virtual(&ScriptExtender::loaderPath)) + .def("saveGameAttachmentExtensions", bpy::pure_virtual(&ScriptExtender::saveGameAttachmentExtensions)) + .def("isInstalled", bpy::pure_virtual(&ScriptExtender::isInstalled)) + .def("getExtenderVersion", bpy::pure_virtual(&ScriptExtender::getExtenderVersion)) + .def("getArch", bpy::pure_virtual(&ScriptExtender::getArch)) + ; + + bpy::class_("UnmanagedMods") + .def("mods", bpy::pure_virtual(&UnmanagedMods::mods)) + .def("displayName", bpy::pure_virtual(&UnmanagedMods::displayName)) + .def("referenceFile", bpy::pure_virtual(&UnmanagedMods::referenceFile)) + .def("secondaryFiles", bpy::pure_virtual(&UnmanagedMods::secondaryFiles)) + ; +} diff --git a/src/runner/gamefeatureswrappers.h b/src/runner/gamefeatureswrappers.h new file mode 100644 index 0000000..889889a --- /dev/null +++ b/src/runner/gamefeatureswrappers.h @@ -0,0 +1,85 @@ +#ifndef GAMEFEATURESWRAPPERS_H +#define GAMEFEATURESWRAPPERS_H + +#include +#include +#include +#include +#include +#include +#include + +// this might need turning off if Q_MOC_RUN is defined +#include + +///////////////////////////// +/// Wrapper declarations + +class BSAInvalidationWrapper : public BSAInvalidation, public boost::python::wrapper +{ + virtual bool isInvalidationBSA(const QString &bsaName) override; + virtual void deactivate(MOBase::IProfile *profile) override; + virtual void activate(MOBase::IProfile *profile) override; +}; + +class DataArchivesWrapper : public DataArchives, public boost::python::wrapper +{ + virtual QStringList vanillaArchives() const override; + virtual QStringList archives(const MOBase::IProfile *profile) const override; + virtual void addArchive(MOBase::IProfile *profile, int index, const QString &archiveName) override; + virtual void removeArchive(MOBase::IProfile *profile, const QString &archiveName) override; +}; + +class GamePluginsWrapper : public GamePlugins, public boost::python::wrapper +{ + virtual void writePluginLists(const MOBase::IPluginList *pluginList) override; + virtual void readPluginLists(MOBase::IPluginList *pluginList) override; +}; + +class LocalSavegamesWrapper : public LocalSavegames, public boost::python::wrapper +{ + virtual MappingType mappings(const QDir &profileSaveDir) const override; + virtual void prepareProfile(MOBase::IProfile *profile) override; +}; + +class SaveGameInfoWrapper : public SaveGameInfo, public boost::python::wrapper +{ + virtual MOBase::ISaveGame const *getSaveGameInfo(QString const &file) const override; + virtual MissingAssets getMissingAssets(QString const &file) const override; + virtual MOBase::ISaveGameInfoWidget *getSaveGameWidget(QWidget *parent = 0) const override; + virtual bool hasScriptExtenderSave(QString const &file) const override; +}; + +class ScriptExtenderWrapper : public ScriptExtender, public boost::python::wrapper +{ + virtual QString BinaryName() const override; + virtual QString PluginPath() const override; + virtual QString loaderName() const override; + virtual QString loaderPath() const override; + virtual QStringList saveGameAttachmentExtensions() const override; + virtual bool isInstalled() const override; + virtual QString getExtenderVersion() const override; + virtual WORD getArch() const override; +}; + +class UnmanagedModsWrapper : public UnmanagedMods, public boost::python::wrapper +{ + virtual QStringList mods(bool onlyOfficial) const override; + virtual QString displayName(const QString &modName) const override; + virtual QFileInfo referenceFile(const QString &modName) const override; + virtual QStringList secondaryFiles(const QString &modName) const override; +}; + +/// end Wrapper declarations +///////////////////////////// + +struct game_features_map_from_python +{ + game_features_map_from_python(); + static void *convertible(PyObject *objPtr); + static void construct(PyObject *objPtr, boost::python::converter::rvalue_from_python_stage1_data *data); +}; + +void registerGameFeaturesPythonConverters(); + +#endif // GAMEFEATURESWRAPPERS_H diff --git a/src/runner/proxypluginwrappers.cpp b/src/runner/proxypluginwrappers.cpp index 7518151..da807aa 100644 --- a/src/runner/proxypluginwrappers.cpp +++ b/src/runner/proxypluginwrappers.cpp @@ -1,9 +1,9 @@ #include "proxypluginwrappers.h" -#include -#include "error.h" + #include "gilock.h" #include #include +#include "pycatch.h" #include "sipApiAccess.h" namespace boost @@ -23,9 +23,6 @@ namespace boost using namespace MOBase; -#define PYCATCH catch (const boost::python::error_already_set &) { reportPythonError(); throw MyException("unhandled exception"); }\ - catch (...) { throw MyException("An unknown exception was thrown in python code"); } - #define COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(class_name) \ bool class_name::init(MOBase::IOrganizer *moInfo) \ @@ -153,6 +150,7 @@ MappingType IPluginFileMapperWrapper::mappings() const QString IPluginGameWrapper::gameName() const { + GILock lock; try { return this->get_override("gameName")(); } PYCATCH; @@ -160,6 +158,7 @@ QString IPluginGameWrapper::gameName() const void IPluginGameWrapper::initializeProfile(const QDir & directory, ProfileSettings settings) const { + GILock lock; try { this->get_override("initializeProfile")(directory, settings); } PYCATCH; @@ -167,6 +166,7 @@ void IPluginGameWrapper::initializeProfile(const QDir & directory, ProfileSettin QString IPluginGameWrapper::savegameExtension() const { + GILock lock; try { return this->get_override("savegameExtension")(); } PYCATCH; @@ -174,6 +174,7 @@ QString IPluginGameWrapper::savegameExtension() const QString IPluginGameWrapper::savegameSEExtension() const { + GILock lock; try { return this->get_override("savegameSEExtension")(); } PYCATCH; @@ -181,6 +182,7 @@ QString IPluginGameWrapper::savegameSEExtension() const bool IPluginGameWrapper::isInstalled() const { + GILock lock; try { return this->get_override("isInstalled")(); } PYCATCH; @@ -188,6 +190,7 @@ bool IPluginGameWrapper::isInstalled() const QIcon IPluginGameWrapper::gameIcon() const { + GILock lock; try { return this->get_override("gameIcon")(); } PYCATCH; @@ -195,6 +198,7 @@ QIcon IPluginGameWrapper::gameIcon() const QDir IPluginGameWrapper::gameDirectory() const { + GILock lock; try { return this->get_override("gameDirectory")(); } PYCATCH; @@ -202,6 +206,7 @@ QDir IPluginGameWrapper::gameDirectory() const QDir IPluginGameWrapper::dataDirectory() const { + GILock lock; try { return this->get_override("dataDirectory")(); } PYCATCH; @@ -209,6 +214,7 @@ QDir IPluginGameWrapper::dataDirectory() const void IPluginGameWrapper::setGamePath(const QString & path) { + GILock lock; try { this->get_override("setGamePath")(path); } PYCATCH; @@ -216,6 +222,7 @@ void IPluginGameWrapper::setGamePath(const QString & path) QDir IPluginGameWrapper::documentsDirectory() const { + GILock lock; try { return this->get_override("documentsDirectory")(); } PYCATCH; @@ -223,6 +230,7 @@ QDir IPluginGameWrapper::documentsDirectory() const QDir IPluginGameWrapper::savesDirectory() const { + GILock lock; try { return this->get_override("savesDirectory")(); } PYCATCH; @@ -230,6 +238,7 @@ QDir IPluginGameWrapper::savesDirectory() const QList IPluginGameWrapper::executables() const { + GILock lock; try { return this->get_override("executables")(); } PYCATCH; @@ -237,6 +246,7 @@ QList IPluginGameWrapper::executables() const QString IPluginGameWrapper::steamAPPId() const { + GILock lock; try { return this->get_override("steamAPPId")(); } PYCATCH; @@ -244,6 +254,7 @@ QString IPluginGameWrapper::steamAPPId() const QStringList IPluginGameWrapper::primaryPlugins() const { + GILock lock; try { return this->get_override("primaryPlugins")(); } PYCATCH; @@ -251,6 +262,7 @@ QStringList IPluginGameWrapper::primaryPlugins() const QStringList IPluginGameWrapper::gameVariants() const { + GILock lock; try { return this->get_override("gameVariants")(); } PYCATCH; @@ -258,6 +270,7 @@ QStringList IPluginGameWrapper::gameVariants() const void IPluginGameWrapper::setGameVariant(const QString & variant) { + GILock lock; try { this->get_override("setGameVariant")(variant); } PYCATCH; @@ -265,6 +278,7 @@ void IPluginGameWrapper::setGameVariant(const QString & variant) QString IPluginGameWrapper::binaryName() const { + GILock lock; try { return this->get_override("binaryName")(); } PYCATCH; @@ -272,6 +286,7 @@ QString IPluginGameWrapper::binaryName() const QString IPluginGameWrapper::gameShortName() const { + GILock lock; try { return this->get_override("gameShortName")(); } PYCATCH; @@ -279,6 +294,7 @@ QString IPluginGameWrapper::gameShortName() const QStringList IPluginGameWrapper::validShortNames() const { + GILock lock; try { return this->get_override("validShortNames")(); } PYCATCH; @@ -286,6 +302,7 @@ QStringList IPluginGameWrapper::validShortNames() const QString IPluginGameWrapper::gameNexusName() const { + GILock lock; try { return this->get_override("gameNexusName")(); } PYCATCH; @@ -293,6 +310,7 @@ QString IPluginGameWrapper::gameNexusName() const QStringList IPluginGameWrapper::iniFiles() const { + GILock lock; try { return this->get_override("iniFiles")(); } PYCATCH; @@ -300,6 +318,7 @@ QStringList IPluginGameWrapper::iniFiles() const QStringList IPluginGameWrapper::DLCPlugins() const { + GILock lock; try { return this->get_override("DLCPlugins")(); } PYCATCH; @@ -307,6 +326,7 @@ QStringList IPluginGameWrapper::DLCPlugins() const QStringList IPluginGameWrapper::CCPlugins() const { + GILock lock; try { return this->get_override("CCPlugins")(); } PYCATCH; @@ -314,13 +334,15 @@ QStringList IPluginGameWrapper::CCPlugins() const IPluginGame::LoadOrderMechanism IPluginGameWrapper::loadOrderMechanism() const { + GILock lock; try { - return this->get_override("loadorderMechanism")(); + return this->get_override("loadOrderMechanism")(); } PYCATCH; } IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const { + GILock lock; try { return this->get_override("sortMechanism")(); } PYCATCH; @@ -328,6 +350,7 @@ IPluginGame::SortMechanism IPluginGameWrapper::sortMechanism() const int IPluginGameWrapper::nexusModOrganizerID() const { + GILock lock; try { return this->get_override("nexusModOrganizerID")(); } PYCATCH; @@ -335,6 +358,7 @@ int IPluginGameWrapper::nexusModOrganizerID() const int IPluginGameWrapper::nexusGameID() const { + GILock lock; try { return this->get_override("nexusGameID")(); } PYCATCH; @@ -342,6 +366,7 @@ int IPluginGameWrapper::nexusGameID() const bool IPluginGameWrapper::looksValid(QDir const & dir) const { + GILock lock; try { return this->get_override("looksValid")(dir); } PYCATCH; @@ -349,6 +374,7 @@ bool IPluginGameWrapper::looksValid(QDir const & dir) const QString IPluginGameWrapper::gameVersion() const { + GILock lock; try { return this->get_override("gameVersion")(); } PYCATCH; @@ -356,6 +382,7 @@ QString IPluginGameWrapper::gameVersion() const QString IPluginGameWrapper::getLauncherName() const { + GILock lock; try { return this->get_override("getLauncherName")(); } PYCATCH; @@ -365,7 +392,7 @@ COMMON_I_PLUGIN_WRAPPER_DEFINITIONS(IPluginGameWrapper) std::map IPluginGameWrapper::featureList() const { - qCritical("Calling unproxied method IPluginGameWrapper::featureList()"); + GILock lock; try { return this->get_override("_featureList")(); } PYCATCH; diff --git a/src/runner/proxypluginwrappers.h b/src/runner/proxypluginwrappers.h index 322b316..12e6829 100644 --- a/src/runner/proxypluginwrappers.h +++ b/src/runner/proxypluginwrappers.h @@ -108,11 +108,10 @@ public: COMMON_I_PLUGIN_WRAPPER_DECLARATIONS protected: - - // TODO: implementing converters for this is required as otherwise Mod Organizer will crash on load when the game is being managed by this plugin // Apparently, Python developers interpret an underscore in a function name as it being protected virtual std::map featureList() const override; + // Thankfully, the default implementation of the templated 'T *feature()' function should allow us to get away without overriding it. }; diff --git a/src/runner/pycatch.h b/src/runner/pycatch.h new file mode 100644 index 0000000..e74ce4d --- /dev/null +++ b/src/runner/pycatch.h @@ -0,0 +1,11 @@ +#ifndef PYCATCH_H +#define PYCATCH_H + +#include + +#include "error.h" + +#define PYCATCH catch (const boost::python::error_already_set &) { reportPythonError(); throw MOBase::MyException("unhandled exception"); }\ + catch (...) { throw MOBase::MyException("An unknown exception was thrown in python code"); } + +#endif // PYCATCH_H diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index 1f1cbc2..d987f91 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -9,6 +9,7 @@ #include #include "uibasewrappers.h" #include "proxypluginwrappers.h" +#include "gamefeatureswrappers.h" #include "sipApiAccess.h" #include @@ -72,7 +73,11 @@ namespace bpy = boost::python; struct QString_to_python_str { static PyObject *convert(const QString &str) { - return bpy::incref(bpy::object(str.toUtf8().constData()).ptr()); + // It's safer to explicitly convert to unicode as if we don't, this can return either str or unicode without it being easy to know which to expect + bpy::object pyStr = bpy::object(str.toUtf8().constData()); + if (PyString_Check(pyStr.ptr())) + pyStr = pyStr.attr("decode")("utf-8"); + return bpy::incref(pyStr.ptr()); } }; @@ -334,6 +339,26 @@ struct QList_from_python_obj }; +template +struct std_vector_to_python_list +{ + static PyObject *convert(const std::vector &vector) + { + bpy::list pyList; + + try { + for (const T &item : vector) + pyList.append(item); + } + catch (const bpy::error_already_set&) { + reportPythonError(); + } + + return bpy::incref(pyList.ptr()); + } +}; + + template struct std_vector_from_python_obj { @@ -825,6 +850,16 @@ BOOST_PYTHON_MODULE(mobase) .def("isCustom", &ExecutableInfo::isCustom) ; + bpy::class_("ISaveGame") + .def("getFilename", bpy::pure_virtual(&ISaveGame::getFilename)) + .def("getCreationTime", bpy::pure_virtual(&ISaveGame::getCreationTime)) + .def("getSaveGroupIdentifier", bpy::pure_virtual(&ISaveGame::getSaveGroupIdentifier)) + .def("allFiles", bpy::pure_virtual(&ISaveGame::allFiles)) + .def("hasScriptExtenderFile", bpy::pure_virtual(&ISaveGame::hasScriptExtenderFile)) + ; + + // TODO: ISaveGameInfoWidget bindings + Functor1_converter(); Functor1_converter(); Functor1_converter(); @@ -1013,8 +1048,6 @@ BOOST_PYTHON_MODULE(mobase) .def_readwrite("createTarget", &Mapping::createTarget) ; - std_vector_from_python_obj(); - bpy::class_("IPluginFileMapper") .def("mappings", bpy::pure_virtual(&MOBase::IPluginFileMapper::mappings)) ; @@ -1083,6 +1116,14 @@ BOOST_PYTHON_MODULE(mobase) .def("isActive", bpy::pure_virtual(&MOBase::IPluginGame::isActive)) .def("settings", bpy::pure_virtual(&MOBase::IPluginGame::settings)) + // The syntax has to differ slightly from C++ because these are templated + .def("featureBSAInvalidation", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureDataArchives", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureGamePlugins", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureLocalSavegames", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureSaveGameInfo", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureScriptExtender", &MOBase::IPluginGame::feature, bpy::return_value_policy()) + .def("featureUnmanagedMods", &MOBase::IPluginGame::feature, bpy::return_value_policy()) ; bpy::enum_("InstallResult") @@ -1120,13 +1161,20 @@ BOOST_PYTHON_MODULE(mobase) bpy::to_python_converter, QList_to_python_list>(); QList_from_python_obj(); - QList_to_python_list(); + bpy::to_python_converter, + QList_to_python_list>(); QMap_converters(); + QMap_converters(); std_vector_from_python_obj(); + std_vector_from_python_obj(); + bpy::to_python_converter, + std_vector_to_python_list>(); stdset_from_python_list(); + + registerGameFeaturesPythonConverters(); } diff --git a/src/runner/uibasewrappers.h b/src/runner/uibasewrappers.h index 75db982..36f1c0c 100644 --- a/src/runner/uibasewrappers.h +++ b/src/runner/uibasewrappers.h @@ -21,8 +21,12 @@ #include #include #include +#include +#include + #include "error.h" #include "gilock.h" +#include "pycatch.h" extern MOBase::IOrganizer *s_Organizer; @@ -434,4 +438,17 @@ struct IModListWrapper: MOBase::IModList, boost::python::wrapper +{ +public: + virtual QString getFilename() const override { try { return this->get_override("getFilename")(); } PYCATCH }; + virtual QDateTime getCreationTime() const override { try { return this->get_override("getCreationTime")(); } PYCATCH }; + virtual QString getSaveGroupIdentifier() const override { try { return this->get_override("getSaveGroupIdentifier")(); } PYCATCH }; + virtual QStringList allFiles() const override { try { return this->get_override("allFiles")(); } PYCATCH }; + virtual bool hasScriptExtenderFile() const override { try { return this->get_override("hasScriptExtenderFile")(); } PYCATCH }; +}; + + #endif // UIBASEWRAPPERS_H From 94886104f3a9fe43bdb28b4b182024bc63a9744a Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Wed, 2 May 2018 00:19:29 +0100 Subject: [PATCH 27/27] Prevent the C++ part of PyQt objects being deleted while the Python component is still accessible. --- src/runner/pythonrunner.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/runner/pythonrunner.cpp b/src/runner/pythonrunner.cpp index d987f91..7578949 100644 --- a/src/runner/pythonrunner.cpp +++ b/src/runner/pythonrunner.cpp @@ -499,17 +499,33 @@ struct QClass_converters { struct QClass_to_PyQt { + template + static typename std::enable_if_t, T*> getSafeCopy(T *qClass) + { + return new T(*qClass); + } + + template + static typename std::enable_if_t, T*> getSafeCopy(T *qClass) + { + return qClass; + } + static PyObject *convert(const T &object) { const sipTypeDef *type = sipAPI()->api_find_type(MetaData::className()); if (type == nullptr) { return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type((void*)(&object), type, 0); + PyObject *sipObj = sipAPI()->api_convert_from_type((void*)getSafeCopy((T*)&object), type, 0); if (sipObj == nullptr) { return bpy::incref(Py_None); } + if (std::is_copy_constructible_v) + // Ensure Python deletes the C++ component + sipAPI()->api_transfer_back(sipObj); + return bpy::incref(sipObj); } @@ -523,11 +539,15 @@ struct QClass_converters return bpy::incref(Py_None); } - PyObject *sipObj = sipAPI()->api_convert_from_type(object, type, 0); + PyObject *sipObj = sipAPI()->api_convert_from_type(getSafeCopy(object), type, 0); if (sipObj == nullptr) { return bpy::incref(Py_None); } + if (std::is_copy_constructible_v) + // Ensure Python deletes the C++ component + sipAPI()->api_transfer_back(sipObj); + return bpy::incref(sipObj); }