1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
|
#include <cstring>
#include <openvic-simulation/dataloader/Dataloader.hpp>
#include <openvic-simulation/GameManager.hpp>
#include <openvic-simulation/testing/Testing.hpp>
#include <openvic-simulation/utility/Logger.hpp>
#include <openvic-simulation/ModifierCalculationTestToggle.hpp>
using namespace OpenVic;
static void print_help(std::ostream& stream, char const* program_name) {
stream
<< "Usage: " << program_name << " [-h] [-t] [-b <path>] [path]+\n"
<< " -h : Print this help message and exit the program.\n"
<< " -t : Run tests after loading defines.\n"
<< " -b : Use the following path as the base directory (instead of searching for one).\n"
<< " -s : Use the following path as a hint to search for a base directory.\n"
<< "Any following paths are read as mod directories, with priority starting at one above the base directory.\n"
<< "(Paths with spaces need to be enclosed in \"quotes\").\n";
}
static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) {
bool ret = true;
GameManager game_manager { []() {
Logger::info("State updated");
}, nullptr };
Logger::info("===== Loading definitions... =====");
ret &= game_manager.set_roots(roots);
ret &= game_manager.load_definitions(
[](std::string_view key, Dataloader::locale_t locale, std::string_view localisation) -> bool {
return true;
}
);
if (run_tests) {
Testing testing { game_manager.get_definition_manager() };
std::cout << std::endl << "Testing Loaded" << std::endl << std::endl;
testing.execute_all_scripts();
testing.report_results();
std::cout << "Testing Executed" << std::endl << std::endl;
}
Logger::info("===== Setting up instance... =====");
ret &= game_manager.setup_instance(
game_manager.get_definition_manager().get_history_manager().get_bookmark_manager().get_bookmark_by_index(0)
);
Logger::info("===== Starting game session... =====");
ret &= game_manager.start_game_session();
// This triggers a gamestate update
ret &= game_manager.update_clock();
// TODO - REMOVE TEST CODE
Logger::info("===== Ranking system test... =====");
if (game_manager.get_instance_manager()) {
const auto print_ranking_list = [](std::string_view title, std::vector<CountryInstance*> const& countries) -> void {
std::string text;
for (CountryInstance const* country : countries) {
text += StringUtils::append_string_views(
"\n ", country->get_identifier(),
" - Total #", std::to_string(country->get_total_rank()), " (", country->get_total_score().to_string(1),
"), Prestige #", std::to_string(country->get_prestige_rank()), " (", country->get_prestige().to_string(1),
"), Industry #", std::to_string(country->get_industrial_rank()), " (", country->get_industrial_power().to_string(1),
"), Military #", std::to_string(country->get_military_rank()), " (", country->get_military_power().to_string(1), ")"
);
}
Logger::info(title, ":", text);
};
CountryInstanceManager const& country_instance_manager =
game_manager.get_instance_manager()->get_country_instance_manager();
print_ranking_list("Great Powers", country_instance_manager.get_great_powers());
print_ranking_list("Secondary Powers", country_instance_manager.get_secondary_powers());
print_ranking_list("All countries", country_instance_manager.get_total_ranking());
#if OV_MODIFIER_CALCULATION_TEST
Logger::info("Comparing resultant modifier calculation methods...");
std::vector<ProvinceInstance> const& provinces =
game_manager.get_instance_manager()->get_map_instance().get_province_instances();
std::vector<ProvinceInstance> const& provinces_no_add =
game_manager.get_instance_manager_no_add()->get_map_instance().get_province_instances();
ModifierManager const& modifier_manager = game_manager.get_definition_manager().get_modifier_manager();
if (provinces.size() != provinces_no_add.size()) {
Logger::error("ProvinceInstance count mismatch between add and no-add instances!");
ret = false;
} else {
for (size_t idx = 0; idx < provinces.size(); ++idx) {
ProvinceInstance const& province = provinces[idx];
ProvinceInstance const& province_no_add = provinces_no_add[idx];
if (province.get_identifier() != province_no_add.get_identifier()) {
Logger::error("ProvinceInstance mismatch at index ", idx, " between add and no-add instances!");
ret = false;
continue;
}
if (province.get_modifier_sum().get_value_sum().empty()) {
Logger::error("ProvinceInstance has no modifiers at ID ", province.get_identifier(), "!");
ret = false;
}
for (ModifierManager::modifier_effect_registry_t::storage_type const* modifier_effects : {
&modifier_manager.get_leader_modifier_effects(),
&modifier_manager.get_unit_terrain_modifier_effects(),
&modifier_manager.get_shared_tech_country_modifier_effects(),
&modifier_manager.get_technology_modifier_effects(),
&modifier_manager.get_base_country_modifier_effects(),
&modifier_manager.get_base_province_modifier_effects(),
&modifier_manager.get_terrain_modifier_effects()
}) {
for (ModifierEffect const& effect : *modifier_effects) {
const fixed_point_t value = province.get_modifier_effect_value(effect);
const fixed_point_t value_no_add = province_no_add.get_modifier_effect_value(effect);
if (value != value_no_add) {
Logger::error(
"ProvinceInstance modifier effect value mismatch for effect ", effect.get_identifier(),
" at ID ", province.get_identifier(), " between add (", value.to_string(), ") and no-add (",
value_no_add.to_string(), ") instances!"
);
ret = false;
continue;
}
std::vector<ModifierSum::modifier_entry_t> contributions = province.get_contributing_modifiers(effect);
std::vector<ModifierSum::modifier_entry_t> contributions_no_add =
province_no_add.get_contributing_modifiers(effect);
if (contributions.size() != contributions_no_add.size()) {
Logger::error(
"ProvinceInstance modifier effect contributing modifier count mismatch for effect ",
effect.get_identifier(), " at ID ", province.get_identifier(), " between add (",
contributions.size(), ") and no-add (", contributions_no_add.size(), ") instances!"
);
ret = false;
continue;
}
for (size_t cidx = 0; cidx < contributions.size(); ++cidx) {
ModifierSum::modifier_entry_t const& contribution = contributions[cidx];
ModifierSum::modifier_entry_t const& contribution_no_add = contributions_no_add[cidx];
if (contribution != contribution_no_add) {
Logger::error(
"ProvinceInstance modifier effect contributing modifier mismatch for effect ",
effect.get_identifier(), " at ID ", province.get_identifier(), " between add (",
contribution.to_string(), ") and no-add (", contribution_no_add.to_string(), ") instances!"
);
ret = false;
continue;
}
}
}
}
}
}
#endif
} else {
Logger::error("Instance manager not available!");
ret = false;
}
return ret;
}
/*
$ program [-h] [-t] [-b] [path]+
*/
int main(int argc, char const* argv[]) {
Logger::set_logger_funcs();
char const* program_name = StringUtils::get_filename(argc > 0 ? argv[0] : nullptr, "<program>");
fs::path root;
bool run_tests = false;
int argn = 0;
/* Reads the next argument and converts it to a path via path_transform. If reading or converting fails, an error
* message and the help text are displayed, along with returning false to signify the program should exit.
*/
const auto _read = [&root, &argn, argc, argv, program_name](
std::string_view command, std::string_view path_use, auto path_transform) -> bool {
if (root.empty()) {
if (++argn < argc) {
char const* path = argv[argn];
root = path_transform(path);
if (!root.empty()) {
return true;
} else {
std::cerr << "Empty path after giving \"" << path << "\" to " << path_use
<< " command line argument \"" << command << "\"." << std::endl;
}
} else {
std::cerr << "Missing path after " << path_use << " command line argument \"" << command << "\"." << std::endl;
}
} else {
std::cerr << "Duplicate " << path_use << " command line argument \"-b\"." << std::endl;
}
print_help(std::cerr, program_name);
return false;
};
while (++argn < argc) {
char const* arg = argv[argn];
if (strcmp(arg, "-h") == 0) {
print_help(std::cout, program_name);
return 0;
} else if (strcmp(arg, "-t") == 0) {
run_tests = true;
} else if (strcmp(arg, "-b") == 0) {
if (!_read("-b", "base directory", std::identity {})) {
return -1;
}
} else if (strcmp(arg, "-s") == 0) {
if (!_read("-s", "search hint", Dataloader::search_for_game_path)) {
return -1;
}
} else {
break;
}
}
if (root.empty()) {
root = Dataloader::search_for_game_path();
if (root.empty()) {
std::cerr << "Search for base directory path failed!" << std::endl;
print_help(std::cerr, program_name);
return -1;
}
}
Dataloader::path_vector_t roots { root };
while (argn < argc) {
static const fs::path mod_directory = "mod";
roots.emplace_back(root / mod_directory / argv[argn++]);
}
std::cout << "!!! HEADLESS SIMULATION START !!!" << std::endl;
const bool ret = run_headless(roots, run_tests);
std::cout << "!!! HEADLESS SIMULATION END !!!" << std::endl;
std::cout << "\nLoad returned: " << (ret ? "SUCCESS" : "FAILURE") << std::endl;
std::cout << "\nLogger Summary: Info = " << Logger::get_info_count() << ", Warning = " << Logger::get_warning_count()
<< ", Error = " << Logger::get_error_count() << std::endl;
return ret ? 0 : -1;
}
|