mirror of
https://github.com/ModOrganizer2/modorganizer-plugin_python.git
synced 2026-07-27 14:03:33 -07:00
Add Game Features support
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -0,0 +1,391 @@
|
||||
#include "gamefeatureswrappers.h"
|
||||
|
||||
#include <boost/any.hpp>
|
||||
|
||||
#include <typeindex>
|
||||
|
||||
#include <ipluginlist.h>
|
||||
#include <iprofile.h>
|
||||
#include <isavegame.h>
|
||||
#include <isavegameinfowidget.h>
|
||||
|
||||
#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<typename T>
|
||||
void insertGameFeature(std::map<std::type_index, boost::any> &map, const boost::python::object &pyObject)
|
||||
{
|
||||
map[std::type_index(typeid(T))] = boost::python::extract<T*>(pyObject)();
|
||||
}
|
||||
|
||||
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, boost::any>>());
|
||||
}
|
||||
|
||||
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<std::map<std::type_index, boost::any>>*)data)->storage.bytes;
|
||||
std::map<std::type_index, boost::any> *result = new (storage) std::map<std::type_index, boost::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];
|
||||
// pyKey should be a Boost.Python.class corresponding to a game feature.
|
||||
std::string className = boost::python::extract<std::string>(pyKey.attr("__name__"))();
|
||||
if (className == "BSAInvalidation")
|
||||
insertGameFeature<BSAInvalidation>(*result, source[pyKey]);
|
||||
else if (className == "DataArchives")
|
||||
insertGameFeature<DataArchives>(*result, source[pyKey]);
|
||||
else if (className == "GamePlugins")
|
||||
insertGameFeature<GamePlugins>(*result, source[pyKey]);
|
||||
else if (className == "LocalSavegames")
|
||||
insertGameFeature<LocalSavegames>(*result, source[pyKey]);
|
||||
else if (className == "SaveGameInfo")
|
||||
insertGameFeature<SaveGameInfo>(*result, source[pyKey]);
|
||||
else if (className == "ScriptExtender")
|
||||
insertGameFeature<ScriptExtender>(*result, source[pyKey]);
|
||||
else if (className == "UnmanagedMods")
|
||||
insertGameFeature<UnmanagedMods>(*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_<BSAInvalidationWrapper, boost::noncopyable>("BSAInvalidation")
|
||||
.def("isInvalidationBSA", bpy::pure_virtual(&BSAInvalidation::isInvalidationBSA))
|
||||
.def("deactivate", bpy::pure_virtual(&BSAInvalidation::deactivate))
|
||||
.def("activate", bpy::pure_virtual(&BSAInvalidation::activate))
|
||||
;
|
||||
|
||||
bpy::class_<DataArchivesWrapper, boost::noncopyable>("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_<GamePluginsWrapper, boost::noncopyable>("GamePlugins")
|
||||
.def("writePluginLists", bpy::pure_virtual(&GamePlugins::writePluginLists))
|
||||
.def("readPluginLists", bpy::pure_virtual(&GamePlugins::readPluginLists))
|
||||
;
|
||||
|
||||
bpy::class_<LocalSavegamesWrapper, boost::noncopyable>("LocalSavegames")
|
||||
.def("mappings", bpy::pure_virtual(&LocalSavegames::mappings))
|
||||
.def("prepareProfile", bpy::pure_virtual(&LocalSavegames::prepareProfile))
|
||||
;
|
||||
|
||||
bpy::class_<SaveGameInfoWrapper, boost::noncopyable>("SaveGameInfo")
|
||||
.def("getSaveGameInfo", bpy::pure_virtual(&SaveGameInfo::getSaveGameInfo), bpy::return_value_policy<bpy::manage_new_object>())
|
||||
.def("getMissingAssets", bpy::pure_virtual(&SaveGameInfo::getMissingAssets))
|
||||
.def("getSaveGameWidget", bpy::pure_virtual(&SaveGameInfo::getSaveGameWidget), bpy::return_value_policy<bpy::manage_new_object>())
|
||||
.def("hasScriptExtenderSave", bpy::pure_virtual(&SaveGameInfo::hasScriptExtenderSave))
|
||||
;
|
||||
|
||||
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("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_<UnmanagedModsWrapper, boost::noncopyable>("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))
|
||||
;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#ifndef GAMEFEATURESWRAPPERS_H
|
||||
#define GAMEFEATURESWRAPPERS_H
|
||||
|
||||
#include <bsainvalidation.h>
|
||||
#include <dataarchives.h>
|
||||
#include <gameplugins.h>
|
||||
#include <localsavegames.h>
|
||||
#include <savegameinfo.h>
|
||||
#include <scriptextender.h>
|
||||
#include <unmanagedmods.h>
|
||||
|
||||
// this might need turning off if Q_MOC_RUN is defined
|
||||
#include <boost/python.hpp>
|
||||
|
||||
/////////////////////////////
|
||||
/// Wrapper declarations
|
||||
|
||||
class BSAInvalidationWrapper : public BSAInvalidation, public boost::python::wrapper<BSAInvalidation>
|
||||
{
|
||||
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<DataArchives>
|
||||
{
|
||||
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>
|
||||
{
|
||||
virtual void writePluginLists(const MOBase::IPluginList *pluginList) override;
|
||||
virtual void readPluginLists(MOBase::IPluginList *pluginList) override;
|
||||
};
|
||||
|
||||
class LocalSavegamesWrapper : public LocalSavegames, public boost::python::wrapper<LocalSavegames>
|
||||
{
|
||||
virtual MappingType mappings(const QDir &profileSaveDir) const override;
|
||||
virtual void prepareProfile(MOBase::IProfile *profile) override;
|
||||
};
|
||||
|
||||
class SaveGameInfoWrapper : public SaveGameInfo, public boost::python::wrapper<SaveGameInfo>
|
||||
{
|
||||
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<ScriptExtender>
|
||||
{
|
||||
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<UnmanagedMods>
|
||||
{
|
||||
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
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "proxypluginwrappers.h"
|
||||
#include <utility.h>
|
||||
#include "error.h"
|
||||
|
||||
#include "gilock.h"
|
||||
#include <QUrl>
|
||||
#include <QWidget>
|
||||
#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<MOBase::ExecutableInfo> IPluginGameWrapper::executables() const
|
||||
{
|
||||
GILock lock;
|
||||
try {
|
||||
return this->get_override("executables")();
|
||||
} PYCATCH;
|
||||
@@ -237,6 +246,7 @@ QList<MOBase::ExecutableInfo> 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<std::type_index, boost::any> IPluginGameWrapper::featureList() const
|
||||
{
|
||||
qCritical("Calling unproxied method IPluginGameWrapper::featureList()");
|
||||
GILock lock;
|
||||
try {
|
||||
return this->get_override("_featureList")();
|
||||
} PYCATCH;
|
||||
|
||||
@@ -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<std::type_index, boost::any> featureList() const override;
|
||||
|
||||
// Thankfully, the default implementation of the templated 'T *feature()' function should allow us to get away without overriding it.
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
#ifndef PYCATCH_H
|
||||
#define PYCATCH_H
|
||||
|
||||
#include <utility.h>
|
||||
|
||||
#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
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <iplugintool.h>
|
||||
#include "uibasewrappers.h"
|
||||
#include "proxypluginwrappers.h"
|
||||
#include "gamefeatureswrappers.h"
|
||||
#include "sipApiAccess.h"
|
||||
|
||||
#include <Windows.h>
|
||||
@@ -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 <typename T>
|
||||
struct std_vector_to_python_list
|
||||
{
|
||||
static PyObject *convert(const std::vector<T> &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 <typename T>
|
||||
struct std_vector_from_python_obj
|
||||
{
|
||||
@@ -825,6 +850,16 @@ BOOST_PYTHON_MODULE(mobase)
|
||||
.def("isCustom", &ExecutableInfo::isCustom)
|
||||
;
|
||||
|
||||
bpy::class_<ISaveGameWrapper, boost::noncopyable>("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<bool, const IOrganizer::FileInfo&>();
|
||||
Functor1_converter<void, const QString&>();
|
||||
Functor1_converter<bool, const QString&>();
|
||||
@@ -1013,8 +1048,6 @@ BOOST_PYTHON_MODULE(mobase)
|
||||
.def_readwrite("createTarget", &Mapping::createTarget)
|
||||
;
|
||||
|
||||
std_vector_from_python_obj<Mapping>();
|
||||
|
||||
bpy::class_<IPluginFileMapperWrapper, boost::noncopyable>("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<BSAInvalidation>, bpy::return_value_policy<bpy::reference_existing_object>())
|
||||
.def("featureDataArchives", &MOBase::IPluginGame::feature<DataArchives>, bpy::return_value_policy<bpy::reference_existing_object>())
|
||||
.def("featureGamePlugins", &MOBase::IPluginGame::feature<GamePlugins>, bpy::return_value_policy<bpy::reference_existing_object>())
|
||||
.def("featureLocalSavegames", &MOBase::IPluginGame::feature<LocalSavegames>, bpy::return_value_policy<bpy::reference_existing_object>())
|
||||
.def("featureSaveGameInfo", &MOBase::IPluginGame::feature<SaveGameInfo>, bpy::return_value_policy<bpy::reference_existing_object>())
|
||||
.def("featureScriptExtender", &MOBase::IPluginGame::feature<ScriptExtender>, bpy::return_value_policy<bpy::reference_existing_object>())
|
||||
.def("featureUnmanagedMods", &MOBase::IPluginGame::feature<UnmanagedMods>, bpy::return_value_policy<bpy::reference_existing_object>())
|
||||
;
|
||||
|
||||
bpy::enum_<MOBase::IPluginInstaller::EInstallResult>("InstallResult")
|
||||
@@ -1120,13 +1161,20 @@ BOOST_PYTHON_MODULE(mobase)
|
||||
bpy::to_python_converter<QList<QFileInfo>,
|
||||
QList_to_python_list<QFileInfo>>();
|
||||
QList_from_python_obj<QVariant>();
|
||||
QList_to_python_list<QVariant>();
|
||||
bpy::to_python_converter<QList<QVariant>,
|
||||
QList_to_python_list<QVariant>>();
|
||||
|
||||
QMap_converters<QString, QVariant>();
|
||||
QMap_converters<QString, QStringList>();
|
||||
|
||||
std_vector_from_python_obj<unsigned int>();
|
||||
std_vector_from_python_obj<Mapping>();
|
||||
bpy::to_python_converter<std::vector<Mapping>,
|
||||
std_vector_to_python_list<Mapping>>();
|
||||
|
||||
stdset_from_python_list<QString>();
|
||||
|
||||
registerGameFeaturesPythonConverters();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,8 +21,12 @@
|
||||
#include <idownloadmanager.h>
|
||||
#include <ipluginlist.h>
|
||||
#include <imodlist.h>
|
||||
#include <isavegame.h>
|
||||
#include <isavegameinfowidget.h>
|
||||
|
||||
#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<MOBase::IModLis
|
||||
};
|
||||
|
||||
|
||||
// This needs to be extendable in Python, so actually needs a wrapper
|
||||
// Everything else probably doesn't
|
||||
class ISaveGameWrapper : public MOBase::ISaveGame, public boost::python::wrapper<MOBase::ISaveGame>
|
||||
{
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user