Finally implement everything (some stuff not working).

This commit is contained in:
Mikaël Capelle
2022-04-26 13:53:02 +02:00
parent 92a2429d58
commit 55e8ce326a
22 changed files with 687 additions and 2744 deletions
+10 -10
View File
@@ -14,48 +14,48 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="206"/>
<location filename="proxypython.cpp" line="202"/>
<source>ModOrganizer path contains a semicolon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="208"/>
<location filename="proxypython.cpp" line="204"/>
<source>Python DLL not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="210"/>
<location filename="proxypython.cpp" line="206"/>
<source>Invalid Python DLL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="212"/>
<location filename="proxypython.cpp" line="208"/>
<source>Initializing Python failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="214"/>
<location filename="proxypython.cpp" line="244"/>
<location filename="proxypython.cpp" line="210"/>
<location filename="proxypython.cpp" line="240"/>
<source>invalid problem key %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="222"/>
<location filename="proxypython.cpp" line="218"/>
<source>The path to Mod Organizer (%1) contains a semicolon. &lt;br&gt;While this is legal on NTFS drives, many softwares do not handle it correctly.&lt;br&gt;Unfortunately MO depends on libraries that seem to fall into that group.&lt;br&gt;As a result the python plugin cannot be loaded, and the only solution we canoffer is to remove the semicolon or move MO to a path without a semicolon.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="233"/>
<location filename="proxypython.cpp" line="229"/>
<source>The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="236"/>
<location filename="proxypython.cpp" line="232"/>
<source>The Python plugin DLL is invalid, maybe your antivirus is blocking it. Re-installing MO2 and adding exclusions for it to your AV might fix the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="241"/>
<location filename="proxypython.cpp" line="237"/>
<source>The initialization of the Python plugin DLL failed, unfortunately without any details.</source>
<translation type="unfinished"></translation>
</message>
+2 -6
View File
@@ -153,15 +153,11 @@ QStringList ProxyPython::pluginList(const QDir& pluginPath) const
QString name = iter.next();
QFileInfo info = iter.fileInfo();
if (info.fileName() == "pyCfg.py" || info.fileName() == "installer_wizard") {
if (info.isFile() && name.endsWith(".py")) {
result.append(name);
}
if (info.isFile() && name.endsWith(".py")) {
// result.append(name);
}
else if (info.isDir() && QDir(info.absoluteFilePath()).exists("__init__.py")) {
// result.append(name);
result.append(name);
}
}
+37 -40
View File
@@ -25,59 +25,56 @@ along with python proxy plugin. If not, see <http://www.gnu.org/licenses/>.
#include <Windows.h>
#include <ipluginproxy.h>
#include <iplugindiagnose.h>
#include <ipluginproxy.h>
#include <pythonrunner.h>
class ProxyPython : public QObject, public MOBase::IPluginProxy, public MOBase::IPluginDiagnose
{
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginProxy MOBase::IPluginDiagnose)
#if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
Q_PLUGIN_METADATA(IID "org.tannin.ProxyPython" FILE "proxypython.json")
class ProxyPython : public QObject,
public MOBase::IPluginProxy,
public MOBase::IPluginDiagnose {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginProxy MOBase::IPluginDiagnose)
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
Q_PLUGIN_METADATA(IID "org.mo2.ProxyPython")
#endif
public:
ProxyPython();
ProxyPython();
virtual bool init(MOBase::IOrganizer *moInfo);
virtual QString name() const override;
virtual QString localizedName() const override;
virtual QString author() const override;
virtual QString description() const override;
virtual MOBase::VersionInfo version() const override;
virtual QList<MOBase::PluginSetting> settings() const override;
virtual bool init(MOBase::IOrganizer* moInfo);
virtual QString name() const override;
virtual QString localizedName() const override;
virtual QString author() const override;
virtual QString description() const override;
virtual MOBase::VersionInfo version() const override;
virtual QList<MOBase::PluginSetting> settings() const override;
QStringList pluginList(const QDir& pluginPath) const override;
QList<QObject*> load(const QString& identifier) override;
void unload(const QString& identifier) override;
QStringList pluginList(const QDir& pluginPath) const override;
QList<QObject*> load(const QString& identifier) override;
void unload(const QString& identifier) override;
public: // IPluginDiagnose
virtual std::vector<unsigned int> 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;
public: // IPluginDiagnose
virtual std::vector<unsigned int> 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;
private:
MOBase::IOrganizer* m_MOInfo;
HMODULE m_RunnerLib;
std::unique_ptr<IPythonRunner> m_Runner;
MOBase::IOrganizer *m_MOInfo;
HMODULE m_RunnerLib;
std::unique_ptr<IPythonRunner> m_Runner;
enum class FailureType : unsigned int {
NONE = 0,
SEMICOLON = 1,
DLL_NOT_FOUND = 2,
INVALID_DLL = 3,
INITIALIZATION = 4
};
FailureType m_LoadFailure;
enum class FailureType : unsigned int {
NONE = 0,
SEMICOLON = 1,
DLL_NOT_FOUND = 2,
INVALID_DLL = 3,
INITIALIZATION = 4
};
FailureType m_LoadFailure;
};
#endif // PROXYPYTHON_H
#endif // PROXYPYTHON_H
-1
View File
@@ -1 +0,0 @@
{}
+1 -9
View File
@@ -1,14 +1,11 @@
cmake_minimum_required(VERSION 3.16)
# need to find Boost here with a dummy component to get Boost_LIBRARY_DIRS
find_package(Boost COMPONENTS thread REQUIRED)
pybind11_add_module(pythonrunner SHARED)
mo2_configure_library(pythonrunner
WARNINGS OFF
AUTOMOC ON
TRANSLATIONS OFF
PRIVATE_DEPENDS uibase boost Qt::Core
PRIVATE_DEPENDS uibase Qt::Core
)
set_target_properties(pythonrunner
PROPERTIES
@@ -19,11 +16,6 @@ target_link_libraries(pythonrunner PRIVATE pybind11::embed)
target_include_directories(pythonrunner PRIVATE ${PYTHON_ROOT}/Include)
# this is kind of broken but it only works with this...
target_link_directories(pythonrunner
PRIVATE
${Boost_LIBRARY_DIRS})
target_compile_options(pythonrunner
PRIVATE $<$<CXX_COMPILER_ID:MSVC>:/MP>)
target_compile_definitions(pythonrunner
PRIVATE QT_NO_KEYWORDS PYTHONRUNNER_LIBRARY)
mo2_install_target(pythonrunner INSTALLDIR bin/plugins/data)
File diff suppressed because it is too large Load Diff
@@ -1,390 +0,0 @@
#include "gamefeatureswrappers.h"
#include <any>
#include <typeindex>
#include <ifiletree.h>
#include <ipluginlist.h>
#include <iprofile.h>
#include <isavegame.h>
#include <isavegameinfowidget.h>
#include "pythonwrapperutilities.h"
#include "shared_ptr_converter.h"
/////////////////////////////
/// BSAInvalidation Wrapper
bool BSAInvalidationWrapper::isInvalidationBSA(const QString& bsaName)
{
return basicWrapperFunctionImplementation<bool>(this, "isInvalidationBSA", bsaName);
}
void BSAInvalidationWrapper::deactivate(MOBase::IProfile* profile)
{
return basicWrapperFunctionImplementation<void>(this, "deactivate",
boost::python::ptr(profile));
}
void BSAInvalidationWrapper::activate(MOBase::IProfile* profile)
{
return basicWrapperFunctionImplementation<void>(this, "activate",
boost::python::ptr(profile));
}
bool BSAInvalidationWrapper::prepareProfile(MOBase::IProfile* profile)
{
return basicWrapperFunctionImplementation<bool>(this, "prepareProfile",
boost::python::ptr(profile));
}
/// end BSAInvalidation Wrapper
/////////////////////////////
/// DataArchives Wrapper
QStringList DataArchivesWrapper::vanillaArchives() const
{
return basicWrapperFunctionImplementation<QStringList>(this, "vanillaArchives");
}
QStringList DataArchivesWrapper::archives(const MOBase::IProfile* profile) const
{
return basicWrapperFunctionImplementation<QStringList>(this, "archives",
boost::python::ptr(profile));
}
void DataArchivesWrapper::addArchive(MOBase::IProfile* profile, int index,
const QString& archiveName)
{
return basicWrapperFunctionImplementation<void>(
this, "addArchive", boost::python::ptr(profile), index, archiveName);
}
void DataArchivesWrapper::removeArchive(MOBase::IProfile* profile,
const QString& archiveName)
{
return basicWrapperFunctionImplementation<void>(
this, "removeArchive", boost::python::ptr(profile), archiveName);
}
/// end DataArchives Wrapper
/////////////////////////////
/// GamePlugins Wrapper
void GamePluginsWrapper::writePluginLists(const MOBase::IPluginList* pluginList)
{
return basicWrapperFunctionImplementation<void>(this, "writePluginLists",
boost::python::ptr(pluginList));
}
void GamePluginsWrapper::readPluginLists(MOBase::IPluginList* pluginList)
{
return basicWrapperFunctionImplementation<void>(this, "readPluginLists",
boost::python::ptr(pluginList));
}
QStringList GamePluginsWrapper::getLoadOrder()
{
return basicWrapperFunctionImplementation<QStringList>(this, "getLoadOrder");
}
bool GamePluginsWrapper::lightPluginsAreSupported()
{
return basicWrapperFunctionImplementation<bool>(this, "lightPluginsAreSupported");
}
/// end GamePlugins Wrapper
/////////////////////////////
/// LocalSavegames Wrapper
MappingType LocalSavegamesWrapper::mappings(const QDir& profileSaveDir) const
{
return basicWrapperFunctionImplementation<MappingType>(this, "mappings",
profileSaveDir);
}
bool LocalSavegamesWrapper::prepareProfile(MOBase::IProfile* profile)
{
return basicWrapperFunctionImplementation<bool>(this, "prepareProfile",
boost::python::ptr(profile));
}
/// end LocalSavegames Wrapper
/////////////////////////////
/// ModDataChecker Wrapper
ModDataChecker::CheckReturn ModDataCheckerWrapper::dataLooksValid(
std::shared_ptr<const MOBase::IFileTree> fileTree) const
{
return basicWrapperFunctionImplementation<CheckReturn>(this, "dataLooksValid",
fileTree);
}
std::shared_ptr<MOBase::IFileTree>
ModDataCheckerWrapper::fix(std::shared_ptr<MOBase::IFileTree> fileTree) const
{
return utils::clean_shared_ptr(basicWrapperFunctionImplementationWithDefault<
std::shared_ptr<MOBase::IFileTree>>(
this,
[](auto&&... args) {
return nullptr;
},
"fix", fileTree));
}
/// end ModDataChecker Wrapper
/////////////////////////////
/// ModDataContent Wrapper
std::vector<ModDataContent::Content> ModDataContentWrapper::getAllContents() const
{
return basicWrapperFunctionImplementation<std::vector<Content>>(this,
"getAllContents");
}
std::vector<int> ModDataContentWrapper::getContentsFor(
std::shared_ptr<const MOBase::IFileTree> fileTree) const
{
return basicWrapperFunctionImplementation<std::vector<int>>(this, "getContentsFor",
fileTree);
}
/// end ModDataContent Wrapper
/////////////////////////////
/// SaveGameInfo Wrapper
SaveGameInfoWrapper::MissingAssets
SaveGameInfoWrapper::getMissingAssets(MOBase::ISaveGame const& save) const
{
return basicWrapperFunctionImplementation<SaveGameInfoWrapper::MissingAssets>(
this, "getMissingAssets", boost::ref(save));
}
MOBase::ISaveGameInfoWidget*
SaveGameInfoWrapper::getSaveGameWidget(QWidget* parent) const
{
return basicWrapperFunctionImplementation<MOBase::ISaveGameInfoWidget*>(
this, m_SaveGameWidget, "getSaveGameWidget", parent);
}
/// end SaveGameInfo Wrapper
/////////////////////////////
/// ScriptExtender Wrapper
QString ScriptExtenderWrapper::BinaryName() const
{
return basicWrapperFunctionImplementation<QString>(this, "BinaryName");
}
QString ScriptExtenderWrapper::PluginPath() const
{
return basicWrapperFunctionImplementation<QString>(this, "PluginPath");
}
QString ScriptExtenderWrapper::loaderName() const
{
return basicWrapperFunctionImplementation<QString>(this, "loaderName");
}
QString ScriptExtenderWrapper::loaderPath() const
{
return basicWrapperFunctionImplementation<QString>(this, "loaderPath");
}
QString ScriptExtenderWrapper::savegameExtension() const
{
return basicWrapperFunctionImplementation<QString>(this, "savegameExtension");
}
bool ScriptExtenderWrapper::isInstalled() const
{
return basicWrapperFunctionImplementation<bool>(this, "isInstalled");
}
QString ScriptExtenderWrapper::getExtenderVersion() const
{
return basicWrapperFunctionImplementation<QString>(this, "getExtenderVersion");
}
WORD ScriptExtenderWrapper::getArch() const
{
return basicWrapperFunctionImplementation<WORD>(this, "getArch");
}
/// end ScriptExtender Wrapper
/////////////////////////////
/// UnmanagedMods Wrapper
QStringList UnmanagedModsWrapper::mods(bool onlyOfficial) const
{
return basicWrapperFunctionImplementation<QStringList>(this, "mods", onlyOfficial);
}
QString UnmanagedModsWrapper::displayName(const QString& modName) const
{
return basicWrapperFunctionImplementation<QString>(this, "displayName", modName);
}
QFileInfo UnmanagedModsWrapper::referenceFile(const QString& modName) const
{
return basicWrapperFunctionImplementation<QFileInfo>(this, "referenceFile",
modName);
}
QStringList UnmanagedModsWrapper::secondaryFiles(const QString& modName) const
{
return basicWrapperFunctionImplementation<QStringList>(this, "secondaryFiles",
modName);
}
/// end UnmanagedMods Wrapper
/////////////////////////////
game_features_map_from_python::game_features_map_from_python()
{
boost::python::converter::registry::push_back(
&convertible, &construct,
boost::python::type_id<std::map<std::type_index, std::any>>());
}
void* game_features_map_from_python::convertible(PyObject* objPtr)
{
return PyDict_Check(objPtr) ? objPtr : nullptr;
}
template <typename T>
void insertGameFeature(std::map<std::type_index, std::any>& map,
const boost::python::object& pyObject)
{
map[std::type_index(typeid(T))] = boost::python::extract<T*>(pyObject)();
}
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<
std::map<std::type_index, std::any>>*)data)
->storage.bytes;
std::map<std::type_index, std::any>* result =
new (storage) std::map<std::type_index, std::any>();
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];
boost::python::object pyValue = source[pyKey];
boost::mp11::mp_for_each<
// Must user pointers because mp_for_each construct object:
boost::mp11::mp_transform<std::add_pointer_t, MpGameFeaturesList>>(
[&](auto* pt) {
using T = std::remove_pointer_t<decltype(pt)>;
boost::python::extract<T*> extract(pyValue);
if (extract.check()) {
(*result)[std::type_index(typeid(T))] = extract();
}
});
}
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_<BSAInvalidationWrapper, boost::noncopyable>("BSAInvalidation")
.def("isInvalidationBSA",
bpy::pure_virtual(&BSAInvalidation::isInvalidationBSA), bpy::arg("name"))
.def("deactivate", bpy::pure_virtual(&BSAInvalidation::deactivate),
bpy::arg("profile"))
.def("activate", bpy::pure_virtual(&BSAInvalidation::activate),
bpy::arg("profile"));
bpy::class_<DataArchivesWrapper, boost::noncopyable>("DataArchives")
.def("vanillaArchives", bpy::pure_virtual(&DataArchives::vanillaArchives))
.def("archives", bpy::pure_virtual(&DataArchives::archives),
bpy::arg("profile"))
.def("addArchive", bpy::pure_virtual(&DataArchives::addArchive),
(bpy::arg("profile"), "index", "name"))
.def("removeArchive", bpy::pure_virtual(&DataArchives::removeArchive),
(bpy::arg("profile"), "name"));
bpy::class_<GamePluginsWrapper, boost::noncopyable>("GamePlugins")
.def("writePluginLists", bpy::pure_virtual(&GamePlugins::writePluginLists),
bpy::arg("plugin_list"))
.def("readPluginLists", bpy::pure_virtual(&GamePlugins::readPluginLists),
bpy::arg("plugin_list"))
.def("getLoadOrder", bpy::pure_virtual(&GamePlugins::getLoadOrder))
.def("lightPluginsAreSupported",
bpy::pure_virtual(&GamePlugins::lightPluginsAreSupported));
bpy::class_<LocalSavegamesWrapper, boost::noncopyable>("LocalSavegames")
.def("mappings", bpy::pure_virtual(&LocalSavegames::mappings),
bpy::arg("profile_save_dir"))
.def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile),
bpy::arg("profile"));
auto modDataCheckerClass =
bpy::class_<ModDataCheckerWrapper, boost::noncopyable>("ModDataChecker");
{
bpy::scope scope = modDataCheckerClass;
bpy::enum_<ModDataChecker::CheckReturn>("CheckReturn")
.value("INVALID", ModDataChecker::CheckReturn::INVALID)
.value("FIXABLE", ModDataChecker::CheckReturn::FIXABLE)
.value("VALID", ModDataChecker::CheckReturn::VALID)
.export_values();
modDataCheckerClass
.def("dataLooksValid", bpy::pure_virtual(&ModDataChecker::dataLooksValid),
bpy::arg("filetree"))
.def("fix", bpy::pure_virtual(&ModDataChecker::fix), bpy::arg("filetree"));
}
{
bpy::scope scope =
bpy::class_<ModDataContentWrapper, boost::noncopyable>("ModDataContent")
.def("getAllContents",
bpy::pure_virtual(&ModDataContent::getAllContents))
.def("getContentsFor",
bpy::pure_virtual(&ModDataContent::getContentsFor),
bpy::arg("filetree"));
bpy::class_<ModDataContent::Content>(
"Content",
bpy::init<int, QString, QString, bpy::optional<bool>>(
(bpy::arg("id"), "name", "icon", bpy::arg("filter_only") = false)))
.add_property("id", &ModDataContent::Content::id)
.add_property("name", &ModDataContent::Content::name)
.add_property("icon", &ModDataContent::Content::icon)
.def("isOnlyForFilter", &ModDataContent::Content::isOnlyForFilter);
}
bpy::class_<SaveGameInfoWrapper, boost::noncopyable>("SaveGameInfo")
.def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets),
bpy::arg("save"))
.def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget),
bpy::return_value_policy<bpy::manage_new_object>(), bpy::arg("parent"),
"[optional]");
bpy::class_<ScriptExtenderWrapper, boost::noncopyable>("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("savegameExtension", bpy::pure_virtual(&ScriptExtender::savegameExtension))
.def("isInstalled", bpy::pure_virtual(&ScriptExtender::isInstalled))
.def("getExtenderVersion",
bpy::pure_virtual(&ScriptExtender::getExtenderVersion))
.def("getArch", bpy::pure_virtual(&ScriptExtender::getArch));
bpy::class_<UnmanagedModsWrapper, boost::noncopyable>("UnmanagedMods")
.def("mods", bpy::pure_virtual(&UnmanagedMods::mods), bpy::arg("official_only"))
.def("displayName", bpy::pure_virtual(&UnmanagedMods::displayName),
bpy::arg("mod_name"))
.def("referenceFile", bpy::pure_virtual(&UnmanagedMods::referenceFile),
bpy::arg("mod_name"))
.def("secondaryFiles", bpy::pure_virtual(&UnmanagedMods::secondaryFiles),
bpy::arg("mod_name"));
}
-158
View File
@@ -1,158 +0,0 @@
#ifndef GAMEFEATURESWRAPPERS_H
#define GAMEFEATURESWRAPPERS_H
#include <map>
#include <bsainvalidation.h>
#include <dataarchives.h>
#include <gameplugins.h>
#include <localsavegames.h>
#include <moddatachecker.h>
#include <moddatacontent.h>
#include <savegameinfo.h>
#include <scriptextender.h>
#include <unmanagedmods.h>
// this might need turning off if Q_MOC_RUN is defined
#include <boost/mp11.hpp>
#include <boost/python.hpp>
// This is a simple MPL list that contains all the game features in one place:
using MpGameFeaturesList =
boost::mp11::mp_list<BSAInvalidation, DataArchives, GamePlugins, LocalSavegames,
ModDataChecker, ModDataContent, SaveGameInfo, ScriptExtender,
UnmanagedMods>;
/////////////////////////////
/// Wrapper declarations
class BSAInvalidationWrapper : public BSAInvalidation,
public boost::python::wrapper<BSAInvalidation> {
public:
static constexpr const char* className = "BSAInvalidationWrapper";
using boost::python::wrapper<BSAInvalidation>::get_override;
virtual bool isInvalidationBSA(const QString& bsaName) override;
virtual void deactivate(MOBase::IProfile* profile) override;
virtual void activate(MOBase::IProfile* profile) override;
virtual bool prepareProfile(MOBase::IProfile* profile) override;
};
class DataArchivesWrapper : public DataArchives,
public boost::python::wrapper<DataArchives> {
public:
static constexpr const char* className = "DataArchivesWrapper";
using boost::python::wrapper<DataArchives>::get_override;
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<GamePlugins> {
public:
static constexpr const char* className = "GamePluginsWrapper";
using boost::python::wrapper<GamePlugins>::get_override;
virtual void writePluginLists(const MOBase::IPluginList* pluginList) override;
virtual void readPluginLists(MOBase::IPluginList* pluginList) override;
virtual QStringList getLoadOrder() override;
virtual bool lightPluginsAreSupported() override;
};
class LocalSavegamesWrapper : public LocalSavegames,
public boost::python::wrapper<LocalSavegames> {
public:
static constexpr const char* className = "LocalSavegamesWrapper";
using boost::python::wrapper<LocalSavegames>::get_override;
virtual MappingType mappings(const QDir& profileSaveDir) const override;
virtual bool prepareProfile(MOBase::IProfile* profile) override;
};
class ModDataCheckerWrapper : public ModDataChecker,
public boost::python::wrapper<ModDataChecker> {
public:
static constexpr const char* className = "ModDataCheckerWrapper";
using boost::python::wrapper<ModDataChecker>::get_override;
virtual CheckReturn
dataLooksValid(std::shared_ptr<const MOBase::IFileTree> fileTree) const override;
virtual std::shared_ptr<MOBase::IFileTree>
fix(std::shared_ptr<MOBase::IFileTree> fileTree) const override;
};
class ModDataContentWrapper : public ModDataContent,
public boost::python::wrapper<ModDataContent> {
public:
static constexpr const char* className = "ModDataContentWrapper";
using boost::python::wrapper<ModDataContent>::get_override;
virtual std::vector<Content> getAllContents() const override;
virtual std::vector<int>
getContentsFor(std::shared_ptr<const MOBase::IFileTree> fileTree) const override;
};
class SaveGameInfoWrapper : public SaveGameInfo,
public boost::python::wrapper<SaveGameInfo> {
public:
static constexpr const char* className = "SaveGameInfoWrapper";
using boost::python::wrapper<SaveGameInfo>::get_override;
virtual MissingAssets
getMissingAssets(MOBase::ISaveGame const& save) const override;
virtual MOBase::ISaveGameInfoWidget*
getSaveGameWidget(QWidget* parent = 0) const override;
private:
// We need to keep the python objects alive:
mutable std::map<QString, boost::python::object> m_SaveGames;
mutable boost::python::object m_SaveGameWidget;
};
class ScriptExtenderWrapper : public ScriptExtender,
public boost::python::wrapper<ScriptExtender> {
public:
static constexpr const char* className = "ScriptExtenderWrapper";
using boost::python::wrapper<ScriptExtender>::get_override;
virtual QString BinaryName() const override;
virtual QString PluginPath() const override;
virtual QString loaderName() const override;
virtual QString loaderPath() const override;
virtual QString savegameExtension() 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<UnmanagedMods> {
public:
static constexpr const char* className = "UnmanagedModsWrapper";
using boost::python::wrapper<UnmanagedMods>::get_override;
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
-12
View File
@@ -1,12 +0,0 @@
#include "gilock.h"
GILock::GILock()
{
m_State = PyGILState_Ensure();
}
GILock::~GILock()
{
PyErr_Clear();
PyGILState_Release(m_State);
}
-17
View File
@@ -1,17 +0,0 @@
#ifndef GILOCK_H
#define GILOCK_H
#ifndef Q_MOC_RUN
#include <boost/python.hpp>
#endif // Q_MOC_RUN
class GILock {
public:
GILock();
~GILock();
private:
PyGILState_STATE m_State;
};
#endif // GILOCK_H
File diff suppressed because it is too large Load Diff
-299
View File
@@ -1,299 +0,0 @@
#ifndef PROXYPLUGINWRAPPERS_H
#define PROXYPLUGINWRAPPERS_H
#include <iplugin.h>
#include <iplugindiagnose.h>
#include <ipluginfilemapper.h>
#include <iplugingame.h>
#include <iplugininstallercustom.h>
#include <iplugininstallersimple.h>
#include <ipluginmodpage.h>
#include <ipluginpreview.h>
#include <iplugintool.h>
#ifndef Q_MOC_RUN
#include <boost/preprocessor/control/expr_if.hpp>
#include <boost/python.hpp>
#endif
// The wrapper for IPluginGame cannot override requirements or enabledByDefault since
// they're final, so we need to be able to exclude the declarations.
#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(include_requirements) \
public: \
virtual bool init(MOBase::IOrganizer* moInfo) override; \
virtual QString name() const override; \
virtual QString localizedName() const override; \
virtual QString master() const override; \
virtual QString author() const override; \
virtual QString description() const override; \
virtual MOBase::VersionInfo version() const override; \
virtual QList<MOBase::PluginSetting> settings() const override; \
QString localizedName_Default() const; \
QString master_Default() const; \
BOOST_PP_EXPR_IF( \
include_requirements, \
virtual std::vector<std::shared_ptr<const MOBase::IPluginRequirement>> \
requirements() const override; \
std::vector<std::shared_ptr<const MOBase::IPluginRequirement>> \
requirements_Default() const; \
virtual bool enabledByDefault() const override; \
bool enabledByDefault_Default() const;)
#define COMMON_I_PLUGIN_WRAPPER_DECLARATIONS \
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(1)
// Even though the base interface is not a 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 IPluginWrapper : public QObject,
public MOBase::IPlugin,
public boost::python::wrapper<MOBase::IPlugin> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginWrapper";
using boost::python::wrapper<MOBase::IPlugin>::get_override;
};
// 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 IPluginDiagnoseWrapper : public QObject,
public MOBase::IPluginDiagnose,
public MOBase::IPlugin,
public boost::python::wrapper<MOBase::IPluginDiagnose> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginDiagnose)
public:
static constexpr const char* className = "IPluginDiagnoseWrapper";
using boost::python::wrapper<MOBase::IPluginDiagnose>::get_override;
// Bring in public scope:
using IPluginDiagnose::invalidate;
virtual std::vector<unsigned int> 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;
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
};
// 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<MOBase::IPluginFileMapper> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginFileMapper)
public:
static constexpr const char* className = "IPluginFileMapperWrapper";
using boost::python::wrapper<MOBase::IPluginFileMapper>::get_override;
virtual MappingType mappings() const override;
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
};
class IPluginGameWrapper : public MOBase::IPluginGame,
public boost::python::wrapper<MOBase::IPluginGame> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame)
public:
static constexpr const char* className = "IPluginGameWrapper";
using boost::python::wrapper<MOBase::IPluginGame>::get_override;
virtual void detectGame() override;
virtual QString gameName() const override;
virtual void initializeProfile(const QDir& directory,
ProfileSettings settings) const override;
virtual std::vector<std::shared_ptr<const MOBase::ISaveGame>>
listSaves(QDir folder) 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<MOBase::ExecutableInfo> executables() const override;
virtual QList<MOBase::ExecutableForcedLoadSetting>
executableForcedLoads() 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 primarySources() 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;
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS_IMPL(0)
protected:
// Apparently, Python developers interpret an underscore in a function name as it
// being protected
virtual std::map<std::type_index, std::any> featureList() const override;
// Thankfully, the default implementation of the templated 'T *feature()' function
// should allow us to get away without overriding it.
};
#define COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS \
public: \
using IPluginInstaller::parentWidget; \
using IPluginInstaller::manager; \
virtual unsigned int priority() const override; \
virtual bool isManualInstaller() const override; \
virtual void onInstallationStart(QString const& archive, bool reinstallation, \
MOBase::IModInterface* currentMod) override; \
void onInstallationStart_Default(QString const& archive, bool reinstallation, \
MOBase::IModInterface* currentMod) \
{ \
return IPluginInstaller::onInstallationStart(archive, reinstallation, \
currentMod); \
} \
virtual void onInstallationEnd(EInstallResult result, \
MOBase::IModInterface* newMod) override; \
void onInstallationEnd_Default(EInstallResult result, \
MOBase::IModInterface* newMod) \
{ \
return IPluginInstaller::onInstallationEnd(result, newMod); \
} \
virtual bool isArchiveSupported(std::shared_ptr<const MOBase::IFileTree> tree) \
const override;
class IPluginInstallerSimpleWrapper
: public MOBase::IPluginInstallerSimple,
public boost::python::wrapper<MOBase::IPluginInstallerSimple> {
Q_OBJECT
Q_INTERFACES(
MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerSimple)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginInstallerSimpleWrapper";
using boost::python::wrapper<MOBase::IPluginInstallerSimple>::get_override;
virtual EInstallResult install(MOBase::GuessedValue<QString>& modName,
std::shared_ptr<MOBase::IFileTree>& tree,
QString& version, int& nexusID) override;
};
class IPluginInstallerCustomWrapper
: public MOBase::IPluginInstallerCustom,
public boost::python::wrapper<MOBase::IPluginInstallerCustom> {
Q_OBJECT
Q_INTERFACES(
MOBase::IPlugin MOBase::IPluginInstaller MOBase::IPluginInstallerCustom)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
COMMON_I_PLUGIN_INSTALLER_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginInstallerCustomWrapper";
using boost::python::wrapper<MOBase::IPluginInstallerCustom>::get_override;
virtual bool isArchiveSupported(const QString& archiveName) const override;
virtual std::set<QString> supportedExtensions() const override;
virtual EInstallResult install(MOBase::GuessedValue<QString>& modName,
QString gameName, const QString& archiveName,
const QString& version, int modID) override;
};
class IPluginModPageWrapper : public MOBase::IPluginModPage,
public boost::python::wrapper<MOBase::IPluginModPage> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginModPage)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginModPageWrapper";
using boost::python::wrapper<MOBase::IPluginModPage>::get_override;
// Bring in public scope:
using IPluginModPage::parentWidget;
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;
void setParentWidget_Default(QWidget* parent)
{
IPluginModPage::setParentWidget(parent);
}
};
class IPluginPreviewWrapper : public MOBase::IPluginPreview,
public boost::python::wrapper<MOBase::IPluginPreview> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginPreview)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginPreviewWrapper";
using boost::python::wrapper<MOBase::IPluginPreview>::get_override;
virtual std::set<QString> supportedExtensions() const override;
virtual QWidget* genFilePreview(const QString& fileName,
const QSize& maxSize) const override;
};
class IPluginToolWrapper : public MOBase::IPluginTool,
public boost::python::wrapper<MOBase::IPluginTool> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginTool)
COMMON_I_PLUGIN_WRAPPER_DECLARATIONS
public:
static constexpr const char* className = "IPluginToolWrapper";
using boost::python::wrapper<MOBase::IPluginTool>::get_override;
// Bring in public scope:
using IPluginTool::parentWidget;
virtual QString displayName() const override;
virtual QString tooltip() const override;
virtual QIcon icon() const override;
virtual void setParentWidget(QWidget* parent) override;
void setParentWidget_Default(QWidget* parent)
{
IPluginTool::setParentWidget(parent);
}
public Q_SLOTS:
virtual void display() const override;
};
#endif // PROXYPLUGINWRAPPERS_H
@@ -0,0 +1,50 @@
#ifndef PYTHON_PYBIND11_QT_DETAILS_QLIST_HPP
#define PYTHON_PYBIND11_QT_DETAILS_QLIST_HPP
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
namespace pybind11::detail::qt {
// helper class for QList to construct from any proper iterable
//
template <typename Type, typename Value>
struct qlist_caster {
using value_conv = make_caster<Value>;
bool load(handle src, bool convert)
{
if (!isinstance<iterable>(src) || isinstance<bytes>(src) ||
isinstance<str>(src)) {
return false;
}
auto s = reinterpret_borrow<iterable>(src);
value.clear();
if (isinstance<sequence>(src)) {
value.reserve(s.cast<sequence>().size());
}
for (auto it : s) {
value_conv conv;
if (!conv.load(it, convert)) {
return false;
}
value.push_back(cast_op<Value&&>(std::move(conv)));
}
return true;
}
template <typename T>
static handle cast(T&& src, return_value_policy policy, handle parent)
{
return list_caster<QList<Value>, Value>{}.cast(std::forward<T>(src), policy,
parent);
}
PYBIND11_TYPE_CASTER(Type, const_name("Iterable[") + value_conv::name +
const_name("]"));
};
} // namespace pybind11::detail::qt
#endif
@@ -11,45 +11,45 @@
// this needs to be included here to get proper QVariantList and QVariantMap
#include "details/pybind11_qt_qmap.h"
#include "pybind11_qt_basic.h"
#include "details/pybind11_qt_qlist.h"
namespace pybind11::detail {
// QList
//
template <class T>
struct type_caster<QList<T>> : list_caster<QList<T>, T> {
struct type_caster<QList<T>> : qt::qlist_caster<QList<T>, T> {
};
// QSet
//
// template <class T>
// struct type_caster<QSet<T>> : set_caster<QList<T>, T> {
// };
template <class T>
struct type_caster<QSet<T>> : set_caster<QList<T>, T> {
};
// QMap
//
// template <class K, class V>
// struct type_caster<QMap<K, V>> : qt::qmap_caster<QMap<K, V>, K, V> {
// };
template <class K, class V>
struct type_caster<QMap<K, V>> : qt::qmap_caster<QMap<K, V>, K, V> {
};
// QStringList
//
template <>
struct type_caster<QStringList> : list_caster<QStringList, QString> {
struct type_caster<QStringList> : qt::qlist_caster<QStringList, QString> {
};
// QVariantList
//
// template <>
// struct type_caster<QVariantList> : list_caster<QVariantList, QVariant> {
// };
template <>
struct type_caster<QVariantList> : qt::qlist_caster<QVariantList, QVariant> {
};
// QVariantMap
//
// template <>
// struct type_caster<QVariantMap>
// : qt::qmap_caster<QVariantMap, QString, QVariant> {
// };
template <>
struct type_caster<QVariantMap> : qt::qmap_caster<QVariantMap, QString, QVariant> {
};
} // namespace pybind11::detail
-17
View File
@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>QObject</name>
<message>
<location filename="error.h" line="68"/>
<source>An unexpected C++ exception was thrown in python code.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="error.h" line="117"/>
<source>An unknown exception was thrown in python code.</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
@@ -1,202 +0,0 @@
#ifndef PYTHONWRAPPERUTILITIES_H
#define PYTHONWRAPPERUTILITIES_H
#include <functional>
#include <boost/python.hpp>
#include <log.h>
#include <utility.h>
#include "error.h"
#include "gilock.h"
// #include "pybind11_qt/pybind11_qt.h"
namespace details {
/**
* @brief Common stuffs for all basicWrapperFunction methods.
*/
template <class ReturnType, class WrapperTypePtr, class Fn, class... Args>
ReturnType wrapperFunctionImplementation(WrapperTypePtr wrapper, bool apiTransfer,
Fn fn, boost::python::object* objPtr,
const char* methodName, Args... args)
{
boost::python::override implementation = [&]() {
GILock lock;
return wrapper->get_override(methodName);
}();
if (!implementation) {
if constexpr (std::is_same_v<Fn, std::nullptr_t>) {
throw pyexcept::MissingImplementation(wrapper->className, methodName);
}
else {
return std::invoke(fn, wrapper, args...);
}
}
GILock lock;
try {
boost::python::object result = implementation(args...);
if (objPtr) {
*objPtr = result;
}
else if (apiTransfer) {
// pybind11::detail::qt::sipAPI()->api_transfer_to(result.ptr(),
// Py_None);
}
if constexpr (!std::is_same_v<ReturnType, void>) {
return boost::python::extract<ReturnType>(result)();
}
}
catch (const boost::python::error_already_set&) {
throw pyexcept::PythonError("");
}
catch (...) {
throw pyexcept::UnknownException();
}
}
} // namespace details
/**
* @brief Call the given method on the wrapper with the given arguments, with
* proper exception handling.
*
* @param wrapper The wrapper object to use to retrieve the python method. Must
* have a publicly available `className` attribute.
* @param methodName The name of the method.
* @param args... Arguments for the method.
*
* @return the result of calling the given Python method on the wrapper.
*
* @throw pyexcept::MissingImplementation if the method does not exist.
* @throw pyexcept::PythonError if an error occurs while executing the python
* method.
* @throw pyexecpt::UnknownException if an unknown error occurs.
*/
template <class ReturnType, class WrapperType, class... Args>
ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper,
const char* methodName, Args... args)
{
return details::wrapperFunctionImplementation<ReturnType>(
wrapper, false, nullptr, nullptr, methodName, args...);
}
/**
* @brief Call the given method on the wrapper with the given arguments, with
* proper exception handling, and store the intermediate result in the given
* python object.
*
* @param wrapper The wrapper object to use to retrieve the python method. Must
* have a publicly available `className` attribute.
* @param ref Python object to which the result of `get_override()` should be
* stored.
* @param methodName The name of the method.
* @param args... Arguments for the method.
*
* @return the result of calling the given Python method on the wrapper.
*
* @throw pyexcept::MissingImplementation if the method does not exist.
* @throw pyexcept::PythonError if an error occurs while executing the python
* method.
* @throw pyexecpt::UnknownException if an unknown error occurs.
*/
template <class ReturnType, class WrapperType, class... Args>
ReturnType basicWrapperFunctionImplementation(const WrapperType* wrapper,
boost::python::object& ref,
const char* methodName, Args... args)
{
return details::wrapperFunctionImplementation<ReturnType>(
wrapper, false, nullptr, &ref, methodName, args...);
}
/**
* @brief Call the given method on the wrapper with the given arguments, with
* proper exception handling, and transfer the responsibility of the returned
* object to the C++ side.
*
* @param wrapper The wrapper object to use to retrieve the python method. Must
* have a publicly available `className` attribute.
* @param methodName The name of the method.
* @param args... Arguments for the method.
*
* @return the result of calling the given Python method on the wrapper.
*
* @throw pyexcept::MissingImplementation if the method does not exist.
* @throw pyexcept::PythonError if an error occurs while executing the python
* method.
* @throw pyexecpt::UnknownException if an unknown error occurs.
*/
template <class ReturnType, class WrapperType, class... Args>
ReturnType wrapperFunctionImplementationWithApiTransfer(const WrapperType* wrapper,
const char* methodName,
Args... args)
{
return details::wrapperFunctionImplementation<ReturnType>(
wrapper, true, nullptr, nullptr, methodName, args...);
}
/**
* @brief Call the given method on the wrapper with the given arguments, with
* proper exception handling, falling back to the given function if the method
* does not exist.
*
* @param wrapper The wrapper object to use to retrieve the python method. Must
* have a publicly available `className` attribute.
* @param fn The function to call if the method does not exists.
* @param methodName The name of the method.
* @param args... Arguments for the method.
*
* Note: `fn` does not have to be a member-function of `wrapper` but
* `std::invoke(fn, wrapper, args...)` must be valid.
*
* @return the result of calling the given Python method on the wrapper.
*
* @throw pyexcept::PythonError if an error occurs while executing the python
* method.
* @throw pyexecpt::UnknownException if an unknown error occurs.
*/
template <class ReturnType, class WrapperTypePtr, class Fn, class... Args>
ReturnType basicWrapperFunctionImplementationWithDefault(WrapperTypePtr wrapper, Fn fn,
const char* methodName,
Args... args)
{
return details::wrapperFunctionImplementation<ReturnType>(
wrapper, false, fn, nullptr, methodName, args...);
}
/**
* @brief Call the given method on the wrapper with the given arguments, with
* proper exception handling, and store the intermediate result in the given
* python object, falling back to the given function if the method does not
* exist.
*
* @param wrapper The wrapper object to use to retrieve the python method. Must
* have a publicly available `className` attribute.
* @param fn The function to call if the method does not exists.
* @param ref Python object to which the result of `get_override()` should be
* stored.
* @param methodName The name of the method.
* @param args... Arguments for the method.
*
* Note: `fn` does not have to be a member-function of `wrapper` but
* `std::invoke(fn, wrapper, args...)` must be valid.
*
* @return the result of calling the given Python method on the wrapper.
*
* @throw pyexcept::PythonError if an error occurs while executing the python
* method.
* @throw pyexecpt::UnknownException if an unknown error occurs.
*/
template <class ReturnType, class WrapperType, class Fn, class... Args>
ReturnType
basicWrapperFunctionImplementationWithDefault(const WrapperType* wrapper, Fn fn,
boost::python::object& ref,
const char* methodName, Args... args)
{
return details::wrapperFunctionImplementation<ReturnType>(wrapper, false, fn, &ref,
methodName, args...);
}
#endif // PYTHONWRAPPERUTILITIES_H
-139
View File
@@ -1,139 +0,0 @@
#ifndef PYTHONRUNNER_SHARED_PTR_CONVERTER_H
#define PYTHONRUNNER_SHARED_PTR_CONVERTER_H
#include <boost/python.hpp>
#include "error.h"
#include "gilock.h"
namespace utils {
// Shared pointers are handled in a special way by Boost.Python since they hold
// the wrapped Python object and only release it when the ref counter of the shared
// ptr drops to 0 using shared_ptr_deleter.
//
// Unfortunately for us, this will happen outside of the Python proxy for some
// objects and thus without the GIL lock, making everything crash, so we need a
// custom deleter that holds the GIL while releasing the lock.
//
// Note that this is only useful for Python -> C++ conversion, and without this,
// Boost will automatically wrapped the pointer. The C++ -> Python conversion is
// handled separately by boost::python::register_ptr_to_python.
//
// This is an open Boost.Python problem: https://github.com/boostorg/python/pull/11
template <class SharedPtr>
struct shared_ptr_from_python;
namespace details {
struct shared_ptr_deleter_with_gil_lock
: boost::python::converter::shared_ptr_deleter {
using shared_ptr_deleter::shared_ptr_deleter;
void operator()(void const* o)
{
GILock lock;
shared_ptr_deleter::operator()(o);
}
};
template <class SharedPtr>
struct shared_ptr_void;
template <class T>
struct shared_ptr_void<std::shared_ptr<T>> {
using type = std::shared_ptr<void>;
};
template <class T>
struct shared_ptr_void<boost::shared_ptr<T>> {
using type = boost::shared_ptr<void>;
};
template <class SharedPtr>
using shared_ptr_void_t = typename shared_ptr_void<SharedPtr>::type;
} // namespace details
template <class SharedPtr>
struct shared_ptr_from_python {
using T = typename SharedPtr::element_type;
shared_ptr_from_python()
{
using namespace boost::python;
converter::registry::insert(
&convertible, &construct, type_id<SharedPtr>()
#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
,
&converter::expected_from_python_type_direct<T>::get_pytype
#endif
);
}
private:
static void* convertible(PyObject* p)
{
if (p == Py_None)
return p;
return boost::python::converter::get_lvalue_from_python(
p, boost::python::converter::registered<T>::converters);
}
static void
construct(PyObject* source,
boost::python::converter::rvalue_from_python_stage1_data* data)
{
using namespace boost::python;
void* const storage =
((converter::rvalue_from_python_storage<SharedPtr>*)data)
->storage.bytes;
// Deal with the "None" case.
if (data->convertible == source)
new (storage) SharedPtr();
else {
details::shared_ptr_void_t<SharedPtr> hold_convertible_ref_count(
(void*)0, details::shared_ptr_deleter_with_gil_lock(
handle<>(borrowed(source))));
// use aliasing constructor
new (storage) SharedPtr(hold_convertible_ref_count,
static_cast<T*>(data->convertible));
}
data->convertible = storage;
}
};
// release the bpy::object associated with the deleter of the given shared_ptr,
// if the given shared_ptr has a Boost.Python deleter
//
// this should only be used when returning from Python objects that have been
// created on the C++ side, e.g. if IFileTree.createOrphanTree() from Python and
// then return the tree
//
// for reason yet to be known, Boost.Python had a custom deleter in this case that
// tries to delete the bpy::object and fails, so we have to release the object
// manually
//
template <class SharedPtr>
SharedPtr clean_shared_ptr(SharedPtr&& ptr)
{
if (auto* d = get_deleter<boost::python::converter::shared_ptr_deleter>(ptr);
d != nullptr) {
// we cannot do a proper reset() here, even with the GIL lock, for unknown
// reason, so we only release
//
// this might create lost references to Python object but this should not
// happen too often so hopefully it's not a big issue
//
d->owner.release();
}
return ptr;
}
} // namespace utils
#endif
@@ -1,5 +1,7 @@
#include "wrappers.h"
#include <tuple>
#include <pybind11/functional.h>
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
@@ -7,8 +9,18 @@
#include "../pybind11_qt/pybind11_qt.h"
#include <ipluginlist.h>
#include <isavegameinfowidget.h>
#include <bsainvalidation.h>
#include <dataarchives.h>
#include <gameplugins.h>
#include <localsavegames.h>
#include <moddatachecker.h>
#include <moddatacontent.h>
#include <savegameinfo.h>
#include <scriptextender.h>
#include <unmanagedmods.h>
#include "pyfiletree.h"
@@ -18,6 +30,85 @@ using namespace pybind11::literals;
namespace mo2::python {
class PyBSAInvalidation : public BSAInvalidation {
public:
bool isInvalidationBSA(const QString& bsaName) override
{
PYBIND11_OVERRIDE_PURE(bool, BSAInvalidation, isInvalidationBSA, bsaName);
}
void deactivate(MOBase::IProfile* profile) override
{
PYBIND11_OVERRIDE_PURE(void, BSAInvalidation, deactivate, profile);
}
void activate(MOBase::IProfile* profile) override
{
PYBIND11_OVERRIDE_PURE(void, BSAInvalidation, activate, profile);
}
bool prepareProfile(MOBase::IProfile* profile) override
{
PYBIND11_OVERRIDE_PURE(bool, BSAInvalidation, prepareProfile, profile);
}
};
class PyDataArchives : public DataArchives {
public:
QStringList vanillaArchives() const override
{
PYBIND11_OVERRIDE_PURE(QStringList, DataArchives, vanillaArchives, );
}
QStringList archives(const MOBase::IProfile* profile) const override
{
PYBIND11_OVERRIDE_PURE(QStringList, DataArchives, archives, profile);
}
void addArchive(MOBase::IProfile* profile, int index,
const QString& archiveName) override
{
PYBIND11_OVERRIDE_PURE(void, DataArchives, addArchive, profile, index,
archiveName);
}
void removeArchive(MOBase::IProfile* profile,
const QString& archiveName) override
{
PYBIND11_OVERRIDE_PURE(void, DataArchives, removeArchive, profile,
archiveName);
}
};
class PyGamePlugins : public GamePlugins {
public:
void writePluginLists(const MOBase::IPluginList* pluginList) override
{
PYBIND11_OVERRIDE_PURE(void, GamePlugins, writePluginLists, pluginList);
}
void readPluginLists(MOBase::IPluginList* pluginList) override
{
// TODO: cannot update plugin list or create one from Python so this is
// useless
PYBIND11_OVERRIDE_PURE(void, GamePlugins, readPluginLists, pluginList);
}
QStringList getLoadOrder() override
{
PYBIND11_OVERRIDE_PURE(QStringList, GamePlugins, getLoadOrder, );
}
bool lightPluginsAreSupported() override
{
PYBIND11_OVERRIDE_PURE(bool, GamePlugins, lightPluginsAreSupported, );
}
};
class PyLocalSavegames : public LocalSavegames {
public:
MappingType mappings(const QDir& profileSaveDir) const override
{
PYBIND11_OVERRIDE_PURE(MappingType, LocalSavegames, mappings,
profileSaveDir);
}
bool prepareProfile(MOBase::IProfile* profile) override
{
PYBIND11_OVERRIDE_PURE(bool, LocalSavegames, prepareProfile, profile);
}
};
class PyModDataChecker : public ModDataChecker {
public:
CheckReturn
@@ -35,6 +126,37 @@ namespace mo2::python {
}
};
class PyModDataContent : public ModDataContent {
public:
std::vector<Content> getAllContents() const override
{
PYBIND11_OVERRIDE_PURE(std::vector<Content>, ModDataContent,
getAllContents, );
;
}
std::vector<int>
getContentsFor(std::shared_ptr<const MOBase::IFileTree> fileTree) const override
{
PYBIND11_OVERRIDE_PURE(std::vector<int>, ModDataContent, getContentsFor,
fileTree);
}
};
class PySaveGameInfo : public SaveGameInfo {
public:
MissingAssets getMissingAssets(MOBase::ISaveGame const& save) const override
{
PYBIND11_OVERRIDE_PURE(MissingAssets, SaveGameInfo, getMissingAssets,
&save);
}
ISaveGameInfoWidget* getSaveGameWidget(QWidget* parent = 0) const override
{
// TODO: transfer ownership
PYBIND11_OVERRIDE_PURE(ISaveGameInfoWidget*, SaveGameInfo,
getSaveGameWidget, parent);
}
};
class PyScriptExtender : public ScriptExtender {
public:
QString BinaryName() const override
@@ -78,8 +200,62 @@ namespace mo2::python {
}
};
class PyPyUnmanagedMods : public UnmanagedMods {
public:
QStringList mods(bool onlyOfficial) const override
{
PYBIND11_OVERRIDE_PURE(QStringList, UnmanagedMods, mods, onlyOfficial);
}
QString displayName(const QString& modName) const override
{
PYBIND11_OVERRIDE_PURE(QString, UnmanagedMods, displayName, modName);
}
QFileInfo referenceFile(const QString& modName) const override
{
PYBIND11_OVERRIDE_PURE(QFileInfo, UnmanagedMods, referenceFile, modName);
}
QStringList secondaryFiles(const QString& modName) const override
{
PYBIND11_OVERRIDE_PURE(QStringList, UnmanagedMods, secondaryFiles, modName);
}
};
void add_game_feature_bindings(pybind11::module_ m)
{
// BSAInvalidation
py::class_<BSAInvalidation, PyBSAInvalidation>(m, "BSAInvalidation")
.def(py::init<>())
.def("isInvalidationBSA", &BSAInvalidation::isInvalidationBSA, "name"_a)
.def("deactivate", &BSAInvalidation::deactivate, "profile"_a)
.def("activate", &BSAInvalidation::activate, "profile"_a);
// DataArchives
py::class_<DataArchives, PyDataArchives>(m, "DataArchives")
.def(py::init<>())
.def("vanillaArchives", &DataArchives::vanillaArchives)
.def("archives", &DataArchives::archives, "profile"_a)
.def("addArchive", &DataArchives::addArchive,
("profile"_a, "index", "name"))
.def("removeArchive", &DataArchives::removeArchive, "profile"_a, "name"_a);
// GamePlugins
py::class_<GamePlugins, PyGamePlugins>(m, "GamePlugins")
.def(py::init<>())
.def("writePluginLists", &GamePlugins::writePluginLists, "plugin_list"_a)
.def("readPluginLists", &GamePlugins::readPluginLists, "plugin_list"_a)
.def("getLoadOrder", &GamePlugins::getLoadOrder)
.def("lightPluginsAreSupported", &GamePlugins::lightPluginsAreSupported);
// LocalSavegames
py::class_<LocalSavegames, PyLocalSavegames>(m, "LocalSavegames")
.def(py::init<>())
.def("mappings", &LocalSavegames::mappings, "profile_save_dir"_a)
.def("prepareProfile", &LocalSavegames::prepareProfile, "profile"_a);
// ModDataChecker
py::class_<ModDataChecker, PyModDataChecker> pyModDataChecker(m,
@@ -95,6 +271,30 @@ namespace mo2::python {
.def("dataLooksValid", &ModDataChecker::dataLooksValid, "filetree"_a)
.def("fix", &ModDataChecker::fix, "filetree"_a);
// ModDataContent
py::class_<ModDataContent, PyModDataContent> pyModDataContent(m,
"ModDataContent");
py::class_<ModDataContent::Content>(pyModDataContent, "Content")
.def(py::init<int, QString, QString, bool>(), "id"_a, "name"_a, "icon"_a,
"filter_only"_a = false)
.def_property_readonly("id", &ModDataContent::Content::id)
.def_property_readonly("name", &ModDataContent::Content::name)
.def_property_readonly("icon", &ModDataContent::Content::icon)
.def("isOnlyForFilter", &ModDataContent::Content::isOnlyForFilter);
pyModDataContent.def(py::init<>())
.def("getAllContents", &ModDataContent::getAllContents)
.def("getContentsFor", &ModDataContent::getContentsFor, "filetree"_a);
// SaveGameInfo
py::class_<SaveGameInfo, PySaveGameInfo>(m, "SaveGameInfo")
.def(py::init<>())
.def("getMissingAssets", &SaveGameInfo::getMissingAssets, "save"_a)
.def("getSaveGameWidget", &SaveGameInfo::getSaveGameWidget,
py::return_value_policy::reference, "parent"_a, "[optional]");
// ScriptExtender
py::class_<ScriptExtender, PyScriptExtender>(m, "ScriptExtender")
@@ -107,6 +307,81 @@ namespace mo2::python {
.def("isInstalled", &ScriptExtender::isInstalled)
.def("getExtenderVersion", &ScriptExtender::getExtenderVersion)
.def("getArch", &ScriptExtender::getArch);
// UnmanagedMods
py::class_<UnmanagedMods, PyPyUnmanagedMods>(m, "UnmanagedMods")
.def(py::init<>())
.def("mods", &UnmanagedMods::mods, "official_only"_a)
.def("displayName", &UnmanagedMods::displayName, "mod_name"_a)
.def("referenceFile", &UnmanagedMods::referenceFile, "mod_name"_a)
.def("secondaryFiles", &UnmanagedMods::secondaryFiles, "mod_name"_a);
}
} // namespace mo2::python
namespace mo2::python {
class GameFeaturesHelper {
using GameFeatures = std::tuple<
// BSAInvalidation, DataArchives, GamePlugins, LocalSavegames,
ModDataChecker,
// ModDataContent, SaveGameInfo,
ScriptExtender
// , UnmanagedMods
>;
template <class F, std::size_t... Is>
static void helper(F&& f, std::index_sequence<Is...>)
{
(f(static_cast<std::tuple_element_t<Is, GameFeatures>*>(nullptr)), ...);
}
public:
// apply the function f on a null-pointer of type Feature* for each game
// feature
template <class F>
static void apply(F&& f)
{
helper(f, std::make_index_sequence<std::tuple_size_v<GameFeatures>>{});
}
};
pybind11::object extract_feature(IPluginGame const& game, pybind11::object type)
{
py::object py_feature = py::none();
GameFeaturesHelper::apply([&]<class Feature>(Feature* feature) {
if (py::type::of<Feature>().is(type)) {
py_feature = py::cast(game.feature<Feature>(),
py::return_value_policy::reference);
}
});
return py_feature;
}
pybind11::dict extract_feature_list(IPluginGame const& game)
{
// constructing a dict from class name to actual object
py::dict dict;
GameFeaturesHelper::apply([&]<class Feature>(Feature* feature) {
dict[py::type::of<Feature>()] =
py::cast(game.feature<Feature>(), py::return_value_policy::reference);
});
return dict;
}
std::map<std::type_index, std::any>
convert_feature_list(py::dict const& py_features)
{
std::map<std::type_index, std::any> features;
GameFeaturesHelper::apply([&]<class Feature>(Feature* feature) {
const auto py_type = py::type::of<Feature>();
if (py_features.contains(py_type)) {
features[std::type_index(typeid(Feature))] =
py_features[py_type].cast<Feature*>();
}
});
return features;
}
} // namespace mo2::python
+92 -179
View File
@@ -4,188 +4,103 @@
#include "pyplugins.h"
#include <bsainvalidation.h>
#include <dataarchives.h>
#include <gameplugins.h>
#include <localsavegames.h>
#include <moddatachecker.h>
#include <moddatacontent.h>
#include <savegameinfo.h>
#include <scriptextender.h>
#include <unmanagedmods.h>
namespace py = pybind11;
using namespace pybind11::literals;
using namespace MOBase;
namespace mo2::python {
class GameFeaturesHelper {
using GameFeatures = std::tuple<
// BSAInvalidation, DataArchives, GamePlugins, LocalSavegames,
ModDataChecker,
// ModDataContent, SaveGameInfo,
ScriptExtender
// , UnmanagedMods
>;
std::map<std::type_index, std::any> PyPluginGame::featureList() const
{
py::dict pyFeatures = [this]() {
PYBIND11_OVERRIDE_PURE(py::dict, IPluginGame, featureList, );
}();
template <class F, std::size_t... Is>
static void helper(F&& f, std::index_sequence<Is...>)
{
(f(static_cast<std::tuple_element_t<Is, GameFeatures>*>(nullptr)), ...);
}
public:
// apply the function f on a null-pointer of type Feature* for each game
// feature
template <class F>
static void apply(F&& f)
{
helper(f, std::make_index_sequence<std::tuple_size_v<GameFeatures>>{});
}
};
return convert_feature_list(pyFeatures);
}
// this one is kind of big so it has its own function
void add_iplugingame_bindings(pybind11::module_ m)
{
py::enum_<MOBase::IPluginGame::LoadOrderMechanism>(m, "LoadOrderMechanism")
.value("FileTime", MOBase::IPluginGame::LoadOrderMechanism::FileTime)
.value("PluginsTxt", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt)
py::enum_<IPluginGame::LoadOrderMechanism>(m, "LoadOrderMechanism")
.value("FileTime", IPluginGame::LoadOrderMechanism::FileTime)
.value("PluginsTxt", IPluginGame::LoadOrderMechanism::PluginsTxt)
.value("FILE_TIME", MOBase::IPluginGame::LoadOrderMechanism::FileTime)
.value("PLUGINS_TXT", MOBase::IPluginGame::LoadOrderMechanism::PluginsTxt);
.value("FILE_TIME", IPluginGame::LoadOrderMechanism::FileTime)
.value("PLUGINS_TXT", IPluginGame::LoadOrderMechanism::PluginsTxt);
py::enum_<MOBase::IPluginGame::SortMechanism>(m, "SortMechanism")
.value("NONE", MOBase::IPluginGame::SortMechanism::NONE)
.value("MLOX", MOBase::IPluginGame::SortMechanism::MLOX)
.value("BOSS", MOBase::IPluginGame::SortMechanism::BOSS)
.value("LOOT", MOBase::IPluginGame::SortMechanism::LOOT);
py::enum_<IPluginGame::SortMechanism>(m, "SortMechanism")
.value("NONE", IPluginGame::SortMechanism::NONE)
.value("MLOX", IPluginGame::SortMechanism::MLOX)
.value("BOSS", IPluginGame::SortMechanism::BOSS)
.value("LOOT", IPluginGame::SortMechanism::LOOT);
// this does not actually do the conversion, but might be convenient
// for accessing the names for enum bits
py::enum_<MOBase::IPluginGame::ProfileSetting>(m, "ProfileSetting")
.value("mods", MOBase::IPluginGame::MODS)
.value("configuration", MOBase::IPluginGame::CONFIGURATION)
.value("savegames", MOBase::IPluginGame::SAVEGAMES)
.value("preferDefaults", MOBase::IPluginGame::PREFER_DEFAULTS)
py::enum_<IPluginGame::ProfileSetting>(m, "ProfileSetting")
.value("mods", IPluginGame::MODS)
.value("configuration", IPluginGame::CONFIGURATION)
.value("savegames", IPluginGame::SAVEGAMES)
.value("preferDefaults", IPluginGame::PREFER_DEFAULTS)
.value("MODS", MOBase::IPluginGame::MODS)
.value("CONFIGURATION", MOBase::IPluginGame::CONFIGURATION)
.value("SAVEGAMES", MOBase::IPluginGame::SAVEGAMES)
.value("PREFER_DEFAULTS", MOBase::IPluginGame::PREFER_DEFAULTS);
.value("MODS", IPluginGame::MODS)
.value("CONFIGURATION", IPluginGame::CONFIGURATION)
.value("SAVEGAMES", IPluginGame::SAVEGAMES)
.value("PREFER_DEFAULTS", IPluginGame::PREFER_DEFAULTS);
py::class_<IPluginGame, IPlugin, std::unique_ptr<IPluginGame, py::nodelete>>(
m, "IPluginGame")
py::class_<IPluginGame, PyPluginGame, IPlugin,
std::unique_ptr<IPluginGame, py::nodelete>>(
m, "IPluginGame", py::multiple_inheritance())
.def(py::init<>())
.def("featureList",
[](MOBase::IPluginGame* p) {
// constructing a dict from class name to actual object
py::dict dict;
GameFeaturesHelper::apply(
[p, &dict]<class Feature>(Feature* feature) {
const auto name = py::type::of<Feature>().attr("__name__");
dict[name] = py::cast(p->feature<Feature>(),
py::return_value_policy::reference);
});
return dict;
})
.def("featureList", &extract_feature_list)
.def("feature", &extract_feature, "feature_type"_a,
py::return_value_policy::reference)
.def(
"feature",
[](MOBase::IPluginGame* p, py::object clsObj) {
py::object py_feature = py::none();
GameFeaturesHelper::apply([p, &clsObj, &py_feature]<class Feature>(
Feature* feature) {
if (py::type::of<Feature>().is(clsObj)) {
py_feature = py::cast(p->feature<Feature>(),
py::return_value_policy::reference);
}
});
return py_feature;
},
"feature_type"_a, py::return_value_policy::reference)
// .def("localizedName", &MOBase::IPlugin::localizedName,
// &IPluginGameWrapper::localizedName_Default) .def("master",
// &MOBase::IPlugin::master, &IPluginGameWrapper::master_Default)
// .def("detectGame",
// py::pure_virtual(&MOBase::IPluginGame::detectGame))
// .def("gameName",
// py::pure_virtual(&MOBase::IPluginGame::gameName))
// .def("initializeProfile",
// py::pure_virtual(&MOBase::IPluginGame::initializeProfile),
// (py::arg("directory"), "settings")) .def("listSaves",
// py::pure_virtual(&MOBase::IPluginGame::listSaves),
// py::arg("folder")) .def("isInstalled",
// py::pure_virtual(&MOBase::IPluginGame::isInstalled))
// .def("gameIcon",
// py::pure_virtual(&MOBase::IPluginGame::gameIcon))
// .def("gameDirectory",
// py::pure_virtual(&MOBase::IPluginGame::gameDirectory))
.def("dataDirectory", &MOBase::IPluginGame::dataDirectory)
// .def("setGamePath",
// py::pure_virtual(&MOBase::IPluginGame::setGamePath),
// py::arg("path")) .def("documentsDirectory",
// py::pure_virtual(&MOBase::IPluginGame::documentsDirectory))
// .def("savesDirectory",
// py::pure_virtual(&MOBase::IPluginGame::savesDirectory))
// .def("executables",
// py::pure_virtual(&MOBase::IPluginGame::executables))
// .def("executableForcedLoads",
// py::pure_virtual(&MOBase::IPluginGame::executableForcedLoads))
// .def("steamAPPId",
// py::pure_virtual(&MOBase::IPluginGame::steamAPPId))
// .def("primaryPlugins",
// py::pure_virtual(&MOBase::IPluginGame::primaryPlugins))
// .def("gameVariants",
// py::pure_virtual(&MOBase::IPluginGame::gameVariants))
// .def("setGameVariant",
// py::pure_virtual(&MOBase::IPluginGame::setGameVariant),
// py::arg("variant")) .def("binaryName",
// py::pure_virtual(&MOBase::IPluginGame::binaryName))
// .def("gameShortName",
// py::pure_virtual(&MOBase::IPluginGame::gameShortName))
// .def("primarySources",
// py::pure_virtual(&MOBase::IPluginGame::primarySources))
// .def("validShortNames",
// py::pure_virtual(&MOBase::IPluginGame::validShortNames))
// .def("gameNexusName",
// py::pure_virtual(&MOBase::IPluginGame::gameNexusName))
// .def("iniFiles",
// py::pure_virtual(&MOBase::IPluginGame::iniFiles))
// .def("DLCPlugins",
// py::pure_virtual(&MOBase::IPluginGame::DLCPlugins))
// .def("CCPlugins",
// py::pure_virtual(&MOBase::IPluginGame::CCPlugins))
// .def("loadOrderMechanism",
// py::pure_virtual(&MOBase::IPluginGame::loadOrderMechanism))
// .def("sortMechanism",
// py::pure_virtual(&MOBase::IPluginGame::sortMechanism))
// .def("nexusModOrganizerID",
// py::pure_virtual(&MOBase::IPluginGame::nexusModOrganizerID))
// .def("nexusGameID",
// py::pure_virtual(&MOBase::IPluginGame::nexusGameID))
// .def("looksValid",
// py::pure_virtual(&MOBase::IPluginGame::looksValid),
// py::arg("directory")) .def("gameVersion",
// py::pure_virtual(&MOBase::IPluginGame::gameVersion))
// .def("getLauncherName",
// py::pure_virtual(&MOBase::IPluginGame::getLauncherName))
//
;
.def("detectGame", &IPluginGame::detectGame)
.def("gameName", &IPluginGame::gameName)
.def("initializeProfile", &IPluginGame::initializeProfile, "directory"_a,
"settings"_a)
.def("listSaves", &IPluginGame::listSaves, "folder"_a)
.def("isInstalled", &IPluginGame::isInstalled)
.def("gameIcon", &IPluginGame::gameIcon)
.def("gameDirectory", &IPluginGame::gameDirectory)
.def("dataDirectory", &IPluginGame::dataDirectory)
.def("setGamePath", &IPluginGame::setGamePath, "path"_a)
.def("documentsDirectory", &IPluginGame::documentsDirectory)
.def("savesDirectory", &IPluginGame::savesDirectory)
.def("executables", &IPluginGame::executables)
.def("executableForcedLoads", &IPluginGame::executableForcedLoads)
.def("steamAPPId", &IPluginGame::steamAPPId)
.def("primaryPlugins", &IPluginGame::primaryPlugins)
.def("gameVariants", &IPluginGame::gameVariants)
.def("setGameVariant", &IPluginGame::setGameVariant, "variant"_a)
.def("binaryName", &IPluginGame::binaryName)
.def("gameShortName", &IPluginGame::gameShortName)
.def("primarySources", &IPluginGame::primarySources)
.def("validShortNames", &IPluginGame::validShortNames)
.def("gameNexusName", &IPluginGame::gameNexusName)
.def("iniFiles", &IPluginGame::iniFiles)
.def("DLCPlugins", &IPluginGame::DLCPlugins)
.def("CCPlugins", &IPluginGame::CCPlugins)
.def("loadOrderMechanism", &IPluginGame::loadOrderMechanism)
.def("sortMechanism", &IPluginGame::sortMechanism)
.def("nexusModOrganizerID", &IPluginGame::nexusModOrganizerID)
.def("nexusGameID", &IPluginGame::nexusGameID)
.def("looksValid", &IPluginGame::looksValid, "directory"_a)
.def("gameVersion", &IPluginGame::gameVersion)
.def("getLauncherName", &IPluginGame::getLauncherName);
}
// multiple installers
void add_iplugininstaller_bindings(pybind11::module_ m)
{
py::enum_<MOBase::IPluginInstaller::EInstallResult>(m, "InstallResult")
.value("SUCCESS", MOBase::IPluginInstaller::RESULT_SUCCESS)
.value("FAILED", MOBase::IPluginInstaller::RESULT_FAILED)
.value("CANCELED", MOBase::IPluginInstaller::RESULT_CANCELED)
.value("MANUAL_REQUESTED", MOBase::IPluginInstaller::RESULT_MANUALREQUESTED)
.value("NOT_ATTEMPTED", MOBase::IPluginInstaller::RESULT_NOTATTEMPTED);
py::enum_<IPluginInstaller::EInstallResult>(m, "InstallResult")
.value("SUCCESS", IPluginInstaller::RESULT_SUCCESS)
.value("FAILED", IPluginInstaller::RESULT_FAILED)
.value("CANCELED", IPluginInstaller::RESULT_CANCELED)
.value("MANUAL_REQUESTED", IPluginInstaller::RESULT_MANUALREQUESTED)
.value("NOT_ATTEMPTED", IPluginInstaller::RESULT_NOTATTEMPTED);
// this is bind but should not be inherited in Python - does not make sense,
// having it makes it simpler to bind the Simple and Custom installers
@@ -240,16 +155,16 @@ namespace mo2::python {
py::class_<IPlugin, PyPlugin, std::unique_ptr<IPlugin, py::nodelete>>(
m, "IPluginBase", py::multiple_inheritance())
.def(py::init<>())
.def("init", &MOBase::IPlugin::init, "organizer"_a)
.def("name", &MOBase::IPlugin::name)
.def("localizedName", &MOBase::IPlugin::localizedName)
.def("master", &MOBase::IPlugin::master)
.def("author", &MOBase::IPlugin::author)
.def("description", &MOBase::IPlugin::description)
.def("version", &MOBase::IPlugin::version)
.def("requirements", &MOBase::IPlugin::requirements)
.def("settings", &MOBase::IPlugin::settings)
.def("enabledByDefault", &MOBase::IPlugin::enabledByDefault);
.def("init", &IPlugin::init, "organizer"_a)
.def("name", &IPlugin::name)
.def("localizedName", &IPlugin::localizedName)
.def("master", &IPlugin::master)
.def("author", &IPlugin::author)
.def("description", &IPlugin::description)
.def("version", &IPlugin::version)
.def("requirements", &IPlugin::requirements)
.def("settings", &IPlugin::settings)
.def("enabledByDefault", &IPlugin::enabledByDefault);
py::class_<IPyPlugin, PyPlugin, IPlugin,
std::unique_ptr<IPyPlugin, py::nodelete>>(m, "IPlugin",
@@ -266,14 +181,11 @@ namespace mo2::python {
std::unique_ptr<IPyPluginDiagnose, py::nodelete>>(
m, "IPluginDiagnose", py::multiple_inheritance())
.def(py::init<>())
.def("activeProblems", &MOBase::IPluginDiagnose::activeProblems)
.def("shortDescription", &MOBase::IPluginDiagnose::shortDescription,
py::arg("key"))
.def("fullDescription", &MOBase::IPluginDiagnose::fullDescription,
py::arg("key"))
.def("hasGuidedFix", &MOBase::IPluginDiagnose::hasGuidedFix, py::arg("key"))
.def("startGuidedFix", &MOBase::IPluginDiagnose::startGuidedFix,
py::arg("key"))
.def("activeProblems", &IPluginDiagnose::activeProblems)
.def("shortDescription", &IPluginDiagnose::shortDescription, "key"_a)
.def("fullDescription", &IPluginDiagnose::fullDescription, "key"_a)
.def("hasGuidedFix", &IPluginDiagnose::hasGuidedFix, "key"_a)
.def("startGuidedFix", &IPluginDiagnose::startGuidedFix, "key"_a)
.def("_invalidate", &PyPluginDiagnose::invalidate);
py::class_<IPluginTool, PyPluginTool, IPlugin,
@@ -337,14 +249,15 @@ namespace mo2::python {
helper.append_if_instance<IPluginPreview>(plugin_obj);
helper.append_if_instance<IPluginTool>(plugin_obj);
// helper.append_if_instance<IPluginGame>(plugin_obj);
helper.append_if_instance<IPluginGame>(plugin_obj);
// we need to check the two installer types because IPluginInstaller does not
// inherit QObject, and the trampoline do not have a common ancestor
helper.append_if_instance<IPluginInstallerSimple>(plugin_obj);
helper.append_if_instance<IPluginInstallerCustom>(plugin_obj);
if (helper.objects.isEmpty()) {
if (py::isinstance<IPyPlugin>(plugin_obj)) {
helper.objects.append(plugin_obj.cast<IPyPlugin*>());
}
helper.append_if_instance<IPyPlugin>(plugin_obj);
}
return helper.objects;
+166 -12
View File
@@ -27,14 +27,9 @@ namespace mo2::python {
using namespace MOBase;
class IPyPlugin : public QObject, public IPlugin {};
class IPyPluginFileMapper : public IPyPlugin, public IPluginFileMapper {};
class IPyPluginDiagnose : public IPyPlugin, public IPluginDiagnose {};
// we need two base trampoline because IPluginGame has some final methods.
template <class PluginBase>
class PyPluginBase : public PluginBase {
class PyPluginBaseNoFinal : public PluginBase {
public:
using PluginBase::PluginBase;
@@ -54,11 +49,6 @@ namespace mo2::python {
{
PYBIND11_OVERRIDE(QString, PluginBase, master, );
}
std::vector<std::shared_ptr<const IPluginRequirement>> requirements() const
{
PYBIND11_OVERRIDE(std::vector<std::shared_ptr<const IPluginRequirement>>,
PluginBase, requirements, );
}
QString author() const override
{
PYBIND11_OVERRIDE_PURE(QString, PluginBase, author, );
@@ -75,12 +65,32 @@ namespace mo2::python {
{
PYBIND11_OVERRIDE_PURE(QList<PluginSetting>, PluginBase, settings, );
}
};
template <class PluginBase>
class PyPluginBase : public PyPluginBaseNoFinal<PluginBase> {
public:
using PyPluginBaseNoFinal<PluginBase>::PyPluginBaseNoFinal;
std::vector<std::shared_ptr<const IPluginRequirement>> requirements() const
{
PYBIND11_OVERRIDE(std::vector<std::shared_ptr<const IPluginRequirement>>,
PluginBase, requirements, );
}
bool enabledByDefault() const override
{
PYBIND11_OVERRIDE(bool, PluginBase, enabledByDefault, );
}
};
// these classes do not inherit IPlugin or QObject so we need intermediate class to
// get proper bindings
class IPyPlugin : public QObject, public IPlugin {};
class IPyPluginFileMapper : public IPyPlugin, public IPluginFileMapper {};
class IPyPluginDiagnose : public IPyPlugin, public IPluginDiagnose {};
// PyXXX classes - trampoline classes for the plugins
class PyPlugin : public PyPluginBase<IPyPlugin> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin)
@@ -331,6 +341,150 @@ namespace mo2::python {
}
};
// game
class PyPluginGame : public PyPluginBaseNoFinal<IPluginGame> {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginGame)
public:
void detectGame() override
{
PYBIND11_OVERRIDE_PURE(void, IPluginGame, detectGame, );
}
QString gameName() const override
{
PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameName, );
}
void initializeProfile(const QDir& directory,
ProfileSettings settings) const override
{
PYBIND11_OVERRIDE_PURE(void, IPluginGame, initializeProfile, directory,
settings);
}
std::vector<std::shared_ptr<const ISaveGame>>
listSaves(QDir folder) const override
{
PYBIND11_OVERRIDE_PURE(std::vector<std::shared_ptr<const ISaveGame>>,
IPluginGame, listSaves, folder);
}
bool isInstalled() const override
{
PYBIND11_OVERRIDE_PURE(bool, IPluginGame, isInstalled, );
}
QIcon gameIcon() const override
{
PYBIND11_OVERRIDE_PURE(QIcon, IPluginGame, gameIcon, );
}
QDir gameDirectory() const override
{
PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, gameDirectory, );
}
QDir dataDirectory() const override
{
PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, dataDirectory, );
}
void setGamePath(const QString& path) override
{
PYBIND11_OVERRIDE_PURE(void, IPluginGame, setGamePath, path);
}
QDir documentsDirectory() const override
{
PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, documentsDirectory, );
}
QDir savesDirectory() const override
{
PYBIND11_OVERRIDE_PURE(QDir, IPluginGame, savesDirectory, );
}
QList<ExecutableInfo> executables() const override
{
PYBIND11_OVERRIDE_PURE(QList<ExecutableInfo>, IPluginGame, executables, );
}
QList<ExecutableForcedLoadSetting> executableForcedLoads() const override
{
PYBIND11_OVERRIDE_PURE(QList<ExecutableForcedLoadSetting>, IPluginGame,
executableForcedLoads, );
}
QString steamAPPId() const override
{
PYBIND11_OVERRIDE_PURE(QString, IPluginGame, steamAPPId, );
}
QStringList primaryPlugins() const override
{
PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, primaryPlugins, );
}
QStringList gameVariants() const override
{
PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, gameVariants, );
}
void setGameVariant(const QString& variant) override
{
PYBIND11_OVERRIDE_PURE(void, IPluginGame, setGameVariant, variant);
}
QString binaryName() const override
{
PYBIND11_OVERRIDE_PURE(QString, IPluginGame, binaryName, );
}
QString gameShortName() const override
{
PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameShortName, );
}
QStringList primarySources() const override
{
PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, primarySources, );
}
QStringList validShortNames() const override
{
PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, validShortNames, );
}
QString gameNexusName() const override
{
PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameNexuesName, );
}
QStringList iniFiles() const override
{
PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, iniFiles, );
}
QStringList DLCPlugins() const override
{
PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, DLCPlugins, );
}
QStringList CCPlugins() const override
{
PYBIND11_OVERRIDE_PURE(QStringList, IPluginGame, CCPlugins, );
}
LoadOrderMechanism loadOrderMechanism() const override
{
PYBIND11_OVERRIDE_PURE(LoadOrderMechanism, IPluginGame,
loadOrderMechanism, );
}
SortMechanism sortMechanism() const override
{
PYBIND11_OVERRIDE_PURE(SortMechanism, IPluginGame, sortMechanism, );
}
int nexusModOrganizerID() const override
{
PYBIND11_OVERRIDE_PURE(int, IPluginGame, nexusModOrganizerID, );
}
int nexusGameID() const override
{
PYBIND11_OVERRIDE_PURE(int, IPluginGame, nexusGameID, );
}
bool looksValid(QDir const& dir) const override
{
PYBIND11_OVERRIDE_PURE(bool, IPluginGame, looksValid, dir);
}
QString gameVersion() const override
{
PYBIND11_OVERRIDE_PURE(QString, IPluginGame, gameVersion, );
}
QString getLauncherName() const override
{
PYBIND11_OVERRIDE_PURE(QString, IPluginGame, getLauncherName, );
}
protected:
std::map<std::type_index, std::any> featureList() const override;
};
} // namespace mo2::python
#endif

Some files were not shown because too many files have changed in this diff Show More