mirror of
https://github.com/ModOrganizer2/modorganizer-plugin_python.git
synced 2026-07-27 14:03:33 -07:00
354 lines
17 KiB
C++
354 lines
17 KiB
C++
#include "wrappers.h"
|
|
|
|
#include <tuple>
|
|
|
|
#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
|
|
>;
|
|
|
|
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>>{});
|
|
}
|
|
};
|
|
|
|
// 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)
|
|
|
|
.value("FILE_TIME", MOBase::IPluginGame::LoadOrderMechanism::FileTime)
|
|
.value("PLUGINS_TXT", MOBase::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);
|
|
|
|
// 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)
|
|
|
|
.value("MODS", MOBase::IPluginGame::MODS)
|
|
.value("CONFIGURATION", MOBase::IPluginGame::CONFIGURATION)
|
|
.value("SAVEGAMES", MOBase::IPluginGame::SAVEGAMES)
|
|
.value("PREFER_DEFAULTS", MOBase::IPluginGame::PREFER_DEFAULTS);
|
|
|
|
py::class_<IPluginGame, IPlugin, std::unique_ptr<IPluginGame, py::nodelete>>(
|
|
m, "IPluginGame")
|
|
|
|
.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(
|
|
"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))
|
|
//
|
|
;
|
|
}
|
|
|
|
// 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);
|
|
|
|
// 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
|
|
py::class_<IPluginInstaller, PyPluginInstallerBase<IPluginInstaller>, IPlugin,
|
|
std::unique_ptr<IPluginInstaller, py::nodelete>>(
|
|
m, "IPluginInstaller", py::multiple_inheritance())
|
|
.def("isArchiveSupported", &IPluginInstaller::isArchiveSupported, "tree"_a)
|
|
.def("priority", &IPluginInstaller::priority)
|
|
.def("onInstallationStart", &IPluginInstaller::onInstallationStart,
|
|
"archive"_a, "reinstallation"_a, "current_mod"_a)
|
|
.def("onInstallationEnd", &IPluginInstaller::onInstallationEnd, "result"_a,
|
|
"new_mod"_a)
|
|
.def("isManualInstaller", &IPluginInstaller::isManualInstaller)
|
|
.def("setParentWidget", &IPluginInstaller::setParentWidget, "parent"_a)
|
|
.def("setInstallationManager", &IPluginInstaller::setInstallationManager,
|
|
"manager"_a)
|
|
.def("_parentWidget",
|
|
&PyPluginInstallerBase<IPluginInstaller>::parentWidget)
|
|
.def("_manager", &PyPluginInstallerBase<IPluginInstaller>::manager,
|
|
py::return_value_policy::reference);
|
|
|
|
py::class_<IPluginInstallerSimple, PyPluginInstallerSimple, IPluginInstaller,
|
|
std::unique_ptr<IPluginInstallerSimple, py::nodelete>>(
|
|
m, "IPluginInstallerSimple", py::multiple_inheritance())
|
|
.def(py::init<>())
|
|
|
|
// note: keeping the variant here even if we always return a tuple
|
|
// to be consistent with the wrapper and have proper stubs generation.
|
|
.def(
|
|
"install",
|
|
[](IPluginInstallerSimple* p, GuessedValue<QString>& modName,
|
|
std::shared_ptr<IFileTree>& tree, QString& version,
|
|
int& nexusID) -> PyPluginInstallerSimple::py_install_return_type {
|
|
auto result = p->install(modName, tree, version, nexusID);
|
|
return std::make_tuple(result, tree, version, nexusID);
|
|
},
|
|
"name"_a, "tree"_a, "version"_a, "nexus_id"_a);
|
|
|
|
py::class_<IPluginInstallerCustom, PyPluginInstallerCustom, IPluginInstaller,
|
|
std::unique_ptr<IPluginInstallerCustom, py::nodelete>>(
|
|
m, "IPluginInstallerCustom", py::multiple_inheritance())
|
|
.def(py::init<>())
|
|
.def("isArchiveSupported", &IPluginInstallerCustom::isArchiveSupported,
|
|
"archive_name"_a)
|
|
.def("supportedExtensions", &IPluginInstallerCustom::supportedExtensions)
|
|
.def("install", &IPluginInstallerCustom::install, "mod_name"_a,
|
|
"game_name"_a, "archive_name"_a, "version"_a, "nexus_id"_a);
|
|
}
|
|
|
|
void add_plugins_bindings(pybind11::module_ m)
|
|
{
|
|
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);
|
|
|
|
py::class_<IPyPlugin, PyPlugin, IPlugin,
|
|
std::unique_ptr<IPyPlugin, py::nodelete>>(m, "IPlugin",
|
|
py::multiple_inheritance())
|
|
.def(py::init<>());
|
|
|
|
py::class_<IPyPluginFileMapper, PyPluginFileMapper, IPlugin,
|
|
std::unique_ptr<IPyPluginFileMapper, py::nodelete>>(
|
|
m, "IPluginFileMapper", py::multiple_inheritance())
|
|
.def(py::init<>())
|
|
.def("mappings", &IPluginFileMapper::mappings);
|
|
|
|
py::class_<IPyPluginDiagnose, PyPluginDiagnose, IPlugin,
|
|
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("_invalidate", &PyPluginDiagnose::invalidate);
|
|
|
|
py::class_<IPluginTool, PyPluginTool, IPlugin,
|
|
std::unique_ptr<IPluginTool, py::nodelete>>(
|
|
m, "IPluginTool", py::multiple_inheritance())
|
|
.def(py::init<>())
|
|
.def("displayName", &IPluginTool::displayName)
|
|
.def("tooltip", &IPluginTool::tooltip)
|
|
.def("icon", &IPluginTool::icon)
|
|
.def("display", &IPluginTool::display)
|
|
.def("setParentWidget", &IPluginTool::setParentWidget)
|
|
.def("_parentWidget", &PyPluginTool::parentWidget);
|
|
|
|
py::class_<IPluginPreview, PyPluginPreview, IPlugin,
|
|
std::unique_ptr<IPluginPreview, py::nodelete>>(
|
|
m, "IPluginPreview", py::multiple_inheritance())
|
|
.def(py::init<>())
|
|
.def("supportedExtensions", &IPluginPreview::supportedExtensions)
|
|
.def("genFilePreview", &IPluginPreview::genFilePreview, "filename"_a,
|
|
"max_size"_a);
|
|
|
|
py::class_<IPluginModPage, PyPluginModPage, IPluginModPage,
|
|
std::unique_ptr<IPluginModPage, py::nodelete>>(
|
|
m, "IPluginModPage", py::multiple_inheritance())
|
|
.def(py::init<>())
|
|
.def("displayName", &IPluginModPage::displayName)
|
|
.def("icon", &IPluginModPage::icon)
|
|
.def("pageURL", &IPluginModPage::pageURL)
|
|
.def("useIntegratedBrowser", &IPluginModPage::useIntegratedBrowser)
|
|
.def("handlesDownload", &IPluginModPage::handlesDownload, "page_url"_a,
|
|
"download_url"_a, "fileinfo"_a)
|
|
.def("setParentWidget", &IPluginModPage::setParentWidget, "parent"_a)
|
|
.def("_parentWidget", &PyPluginModPage::parentWidget);
|
|
|
|
add_iplugingame_bindings(m);
|
|
add_iplugininstaller_bindings(m);
|
|
}
|
|
|
|
struct extract_plugins_helper {
|
|
QList<QObject*> objects;
|
|
|
|
template <class IPluginClass>
|
|
void append_if_instance(pybind11::object plugin_obj)
|
|
{
|
|
if (py::isinstance<IPluginClass>(plugin_obj)) {
|
|
objects.append(plugin_obj.cast<IPluginClass*>());
|
|
}
|
|
}
|
|
};
|
|
|
|
QList<QObject*> extract_plugins(pybind11::object plugin_obj)
|
|
{
|
|
extract_plugins_helper helper;
|
|
|
|
// we need to check the trampoline class for these since the interfaces do not
|
|
// extend IPlugin
|
|
helper.append_if_instance<IPyPluginFileMapper>(plugin_obj);
|
|
helper.append_if_instance<IPyPluginDiagnose>(plugin_obj);
|
|
|
|
helper.append_if_instance<IPluginModPage>(plugin_obj);
|
|
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<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*>());
|
|
}
|
|
}
|
|
|
|
return helper.objects;
|
|
}
|
|
|
|
} // namespace mo2::python
|