11#include " GameManager.hpp"
22
3+ #include < cstddef>
4+ #include < string_view>
5+
6+ #include " openvic-simulation/dataloader/Dataloader.hpp"
7+ #include " openvic-simulation/utility/Logger.hpp"
8+
39using namespace OpenVic ;
410
511GameManager::GameManager (
@@ -8,25 +14,124 @@ GameManager::GameManager(
814 new_gamestate_updated_callback ? std::move (new_gamestate_updated_callback) : []() {}
915 }, definitions_loaded { false }, mod_descriptors_loaded { false } {}
1016
11- bool GameManager::load_mod_descriptors (std::span< const memory::string> descriptors ) {
17+ bool GameManager::load_mod_descriptors () {
1218 if (mod_descriptors_loaded) {
1319 Logger::error (" Cannot load mod descriptors - already loaded!" );
1420 return false ;
1521 }
1622
17- if (!dataloader.load_mod_descriptors (descriptors, mod_manager)) {
23+ if (!dataloader.load_mod_descriptors (mod_manager)) {
1824 Logger::error (" Failed to load mod descriptors!" );
1925 return false ;
2026 }
2127 return true ;
2228}
2329
24- bool GameManager::set_roots (Dataloader::path_span_t roots, Dataloader::path_span_t replace_paths) {
25- if (!dataloader.set_roots (roots, replace_paths)) {
30+ bool GameManager::_get_mod_dependencies (Mod const * mod, memory::vector<Mod const *>& dep_list) {
31+ static constexpr size_t MAX_RECURSE = 16 ;
32+ size_t current_recurse = 0 ;
33+
34+ static auto dep_cycle = [this , ¤t_recurse](auto self, Mod const * mod, memory::vector<Mod const *>& dep_list) -> bool {
35+ bool ret = true ;
36+ for (std::string_view dep_identifier : mod->get_dependencies ()) {
37+ if (!mod_manager.has_mod_identifier (dep_identifier)) {
38+ Logger::error (" Mod \" " , mod->get_identifier (), " \" has unmet dependency \" " , dep_identifier, " \" and cannot be loaded!" );
39+ return false ;
40+ }
41+ Mod const * dep = mod_manager.get_mod_by_identifier (dep_identifier);
42+ /* The poor man's cycle checking (cycles should be very rare and hard to accomplish with vic2 modding, this is a failsafe) */
43+ if (current_recurse == MAX_RECURSE) {
44+ Logger::error (" Mod \" " , mod->get_identifier (), " \" has cyclical or broken dependency chain and cannot be loaded!" );
45+ return false ;
46+ } else {
47+ current_recurse++;
48+ ret &= self (self, dep, dep_list); /* recursively search for mod dependencies */
49+ }
50+ if (std::find (dep_list.begin (), dep_list.end (), dep) == dep_list.end ()) {
51+ dep_list.emplace_back (dep);
52+ }
53+ }
54+ return ret;
55+ };
56+ return dep_cycle (dep_cycle, mod, dep_list);
57+ }
58+
59+ bool GameManager::load_mods (
60+ Dataloader::path_vector_t & roots,
61+ Dataloader::path_vector_t & replace_paths,
62+ utility::forwardable_span<const memory::string> requested_mods
63+ ) {
64+ if (requested_mods.empty ()) {
65+ return true ;
66+ }
67+
68+ bool ret = true ;
69+
70+ memory::vector<Mod const *> load_list;
71+
72+ /* Check loaded mod descriptors for requested mods, using either full name or user directory name
73+ * (Historical Project Mod 0.4.6 or HPM both valid, for example), and load them plus their dependencies.
74+ */
75+ for (std::string_view requested_mod : requested_mods) {
76+ auto it = std::find_if (
77+ mod_manager.get_mods ().begin (),
78+ mod_manager.get_mods ().end (),
79+ [&requested_mod](Mod const & mod) -> bool {
80+ return mod.get_identifier () == requested_mod || mod.get_user_dir () == requested_mod;
81+ }
82+ );
83+
84+ if (it == mod_manager.get_mods ().end ()) {
85+ Logger::error (" Requested mod \" " , requested_mod, " \" does not exist!" );
86+ ret = false ;
87+ continue ;
88+ }
89+
90+ Mod const * mod_ptr = &*it;
91+ memory::vector<Mod const *> dependencies;
92+ if (!_get_mod_dependencies (mod_ptr, dependencies)) {
93+ ret = false ;
94+ continue ;
95+ }
96+
97+ /* Add mod plus dependencies to load_list in proper order. */
98+ load_list.reserve (1 + dependencies.size ());
99+ for (Mod const * dep : dependencies) {
100+ if (ret && std::find (load_list.begin (), load_list.end (), dep) == load_list.end ()) {
101+ load_list.emplace_back (dep);
102+ }
103+ }
104+ if (ret && std::find (load_list.begin (), load_list.end (), mod_ptr) == load_list.end ()) {
105+ load_list.emplace_back (mod_ptr);
106+ }
107+ }
108+
109+ /* Actually registers all roots and replace paths to be loaded by the game. */
110+ for (Mod const * mod : load_list) {
111+ roots.emplace_back (roots[0 ] / mod->get_dataloader_root_path ());
112+ for (std::string_view path : mod->get_replace_paths ()) {
113+ if (std::find (replace_paths.begin (), replace_paths.end (), path) == replace_paths.end ()) {
114+ replace_paths.emplace_back (path);
115+ }
116+ }
117+ }
118+
119+ /* Load only vanilla and push an error if mod loading failed. */
120+ if (ret) {
121+ mod_manager.set_loaded_mods (std::move (load_list));
122+ } else {
123+ mod_manager.set_loaded_mods ({});
124+ replace_paths.clear ();
125+ roots.erase (roots.begin ()+1 , roots.end ());
126+ Logger::error (" Mod loading failed, loading base only!" );
127+ }
128+
129+ if (!dataloader.set_roots (roots, replace_paths, false )) {
26130 Logger::error (" Failed to set dataloader roots!" );
27- return false ;
131+ ret = false ;
28132 }
29- return true ;
133+
134+ return ret;
30135}
31136
32137bool GameManager::load_definitions (Dataloader::localisation_callback_t localisation_callback) {
0 commit comments