1010#include < bsainvalidation.h>
1111#include < dataarchives.h>
1212#include < gameplugins.h>
13+ #include < igamefeatures.h>
1314#include < localsavegames.h>
1415#include < moddatachecker.h>
1516#include < moddatacontent.h>
2021#include " pyfiletree.h"
2122
2223namespace py = pybind11;
24+
2325using namespace MOBase ;
2426using namespace pybind11 ::literals;
2527
@@ -198,7 +200,7 @@ namespace mo2::python {
198200 }
199201 };
200202
201- class PyPyUnmanagedMods : public UnmanagedMods {
203+ class PyUnmanagedMods : public UnmanagedMods {
202204 public:
203205 QStringList mods (bool onlyOfficial) const override
204206 {
@@ -223,17 +225,24 @@ namespace mo2::python {
223225
224226 void add_game_feature_bindings (pybind11::module_ m)
225227 {
228+ // this is just to allow accepting GameFeature in function, we do not expose
229+ // anything from game feature to Python since typeInfo() is useless in Python
230+ //
231+ py::class_<GameFeature, std::shared_ptr<GameFeature>>(m, " GameFeature" );
232+
226233 // BSAInvalidation
227234
228- py::class_<BSAInvalidation, PyBSAInvalidation>(m, " BSAInvalidation" )
235+ py::class_<BSAInvalidation, GameFeature, PyBSAInvalidation,
236+ std::shared_ptr<BSAInvalidation>>(m, " BSAInvalidation" )
229237 .def (py::init<>())
230238 .def (" isInvalidationBSA" , &BSAInvalidation::isInvalidationBSA, " name" _a)
231239 .def (" deactivate" , &BSAInvalidation::deactivate, " profile" _a)
232240 .def (" activate" , &BSAInvalidation::activate, " profile" _a);
233241
234242 // DataArchives
235243
236- py::class_<DataArchives, PyDataArchives>(m, " DataArchives" )
244+ py::class_<DataArchives, GameFeature, PyDataArchives,
245+ std::shared_ptr<DataArchives>>(m, " DataArchives" )
237246 .def (py::init<>())
238247 .def (" vanillaArchives" , &DataArchives::vanillaArchives)
239248 .def (" archives" , &DataArchives::archives, " profile" _a)
@@ -243,7 +252,8 @@ namespace mo2::python {
243252
244253 // GamePlugins
245254
246- py::class_<GamePlugins, PyGamePlugins>(m, " GamePlugins" )
255+ py::class_<GamePlugins, GameFeature, PyGamePlugins,
256+ std::shared_ptr<GamePlugins>>(m, " GamePlugins" )
247257 .def (py::init<>())
248258 .def (" writePluginLists" , &GamePlugins::writePluginLists, " plugin_list" _a)
249259 .def (" readPluginLists" , &GamePlugins::readPluginLists, " plugin_list" _a)
@@ -254,15 +264,17 @@ namespace mo2::python {
254264
255265 // LocalSavegames
256266
257- py::class_<LocalSavegames, PyLocalSavegames>(m, " LocalSavegames" )
267+ py::class_<LocalSavegames, GameFeature, PyLocalSavegames,
268+ std::shared_ptr<LocalSavegames>>(m, " LocalSavegames" )
258269 .def (py::init<>())
259270 .def (" mappings" , &LocalSavegames::mappings, " profile_save_dir" _a)
260271 .def (" prepareProfile" , &LocalSavegames::prepareProfile, " profile" _a);
261272
262273 // ModDataChecker
263274
264- py::class_<ModDataChecker, PyModDataChecker> pyModDataChecker (m,
265- " ModDataChecker" );
275+ py::class_<ModDataChecker, GameFeature, PyModDataChecker,
276+ std::shared_ptr<ModDataChecker>>
277+ pyModDataChecker (m, " ModDataChecker" );
266278
267279 py::enum_<ModDataChecker::CheckReturn>(pyModDataChecker, " CheckReturn" )
268280 .value (" INVALID" , ModDataChecker::CheckReturn::INVALID)
@@ -275,8 +287,9 @@ namespace mo2::python {
275287 .def (" fix" , &ModDataChecker::fix, " filetree" _a);
276288
277289 // ModDataContent
278- py::class_<ModDataContent, PyModDataContent> pyModDataContent (m,
279- " ModDataContent" );
290+ py::class_<ModDataContent, GameFeature, PyModDataContent,
291+ std::shared_ptr<ModDataContent>>
292+ pyModDataContent (m, " ModDataContent" );
280293
281294 py::class_<ModDataContent::Content>(pyModDataContent, " Content" )
282295 .def (py::init<int , QString, QString, bool >(), " id" _a, " name" _a, " icon" _a,
@@ -292,15 +305,17 @@ namespace mo2::python {
292305
293306 // SaveGameInfo
294307
295- py::class_<SaveGameInfo, PySaveGameInfo>(m, " SaveGameInfo" )
308+ py::class_<SaveGameInfo, GameFeature, PySaveGameInfo,
309+ std::shared_ptr<SaveGameInfo>>(m, " SaveGameInfo" )
296310 .def (py::init<>())
297311 .def (" getMissingAssets" , &SaveGameInfo::getMissingAssets, " save" _a)
298312 .def (" getSaveGameWidget" , &SaveGameInfo::getSaveGameWidget,
299313 py::return_value_policy::reference, " parent" _a);
300314
301315 // ScriptExtender
302316
303- py::class_<ScriptExtender, PyScriptExtender>(m, " ScriptExtender" )
317+ py::class_<ScriptExtender, GameFeature, PyScriptExtender,
318+ std::shared_ptr<ScriptExtender>>(m, " ScriptExtender" )
304319 .def (py::init<>())
305320 .def (" binaryName" , &ScriptExtender::BinaryName)
306321 .def (" pluginPath" , wrap_return_for_directory (&ScriptExtender::PluginPath))
@@ -313,7 +328,8 @@ namespace mo2::python {
313328
314329 // UnmanagedMods
315330
316- py::class_<UnmanagedMods, PyPyUnmanagedMods>(m, " UnmanagedMods" )
331+ py::class_<UnmanagedMods, GameFeature, PyUnmanagedMods,
332+ std::shared_ptr<UnmanagedMods>>(m, " UnmanagedMods" )
317333 .def (py::init<>())
318334 .def (" mods" , &UnmanagedMods::mods, " official_only" _a)
319335 .def (" displayName" , &UnmanagedMods::displayName, " mod_name" _a)
@@ -328,19 +344,39 @@ namespace mo2::python {
328344 " mod_name" _a);
329345 }
330346
347+ void add_igamefeatures_classes (py::module_ m)
348+ {
349+ py::class_<IGameFeatures>(m, " IGameFeatures" )
350+ .def (" registerFeature" ,
351+ py::overload_cast<QStringList const &, std::shared_ptr<GameFeature>,
352+ int , bool >(&IGameFeatures::registerFeature),
353+ " games" _a, " feature" _a, " priority" _a, " replace" _a = false )
354+ .def (" registerFeature" ,
355+ py::overload_cast<MOBase::IPluginGame*, std::shared_ptr<GameFeature>,
356+ int , bool >(&IGameFeatures::registerFeature),
357+ " game" _a, " feature" _a, " priority" _a, " replace" _a = false )
358+ .def (" registerFeature" ,
359+ py::overload_cast<std::shared_ptr<GameFeature>, int , bool >(
360+ &IGameFeatures::registerFeature),
361+ " feature" _a, " priority" _a, " replace" _a = false )
362+ .def (" unregisterFeature" , &IGameFeatures::unregisterFeature, " feature" _a)
363+ .def (" unregisterFeatures" , &unregister_feature, " feature_type" _a)
364+ .def (" gameFeature" , &extract_feature, " feature_type" _a,
365+ py ::return_value_policy::reference);
366+ }
367+
331368} // namespace mo2::python
332369
333370namespace mo2 ::python {
334371
335372 class GameFeaturesHelper {
336- using GameFeatures = std::tuple<BSAInvalidation, DataArchives, GamePlugins,
337- LocalSavegames, ModDataChecker, ModDataContent,
338- SaveGameInfo, ScriptExtender, UnmanagedMods>;
339-
340373 template <class F , std::size_t ... Is>
341374 static void helper (F&& f, std::index_sequence<Is...>)
342375 {
343- (f (static_cast <std::tuple_element_t <Is, GameFeatures>*>(nullptr )), ...);
376+ (f (static_cast <
377+ std::tuple_element_t <Is, MOBase::details::BaseGameFeaturesP>>(
378+ nullptr )),
379+ ...);
344380 }
345381
346382 public:
@@ -349,45 +385,33 @@ namespace mo2::python {
349385 template <class F >
350386 static void apply (F&& f)
351387 {
352- helper (f, std::make_index_sequence<std::tuple_size_v<GameFeatures>>{});
388+ helper (f, std::make_index_sequence<
389+ std::tuple_size_v<MOBase::details::BaseGameFeaturesP>>{});
353390 }
354391 };
355392
356- pybind11::object extract_feature (IPluginGame const & game, pybind11::object type)
393+ pybind11::object extract_feature (IGameFeatures const & gameFeatures,
394+ pybind11::object type)
357395 {
358396 py::object py_feature = py::none ();
359397 GameFeaturesHelper::apply ([&]<class Feature >(Feature*) {
360398 if (py::type::of<Feature>().is (type)) {
361- py_feature = py::cast (game. feature <Feature>(),
399+ py_feature = py::cast (gameFeatures. gameFeature <Feature>(),
362400 py::return_value_policy::reference);
363401 }
364402 });
365403 return py_feature;
366404 }
367405
368- pybind11::dict extract_feature_list (IPluginGame const & game )
406+ int unregister_feature (MOBase::IGameFeatures& gameFeatures, pybind11::object type )
369407 {
370- // constructing a dict from class name to actual object
371- py::dict dict;
408+ int count = 0 ;
372409 GameFeaturesHelper::apply ([&]<class Feature >(Feature*) {
373- dict[py::type::of<Feature>()] =
374- py::cast (game.feature <Feature>(), py::return_value_policy::reference);
375- });
376- return dict;
377- }
378-
379- std::map<std::type_index, std::any>
380- convert_feature_list (py::dict const & py_features)
381- {
382- std::map<std::type_index, std::any> features;
383- GameFeaturesHelper::apply ([&]<class Feature >(Feature*) {
384- const auto py_type = py::type::of<Feature>();
385- if (py_features.contains (py_type)) {
386- features[std::type_index (typeid (Feature))] =
387- py_features[py_type].cast <Feature*>();
410+ if (py::type::of<Feature>().is (type)) {
411+ count = gameFeatures.unregisterFeatures <Feature>();
388412 }
389413 });
390- return features ;
414+ return count ;
391415 }
392416
393417} // namespace mo2::python
0 commit comments