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