rewrite ServerState dependency management
This commit is contained in:
+2
-2
@@ -123,10 +123,10 @@ set(SOURCES
|
|||||||
src/ServerState.cc
|
src/ServerState.cc
|
||||||
src/Shell.cc
|
src/Shell.cc
|
||||||
src/StaticGameData.cc
|
src/StaticGameData.cc
|
||||||
|
src/StepGraph.cc
|
||||||
src/TeamIndex.cc
|
src/TeamIndex.cc
|
||||||
src/Text.cc
|
src/Text.cc
|
||||||
src/TextArchive.cc
|
src/TextIndex.cc
|
||||||
src/UnicodeTextSet.cc
|
|
||||||
src/Version.cc
|
src/Version.cc
|
||||||
src/WordSelectTable.cc
|
src/WordSelectTable.cc
|
||||||
)
|
)
|
||||||
|
|||||||
+37
-48
@@ -1493,24 +1493,20 @@ Action a_show_ep3_cards(
|
|||||||
+[](Arguments& args) {
|
+[](Arguments& args) {
|
||||||
bool one_line = args.get<bool>("one-line");
|
bool one_line = args.get<bool>("one-line");
|
||||||
|
|
||||||
Episode3::CardIndex card_index(
|
ServerState s;
|
||||||
"system/ep3/card-definitions.mnr",
|
s.load_objects("ep3_data");
|
||||||
"system/ep3/card-definitions.mnrd",
|
|
||||||
"system/ep3/card-text.mnr",
|
unique_ptr<BinaryTextSet> text_english;
|
||||||
"system/ep3/card-text.mnrd",
|
|
||||||
"system/ep3/card-dice-text.mnr",
|
|
||||||
"system/ep3/card-dice-text.mnrd");
|
|
||||||
unique_ptr<TextArchive> text_english;
|
|
||||||
try {
|
try {
|
||||||
JSON json = JSON::parse(load_file("system/ep3/text-english.json"));
|
JSON json = JSON::parse(load_file("system/ep3/text-english.json"));
|
||||||
text_english = make_unique<TextArchive>(json);
|
text_english = make_unique<BinaryTextSet>(json);
|
||||||
} catch (const exception& e) {
|
} catch (const exception& e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto card_ids = card_index.all_ids();
|
auto card_ids = s.ep3_card_index->all_ids();
|
||||||
log_info("%zu card definitions", card_ids.size());
|
log_info("%zu card definitions", card_ids.size());
|
||||||
for (uint32_t card_id : card_ids) {
|
for (uint32_t card_id : card_ids) {
|
||||||
auto entry = card_index.definition_for_id(card_id);
|
auto entry = s.ep3_card_index->definition_for_id(card_id);
|
||||||
string s = entry->def.str(one_line, text_english.get());
|
string s = entry->def.str(one_line, text_english.get());
|
||||||
if (one_line) {
|
if (one_line) {
|
||||||
fprintf(stdout, "%s\n", s.c_str());
|
fprintf(stdout, "%s\n", s.c_str());
|
||||||
@@ -1544,18 +1540,14 @@ Action a_generate_ep3_cards_html(
|
|||||||
+[](Arguments& args) {
|
+[](Arguments& args) {
|
||||||
size_t num_threads = args.get<size_t>("threads", 0);
|
size_t num_threads = args.get<size_t>("threads", 0);
|
||||||
|
|
||||||
Episode3::CardIndex card_index(
|
ServerState s;
|
||||||
"system/ep3/card-definitions.mnr",
|
s.load_objects("ep3_data");
|
||||||
"system/ep3/card-definitions.mnrd",
|
s.load_objects("text_index");
|
||||||
"system/ep3/card-text.mnr",
|
|
||||||
"system/ep3/card-text.mnrd",
|
shared_ptr<const TextSet> text_english;
|
||||||
"system/ep3/card-dice-text.mnr",
|
|
||||||
"system/ep3/card-dice-text.mnrd");
|
|
||||||
unique_ptr<TextArchive> text_english;
|
|
||||||
try {
|
try {
|
||||||
JSON json = JSON::parse(load_file("system/ep3/text-english.json"));
|
text_english = s.text_index->get(Version::GC_EP3, 1);
|
||||||
text_english = make_unique<TextArchive>(json);
|
} catch (const out_of_range&) {
|
||||||
} catch (const exception& e) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CardInfo {
|
struct CardInfo {
|
||||||
@@ -1572,11 +1564,11 @@ Action a_generate_ep3_cards_html(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
vector<CardInfo> infos;
|
vector<CardInfo> infos;
|
||||||
for (uint32_t card_id : card_index.all_ids()) {
|
for (uint32_t card_id : s.ep3_card_index->all_ids()) {
|
||||||
if (infos.size() <= card_id) {
|
if (infos.size() <= card_id) {
|
||||||
infos.resize(card_id + 1);
|
infos.resize(card_id + 1);
|
||||||
}
|
}
|
||||||
infos[card_id].ce = card_index.definition_for_id(card_id);
|
infos[card_id].ce = s.ep3_card_index->definition_for_id(card_id);
|
||||||
}
|
}
|
||||||
for (const auto& filename : list_directory_sorted("system/ep3/cardtex")) {
|
for (const auto& filename : list_directory_sorted("system/ep3/cardtex")) {
|
||||||
if ((filename[0] == 'C' || filename[0] == 'M' || filename[0] == 'L') && (filename[1] == '_')) {
|
if ((filename[0] == 'C' || filename[0] == 'M' || filename[0] == 'L') && (filename[1] == '_')) {
|
||||||
@@ -1684,20 +1676,21 @@ Action a_show_ep3_maps(
|
|||||||
human-readable format.\n",
|
human-readable format.\n",
|
||||||
+[](Arguments&) {
|
+[](Arguments&) {
|
||||||
config_log.info("Collecting Episode 3 data");
|
config_log.info("Collecting Episode 3 data");
|
||||||
Episode3::MapIndex map_index("system/ep3/maps");
|
|
||||||
Episode3::CardIndex card_index("system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnrd");
|
|
||||||
|
|
||||||
auto map_ids = map_index.all_numbers();
|
ServerState s;
|
||||||
|
s.load_objects("ep3_data");
|
||||||
|
|
||||||
|
auto map_ids = s.ep3_map_index->all_numbers();
|
||||||
log_info("%zu maps", map_ids.size());
|
log_info("%zu maps", map_ids.size());
|
||||||
for (uint32_t map_id : map_ids) {
|
for (uint32_t map_id : map_ids) {
|
||||||
auto map = map_index.for_number(map_id);
|
auto map = s.ep3_map_index->for_number(map_id);
|
||||||
const auto& vms = map->all_versions();
|
const auto& vms = map->all_versions();
|
||||||
for (size_t language = 0; language < vms.size(); language++) {
|
for (size_t language = 0; language < vms.size(); language++) {
|
||||||
if (!vms[language]) {
|
if (!vms[language]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
string s = vms[language]->map->str(&card_index, language);
|
string map_s = vms[language]->map->str(s.ep3_card_index.get(), language);
|
||||||
fprintf(stdout, "(%c) %s\n", char_for_language_code(language), s.c_str());
|
fprintf(stdout, "(%c) %s\n", char_for_language_code(language), map_s.c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -1708,26 +1701,21 @@ Action a_show_battle_params(
|
|||||||
Print the Blue Burst battle parameters from the system/blueburst directory\n\
|
Print the Blue Burst battle parameters from the system/blueburst directory\n\
|
||||||
in a human-readable format.\n",
|
in a human-readable format.\n",
|
||||||
+[](Arguments&) {
|
+[](Arguments&) {
|
||||||
BattleParamsIndex index(
|
ServerState s;
|
||||||
make_shared<string>(load_file("system/blueburst/BattleParamEntry_on.dat")),
|
s.load_objects("battle_params");
|
||||||
make_shared<string>(load_file("system/blueburst/BattleParamEntry_lab_on.dat")),
|
|
||||||
make_shared<string>(load_file("system/blueburst/BattleParamEntry_ep4_on.dat")),
|
|
||||||
make_shared<string>(load_file("system/blueburst/BattleParamEntry.dat")),
|
|
||||||
make_shared<string>(load_file("system/blueburst/BattleParamEntry_lab.dat")),
|
|
||||||
make_shared<string>(load_file("system/blueburst/BattleParamEntry_ep4.dat")));
|
|
||||||
|
|
||||||
fprintf(stdout, "Episode 1 multi\n");
|
fprintf(stdout, "Episode 1 multi\n");
|
||||||
index.get_table(false, Episode::EP1).print(stdout);
|
s.battle_params->get_table(false, Episode::EP1).print(stdout);
|
||||||
fprintf(stdout, "Episode 1 solo\n");
|
fprintf(stdout, "Episode 1 solo\n");
|
||||||
index.get_table(true, Episode::EP1).print(stdout);
|
s.battle_params->get_table(true, Episode::EP1).print(stdout);
|
||||||
fprintf(stdout, "Episode 2 multi\n");
|
fprintf(stdout, "Episode 2 multi\n");
|
||||||
index.get_table(false, Episode::EP2).print(stdout);
|
s.battle_params->get_table(false, Episode::EP2).print(stdout);
|
||||||
fprintf(stdout, "Episode 2 solo\n");
|
fprintf(stdout, "Episode 2 solo\n");
|
||||||
index.get_table(true, Episode::EP2).print(stdout);
|
s.battle_params->get_table(true, Episode::EP2).print(stdout);
|
||||||
fprintf(stdout, "Episode 4 multi\n");
|
fprintf(stdout, "Episode 4 multi\n");
|
||||||
index.get_table(false, Episode::EP4).print(stdout);
|
s.battle_params->get_table(false, Episode::EP4).print(stdout);
|
||||||
fprintf(stdout, "Episode 4 solo\n");
|
fprintf(stdout, "Episode 4 solo\n");
|
||||||
index.get_table(true, Episode::EP4).print(stdout);
|
s.battle_params->get_table(true, Episode::EP4).print(stdout);
|
||||||
});
|
});
|
||||||
|
|
||||||
Action a_parse_object_graph(
|
Action a_parse_object_graph(
|
||||||
@@ -1848,12 +1836,13 @@ Action a_diff_dol_files(
|
|||||||
|
|
||||||
Action a_replay_ep3_battle_commands(
|
Action a_replay_ep3_battle_commands(
|
||||||
"replay-ep3-battle-commands", nullptr, +[](Arguments& args) {
|
"replay-ep3-battle-commands", nullptr, +[](Arguments& args) {
|
||||||
auto card_index = make_shared<Episode3::CardIndex>("system/ep3/card-definitions.mnr", "system/ep3/card-definitions.mnrd");
|
ServerState s;
|
||||||
auto map_index = make_shared<Episode3::MapIndex>("system/ep3/maps");
|
s.load_objects("ep3_data");
|
||||||
|
|
||||||
auto random_crypt = make_shared<PSOV2Encryption>(args.get<uint32_t>("seed", 0, Arguments::IntFormat::HEX));
|
auto random_crypt = make_shared<PSOV2Encryption>(args.get<uint32_t>("seed", 0, Arguments::IntFormat::HEX));
|
||||||
Episode3::Server::Options options = {
|
Episode3::Server::Options options = {
|
||||||
.card_index = card_index,
|
.card_index = s.ep3_card_index,
|
||||||
.map_index = map_index,
|
.map_index = s.ep3_map_index,
|
||||||
.behavior_flags = 0x0092,
|
.behavior_flags = 0x0092,
|
||||||
.random_crypt = random_crypt,
|
.random_crypt = random_crypt,
|
||||||
.tournament = nullptr,
|
.tournament = nullptr,
|
||||||
@@ -1897,7 +1886,7 @@ Action a_run_server_replay_log(
|
|||||||
|
|
||||||
shared_ptr<struct event_base> base(event_base_new(), event_base_free);
|
shared_ptr<struct event_base> base(event_base_new(), event_base_free);
|
||||||
auto state = make_shared<ServerState>(base, config_filename, is_replay);
|
auto state = make_shared<ServerState>(base, config_filename, is_replay);
|
||||||
state->init();
|
state->load_objects("all");
|
||||||
|
|
||||||
shared_ptr<DNSServer> dns_server;
|
shared_ptr<DNSServer> dns_server;
|
||||||
if (state->dns_server_port && !is_replay) {
|
if (state->dns_server_port && !is_replay) {
|
||||||
|
|||||||
+23
-43
@@ -120,17 +120,24 @@ General commands:\n\
|
|||||||
\n\
|
\n\
|
||||||
Server commands:\n\
|
Server commands:\n\
|
||||||
reload ITEM [ITEM...]\n\
|
reload ITEM [ITEM...]\n\
|
||||||
Reload various parts of the server configuration. ITEMs can be:\n\
|
Reload various parts of the server configuration. When you reload any item,\n\
|
||||||
licenses - reload the license index file\n\
|
any other item that depends on it will be reloaded as well. The items are:\n\
|
||||||
patches - reindex the PC and BB patch directories\n\
|
all - reindex/reload everything\n\
|
||||||
battle-params - reload the enemy stats files\n\
|
battle-params - reload the BB enemy stats files\n\
|
||||||
level-table - reload the level-up tables\n\
|
bb-private-keys - reload BB private keys\n\
|
||||||
item-tables - reload the item generation tables\n\
|
|
||||||
ep3 - reload Episode 3 card definitions and maps (not download quests)\n\
|
|
||||||
quests - reindex all quests (including Episode 3 download quests)\n\
|
|
||||||
functions - recompile all client-side functions\n\
|
|
||||||
dol-files - reindex all DOL files\n\
|
|
||||||
config - reload most fields from config.json\n\
|
config - reload most fields from config.json\n\
|
||||||
|
dol-files - reindex all DOL files\n\
|
||||||
|
drop-tables - reload drop tables\n\
|
||||||
|
ep3-data - reload Episode 3 cards and maps (not download quests)\n\
|
||||||
|
functions - recompile all client-side patches and functions\n\
|
||||||
|
item-definitions - reload item definitions files\n\
|
||||||
|
level-table - reload the level-up tables\n\
|
||||||
|
licenses - reindex user licenses\n\
|
||||||
|
patch-indexes - reindex the PC and BB patch directories\n\
|
||||||
|
quest-index - reindex all quests (including Episode3 download quests)\n\
|
||||||
|
teams - reindex all BB teams\n\
|
||||||
|
text-index - reload in-game text\n\
|
||||||
|
word-select-table - regenerate the Word Select translation table\n\
|
||||||
Reloading will not affect items that are in use; for example, if an Episode\n\
|
Reloading will not affect items that are in use; for example, if an Episode\n\
|
||||||
3 battle is in progress, it will continue to use the previous map and card\n\
|
3 battle is in progress, it will continue to use the previous map and card\n\
|
||||||
definitions. Similarly, BB clients are not forced to disconnect or reload\n\
|
definitions. Similarly, BB clients are not forced to disconnect or reload\n\
|
||||||
@@ -285,41 +292,14 @@ Proxy session commands:\n\
|
|||||||
if (types.empty()) {
|
if (types.empty()) {
|
||||||
throw invalid_argument("no data type given");
|
throw invalid_argument("no data type given");
|
||||||
}
|
}
|
||||||
for (const string& type : types) {
|
for (auto& type : types) {
|
||||||
if (type == "licenses") {
|
for (char& ch : type) {
|
||||||
this->state->load_licenses();
|
if (ch == '-') {
|
||||||
} else if (type == "teams") {
|
ch = '_';
|
||||||
this->state->load_teams();
|
}
|
||||||
} else if (type == "patches") {
|
|
||||||
this->state->load_patch_indexes();
|
|
||||||
} else if (type == "battle-params") {
|
|
||||||
this->state->load_battle_params();
|
|
||||||
} else if (type == "level-table") {
|
|
||||||
this->state->load_level_table();
|
|
||||||
} else if (type == "item-tables") {
|
|
||||||
this->state->load_item_name_index();
|
|
||||||
this->state->load_item_tables();
|
|
||||||
} else if (type == "word-select") {
|
|
||||||
this->state->load_word_select_table();
|
|
||||||
} else if (type == "ep3") {
|
|
||||||
this->state->load_ep3_data();
|
|
||||||
} else if (type == "quests") {
|
|
||||||
this->state->load_quest_index();
|
|
||||||
} else if (type == "functions") {
|
|
||||||
auto config_json = this->state->load_config();
|
|
||||||
this->state->compile_functions();
|
|
||||||
} else if (type == "dol-files") {
|
|
||||||
auto config_json = this->state->load_config();
|
|
||||||
this->state->load_dol_files();
|
|
||||||
} else if (type == "config") {
|
|
||||||
auto config_json = this->state->load_config();
|
|
||||||
this->state->parse_config(config_json, true);
|
|
||||||
this->state->resolve_ep3_card_names();
|
|
||||||
this->state->load_teams();
|
|
||||||
} else {
|
|
||||||
throw invalid_argument("incorrect data type");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this->state->load_objects(types);
|
||||||
|
|
||||||
} else if (command_name == "add-license") {
|
} else if (command_name == "add-license") {
|
||||||
auto l = this->state->license_index->create_license();
|
auto l = this->state->license_index->create_license();
|
||||||
|
|||||||
+194
-198
@@ -25,137 +25,31 @@ ServerState::QuestF960Result::QuestF960Result(const JSON& json, std::shared_ptr<
|
|||||||
this->probability_upgrade = json.get_int("ProbabilityUpgrade", 0);
|
this->probability_upgrade = json.get_int("ProbabilityUpgrade", 0);
|
||||||
for (size_t day = 0; day < 7; day++) {
|
for (size_t day = 0; day < 7; day++) {
|
||||||
for (const auto& item_it : json.get_list(day_names[day])) {
|
for (const auto& item_it : json.get_list(day_names[day])) {
|
||||||
this->results[day].emplace_back(name_index->parse_item_description(Version::BB_V4, item_it->as_string()));
|
this->results[day].emplace_back(name_index->parse_item_description(item_it->as_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ServerState::ServerState() : creation_time(now()) {
|
||||||
|
this->create_load_step_graph();
|
||||||
|
}
|
||||||
|
|
||||||
ServerState::ServerState(shared_ptr<struct event_base> base, const string& config_filename, bool is_replay)
|
ServerState::ServerState(shared_ptr<struct event_base> base, const string& config_filename, bool is_replay)
|
||||||
: creation_time(now()),
|
: creation_time(now()),
|
||||||
base(base),
|
base(base),
|
||||||
config_filename(config_filename),
|
config_filename(config_filename),
|
||||||
is_replay(is_replay),
|
is_replay(is_replay),
|
||||||
dns_server_port(0),
|
player_files_manager(this->base ? make_shared<PlayerFilesManager>(base) : nullptr),
|
||||||
ip_stack_debug(false),
|
destroy_lobbies_event(this->base ? event_new(base.get(), -1, EV_TIMEOUT, &ServerState::dispatch_destroy_lobbies, this) : nullptr, event_free) {
|
||||||
allow_unregistered_users(false),
|
this->create_load_step_graph();
|
||||||
allow_pc_nte(false),
|
}
|
||||||
use_temp_licenses_for_prototypes(true),
|
|
||||||
allow_dc_pc_games(false),
|
|
||||||
allow_gc_xb_games(true),
|
|
||||||
allowed_drop_modes_v1_v2_normal(0x1F),
|
|
||||||
allowed_drop_modes_v1_v2_battle(0x07),
|
|
||||||
allowed_drop_modes_v1_v2_challenge(0x07),
|
|
||||||
allowed_drop_modes_v3_normal(0x1F),
|
|
||||||
allowed_drop_modes_v3_battle(0x07),
|
|
||||||
allowed_drop_modes_v3_challenge(0x07),
|
|
||||||
allowed_drop_modes_v4_normal(0x1D), // CLIENT not allowed
|
|
||||||
allowed_drop_modes_v4_battle(0x05),
|
|
||||||
allowed_drop_modes_v4_challenge(0x05),
|
|
||||||
default_drop_mode_v1_v2_normal(Lobby::DropMode::CLIENT),
|
|
||||||
default_drop_mode_v1_v2_battle(Lobby::DropMode::CLIENT),
|
|
||||||
default_drop_mode_v1_v2_challenge(Lobby::DropMode::CLIENT),
|
|
||||||
default_drop_mode_v3_normal(Lobby::DropMode::CLIENT),
|
|
||||||
default_drop_mode_v3_battle(Lobby::DropMode::CLIENT),
|
|
||||||
default_drop_mode_v3_challenge(Lobby::DropMode::CLIENT),
|
|
||||||
default_drop_mode_v4_normal(Lobby::DropMode::SERVER_SHARED),
|
|
||||||
default_drop_mode_v4_battle(Lobby::DropMode::SERVER_SHARED),
|
|
||||||
default_drop_mode_v4_challenge(Lobby::DropMode::SERVER_SHARED),
|
|
||||||
persistent_game_idle_timeout_usecs(0),
|
|
||||||
ep3_send_function_call_enabled(false),
|
|
||||||
catch_handler_exceptions(true),
|
|
||||||
ep3_infinite_meseta(false),
|
|
||||||
ep3_defeat_player_meseta_rewards({400, 500, 600, 700, 800}),
|
|
||||||
ep3_defeat_com_meseta_rewards({100, 200, 300, 400, 500}),
|
|
||||||
ep3_final_round_meseta_bonus(300),
|
|
||||||
ep3_jukebox_is_free(false),
|
|
||||||
ep3_behavior_flags(0),
|
|
||||||
hide_download_commands(true),
|
|
||||||
run_shell_behavior(RunShellBehavior::DEFAULT),
|
|
||||||
cheat_mode_behavior(BehaviorSwitch::OFF_BY_DEFAULT),
|
|
||||||
bb_global_exp_multiplier(1),
|
|
||||||
ep3_card_auction_points(0),
|
|
||||||
ep3_card_auction_min_size(0),
|
|
||||||
ep3_card_auction_max_size(0),
|
|
||||||
player_files_manager(make_shared<PlayerFilesManager>(base)),
|
|
||||||
destroy_lobbies_event(event_new(base.get(), -1, EV_TIMEOUT, &ServerState::dispatch_destroy_lobbies, this), event_free),
|
|
||||||
next_lobby_id(1),
|
|
||||||
pre_lobby_event(0),
|
|
||||||
ep3_menu_song(-1),
|
|
||||||
local_address(0),
|
|
||||||
external_address(0),
|
|
||||||
proxy_allow_save_files(true),
|
|
||||||
proxy_enable_login_options(false) {}
|
|
||||||
|
|
||||||
void ServerState::init() {
|
void ServerState::load_objects(const std::string& what) {
|
||||||
vector<shared_ptr<Lobby>> non_v1_only_lobbies;
|
this->load_step_graph.run(what);
|
||||||
vector<shared_ptr<Lobby>> ep3_only_lobbies;
|
}
|
||||||
|
|
||||||
for (size_t x = 0; x < 20; x++) {
|
void ServerState::load_objects(const std::vector<std::string>& what) {
|
||||||
auto lobby_name = string_printf("LOBBY%zu", x + 1);
|
this->load_step_graph.run(what);
|
||||||
bool allow_v1 = (x <= 9);
|
|
||||||
bool allow_non_ep3 = (x <= 14);
|
|
||||||
|
|
||||||
shared_ptr<Lobby> l = this->create_lobby(false);
|
|
||||||
l->set_flag(Lobby::Flag::PUBLIC);
|
|
||||||
l->set_flag(Lobby::Flag::DEFAULT);
|
|
||||||
l->set_flag(Lobby::Flag::PERSISTENT);
|
|
||||||
if (allow_non_ep3) {
|
|
||||||
if (allow_v1) {
|
|
||||||
l->allow_version(Version::DC_NTE);
|
|
||||||
l->allow_version(Version::DC_V1_11_2000_PROTOTYPE);
|
|
||||||
l->allow_version(Version::DC_V1);
|
|
||||||
}
|
|
||||||
l->allow_version(Version::DC_V2);
|
|
||||||
l->allow_version(Version::PC_NTE);
|
|
||||||
l->allow_version(Version::PC_V2);
|
|
||||||
l->allow_version(Version::GC_NTE);
|
|
||||||
l->allow_version(Version::GC_V3);
|
|
||||||
l->allow_version(Version::XB_V3);
|
|
||||||
l->allow_version(Version::BB_V4);
|
|
||||||
}
|
|
||||||
l->allow_version(Version::GC_EP3_NTE);
|
|
||||||
l->allow_version(Version::GC_EP3);
|
|
||||||
|
|
||||||
l->block = x + 1;
|
|
||||||
l->name = lobby_name;
|
|
||||||
l->max_clients = 12;
|
|
||||||
if (!allow_non_ep3) {
|
|
||||||
l->episode = Episode::EP3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (allow_non_ep3) {
|
|
||||||
this->public_lobby_search_order.emplace_back(l);
|
|
||||||
} else {
|
|
||||||
ep3_only_lobbies.emplace_back(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Annoyingly, the CARD lobbies should be searched first, but are sent at the
|
|
||||||
// end of the lobby list command, so we have to change the search order
|
|
||||||
// manually here.
|
|
||||||
this->public_lobby_search_order.insert(
|
|
||||||
this->public_lobby_search_order.begin(),
|
|
||||||
ep3_only_lobbies.begin(),
|
|
||||||
ep3_only_lobbies.end());
|
|
||||||
|
|
||||||
// Load all the necessary data
|
|
||||||
auto config = this->load_config();
|
|
||||||
this->collect_network_addresses();
|
|
||||||
this->load_item_name_index();
|
|
||||||
this->parse_config(config, false);
|
|
||||||
this->load_bb_private_keys();
|
|
||||||
this->load_licenses();
|
|
||||||
this->load_teams();
|
|
||||||
this->load_patch_indexes();
|
|
||||||
this->load_battle_params();
|
|
||||||
this->load_level_table();
|
|
||||||
this->load_item_tables();
|
|
||||||
this->load_word_select_table();
|
|
||||||
this->load_ep3_data();
|
|
||||||
this->resolve_ep3_card_names();
|
|
||||||
this->load_quest_index();
|
|
||||||
this->compile_functions();
|
|
||||||
this->load_dol_files();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerState::add_client_to_available_lobby(shared_ptr<Client> c) {
|
void ServerState::add_client_to_available_lobby(shared_ptr<Client> c) {
|
||||||
@@ -578,20 +472,6 @@ shared_ptr<const string> ServerState::load_bb_file(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerState::collect_network_addresses() {
|
|
||||||
config_log.info("Reading network addresses");
|
|
||||||
this->all_addresses = get_local_addresses();
|
|
||||||
for (const auto& it : this->all_addresses) {
|
|
||||||
string addr_str = string_for_address(it.second);
|
|
||||||
config_log.info("Found interface: %s = %s", it.first.c_str(), addr_str.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JSON ServerState::load_config() const {
|
|
||||||
config_log.info("Loading configuration");
|
|
||||||
return JSON::parse(load_file(this->config_filename));
|
|
||||||
}
|
|
||||||
|
|
||||||
static vector<PortConfiguration> parse_port_configuration(const JSON& json) {
|
static vector<PortConfiguration> parse_port_configuration(const JSON& json) {
|
||||||
vector<PortConfiguration> ret;
|
vector<PortConfiguration> ret;
|
||||||
for (const auto& item_json_it : json.as_dict()) {
|
for (const auto& item_json_it : json.as_dict()) {
|
||||||
@@ -605,8 +485,22 @@ static vector<PortConfiguration> parse_port_configuration(const JSON& json) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerState::parse_config(const JSON& json, bool is_reload) {
|
void ServerState::collect_network_addresses() {
|
||||||
config_log.info("Parsing configuration");
|
config_log.info("Reading network addresses");
|
||||||
|
this->all_addresses = get_local_addresses();
|
||||||
|
for (const auto& it : this->all_addresses) {
|
||||||
|
string addr_str = string_for_address(it.second);
|
||||||
|
config_log.info("Found interface: %s = %s", it.first.c_str(), addr_str.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerState::load_config() {
|
||||||
|
if (this->config_filename.empty()) {
|
||||||
|
throw logic_error("configuration filename is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
config_log.info("Loading configuration");
|
||||||
|
auto json = JSON::parse(load_file(this->config_filename));
|
||||||
|
|
||||||
auto parse_behavior_switch = [&](const string& json_key, BehaviorSwitch default_value) -> ServerState::BehaviorSwitch {
|
auto parse_behavior_switch = [&](const string& json_key, BehaviorSwitch default_value) -> ServerState::BehaviorSwitch {
|
||||||
try {
|
try {
|
||||||
@@ -629,7 +523,7 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
|||||||
|
|
||||||
this->name = json.at("ServerName").as_string();
|
this->name = json.at("ServerName").as_string();
|
||||||
|
|
||||||
if (!is_reload) {
|
if (!this->config_loaded) {
|
||||||
try {
|
try {
|
||||||
this->username = json.at("User").as_string();
|
this->username = json.at("User").as_string();
|
||||||
if (this->username == "$SUDO_USER") {
|
if (this->username == "$SUDO_USER") {
|
||||||
@@ -772,12 +666,17 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
for (const auto& it : json.get_dict("CardAuctionPool")) {
|
for (const auto& it : json.get_dict("CardAuctionPool")) {
|
||||||
|
uint16_t card_id;
|
||||||
|
try {
|
||||||
|
card_id = this->ep3_card_index->definition_for_name_normalized(it.first)->def.card_id;
|
||||||
|
} catch (const out_of_range&) {
|
||||||
|
throw runtime_error(string_printf("Ep3 card \"%s\" in auction pool does not exist", it.first.c_str()));
|
||||||
|
}
|
||||||
this->ep3_card_auction_pool.emplace_back(
|
this->ep3_card_auction_pool.emplace_back(
|
||||||
CardAuctionPoolEntry{
|
CardAuctionPoolEntry{
|
||||||
.probability = static_cast<uint64_t>(it.second->at(0).as_int()),
|
.probability = static_cast<uint64_t>(it.second->at(0).as_int()),
|
||||||
.card_id = 0,
|
.card_id = card_id,
|
||||||
.min_price = static_cast<uint16_t>(it.second->at(1).as_int()),
|
.min_price = static_cast<uint16_t>(it.second->at(1).as_int())});
|
||||||
.card_name = it.first});
|
|
||||||
}
|
}
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
@@ -788,11 +687,18 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
|||||||
if (ep3_trap_cards_json.size() != 5) {
|
if (ep3_trap_cards_json.size() != 5) {
|
||||||
throw runtime_error("Episode3TrapCards must be a list of 5 lists");
|
throw runtime_error("Episode3TrapCards must be a list of 5 lists");
|
||||||
}
|
}
|
||||||
this->ep3_trap_card_names.clear();
|
for (size_t trap_type = 0; trap_type < 5; trap_type++) {
|
||||||
for (const auto& trap_type_it : ep3_trap_cards_json) {
|
auto& trap_card_ids = this->ep3_trap_card_ids[trap_type];
|
||||||
auto& names = this->ep3_trap_card_names.emplace_back();
|
for (const auto& card_it : ep3_trap_cards_json.at(trap_type)->as_list()) {
|
||||||
for (const auto& card_it : trap_type_it->as_list()) {
|
try {
|
||||||
names.emplace_back(card_it->as_string());
|
const auto& card = this->ep3_card_index->definition_for_name_normalized(card_it->as_string());
|
||||||
|
if (card->def.type != Episode3::CardType::ASSIST) {
|
||||||
|
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list is not an assist card", name.c_str()));
|
||||||
|
}
|
||||||
|
trap_card_ids.emplace_back(card->def.card_id);
|
||||||
|
} catch (const out_of_range&) {
|
||||||
|
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list does not exist", name.c_str()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -800,6 +706,7 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!this->is_replay) {
|
if (!this->is_replay) {
|
||||||
|
this->ep3_lobby_banners.clear();
|
||||||
for (const auto& it : json.get("Episode3LobbyBanners", JSON::list()).as_list()) {
|
for (const auto& it : json.get("Episode3LobbyBanners", JSON::list()).as_list()) {
|
||||||
Image img("system/ep3/banners/" + it->at(2).as_string());
|
Image img("system/ep3/banners/" + it->at(2).as_string());
|
||||||
string gvm = encode_gvm(img, img.get_has_alpha() ? GVRDataFormat::RGB5A3 : GVRDataFormat::RGB565);
|
string gvm = encode_gvm(img, img.get_has_alpha() ? GVRDataFormat::RGB5A3 : GVRDataFormat::RGB565);
|
||||||
@@ -889,13 +796,11 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
|||||||
|
|
||||||
set_log_levels_from_json(json.get("LogLevels", JSON::dict()));
|
set_log_levels_from_json(json.get("LogLevels", JSON::dict()));
|
||||||
|
|
||||||
if (!is_reload) {
|
try {
|
||||||
try {
|
this->run_shell_behavior = json.at("RunInteractiveShell").as_bool()
|
||||||
this->run_shell_behavior = json.at("RunInteractiveShell").as_bool()
|
? ServerState::RunShellBehavior::ALWAYS
|
||||||
? ServerState::RunShellBehavior::ALWAYS
|
: ServerState::RunShellBehavior::NEVER;
|
||||||
: ServerState::RunShellBehavior::NEVER;
|
} catch (const out_of_range&) {
|
||||||
} catch (const out_of_range&) {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this->allow_dc_pc_games = json.get_bool("AllowDCPCGames", this->allow_dc_pc_games);
|
this->allow_dc_pc_games = json.get_bool("AllowDCPCGames", this->allow_dc_pc_games);
|
||||||
@@ -913,13 +818,11 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
|||||||
|
|
||||||
this->ep3_menu_song = json.get_int("Episode3MenuSong", this->ep3_menu_song);
|
this->ep3_menu_song = json.get_int("Episode3MenuSong", this->ep3_menu_song);
|
||||||
|
|
||||||
if (!is_reload) {
|
try {
|
||||||
try {
|
this->quest_category_index = make_shared<QuestCategoryIndex>(json.at("QuestCategories"));
|
||||||
this->quest_category_index = make_shared<QuestCategoryIndex>(json.at("QuestCategories"));
|
} catch (const exception& e) {
|
||||||
} catch (const exception& e) {
|
throw runtime_error(string_printf(
|
||||||
throw runtime_error(string_printf(
|
"QuestCategories is missing or invalid in config.json (%s) - see config.example.json for an example", e.what()));
|
||||||
"QuestCategories is missing or invalid in config.json (%s) - see config.example.json for an example", e.what()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
config_log.info("Creating menus");
|
config_log.info("Creating menus");
|
||||||
@@ -1071,6 +974,8 @@ void ServerState::parse_config(const JSON& json, bool is_reload) {
|
|||||||
}
|
}
|
||||||
} catch (const out_of_range&) {
|
} catch (const out_of_range&) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this->config_loaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerState::load_bb_private_keys() {
|
void ServerState::load_bb_private_keys() {
|
||||||
@@ -1093,7 +998,6 @@ void ServerState::load_licenses() {
|
|||||||
void ServerState::load_teams() {
|
void ServerState::load_teams() {
|
||||||
config_log.info("Indexing teams");
|
config_log.info("Indexing teams");
|
||||||
this->team_index = make_shared<TeamIndex>("system/teams", this->team_reward_defs_json);
|
this->team_index = make_shared<TeamIndex>("system/teams", this->team_reward_defs_json);
|
||||||
this->team_reward_defs_json = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerState::load_patch_indexes() {
|
void ServerState::load_patch_indexes() {
|
||||||
@@ -1393,37 +1297,6 @@ void ServerState::load_ep3_data() {
|
|||||||
config_log.info("Loaded Episode 3 tournament state");
|
config_log.info("Loaded Episode 3 tournament state");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerState::resolve_ep3_card_names() {
|
|
||||||
config_log.info("Resolving Episode 3 card names");
|
|
||||||
for (auto& e : this->ep3_card_auction_pool) {
|
|
||||||
try {
|
|
||||||
const auto& card = this->ep3_card_index->definition_for_name_normalized(e.card_name);
|
|
||||||
e.card_id = card->def.card_id;
|
|
||||||
} catch (const out_of_range&) {
|
|
||||||
throw runtime_error(string_printf("Ep3 card \"%s\" in auction pool does not exist", e.card_name.c_str()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t z = 0; z < this->ep3_trap_card_ids.size(); z++) {
|
|
||||||
auto& ids = this->ep3_trap_card_ids[z];
|
|
||||||
ids.clear();
|
|
||||||
if (z < this->ep3_trap_card_names.size()) {
|
|
||||||
auto& names = this->ep3_trap_card_names[z];
|
|
||||||
for (const auto& name : names) {
|
|
||||||
try {
|
|
||||||
const auto& card = this->ep3_card_index->definition_for_name_normalized(name);
|
|
||||||
if (card->def.type != Episode3::CardType::ASSIST) {
|
|
||||||
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list is not an assist card", name.c_str()));
|
|
||||||
}
|
|
||||||
ids.emplace_back(card->def.card_id);
|
|
||||||
} catch (const out_of_range&) {
|
|
||||||
throw runtime_error(string_printf("Ep3 card \"%s\" in trap card list does not exist", name.c_str()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerState::load_quest_index() {
|
void ServerState::load_quest_index() {
|
||||||
config_log.info("Collecting quests");
|
config_log.info("Collecting quests");
|
||||||
this->default_quest_index = make_shared<QuestIndex>("system/quests", this->quest_category_index, false);
|
this->default_quest_index = make_shared<QuestIndex>("system/quests", this->quest_category_index, false);
|
||||||
@@ -1441,14 +1314,137 @@ void ServerState::load_dol_files() {
|
|||||||
this->dol_file_index = make_shared<DOLFileIndex>("system/dol");
|
this->dol_file_index = make_shared<DOLFileIndex>("system/dol");
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<const vector<string>> ServerState::information_contents_for_client(shared_ptr<const Client> c) const {
|
void ServerState::create_default_lobbies() {
|
||||||
return is_v1_or_v2(c->version()) ? this->information_contents_v2 : this->information_contents_v3;
|
if (this->default_lobbies_created) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->default_lobbies_created = true;
|
||||||
|
|
||||||
|
vector<shared_ptr<Lobby>> non_v1_only_lobbies;
|
||||||
|
vector<shared_ptr<Lobby>> ep3_only_lobbies;
|
||||||
|
|
||||||
|
for (size_t x = 0; x < 20; x++) {
|
||||||
|
auto lobby_name = string_printf("LOBBY%zu", x + 1);
|
||||||
|
bool allow_v1 = (x <= 9);
|
||||||
|
bool allow_non_ep3 = (x <= 14);
|
||||||
|
|
||||||
|
shared_ptr<Lobby> l = this->create_lobby(false);
|
||||||
|
l->event = this->pre_lobby_event;
|
||||||
|
l->set_flag(Lobby::Flag::PUBLIC);
|
||||||
|
l->set_flag(Lobby::Flag::DEFAULT);
|
||||||
|
l->set_flag(Lobby::Flag::PERSISTENT);
|
||||||
|
if (allow_non_ep3) {
|
||||||
|
if (allow_v1) {
|
||||||
|
l->allow_version(Version::DC_NTE);
|
||||||
|
l->allow_version(Version::DC_V1_11_2000_PROTOTYPE);
|
||||||
|
l->allow_version(Version::DC_V1);
|
||||||
|
}
|
||||||
|
l->allow_version(Version::DC_V2);
|
||||||
|
l->allow_version(Version::PC_NTE);
|
||||||
|
l->allow_version(Version::PC_V2);
|
||||||
|
l->allow_version(Version::GC_NTE);
|
||||||
|
l->allow_version(Version::GC_V3);
|
||||||
|
l->allow_version(Version::XB_V3);
|
||||||
|
l->allow_version(Version::BB_V4);
|
||||||
|
}
|
||||||
|
l->allow_version(Version::GC_EP3_NTE);
|
||||||
|
l->allow_version(Version::GC_EP3);
|
||||||
|
|
||||||
|
l->block = x + 1;
|
||||||
|
l->name = lobby_name;
|
||||||
|
l->max_clients = 12;
|
||||||
|
if (!allow_non_ep3) {
|
||||||
|
l->episode = Episode::EP3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allow_non_ep3) {
|
||||||
|
this->public_lobby_search_order.emplace_back(l);
|
||||||
|
} else {
|
||||||
|
ep3_only_lobbies.emplace_back(l);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Annoyingly, the CARD lobbies should be searched first, but are sent at the
|
||||||
|
// end of the lobby list command, so we have to change the search order
|
||||||
|
// manually here.
|
||||||
|
this->public_lobby_search_order.insert(
|
||||||
|
this->public_lobby_search_order.begin(),
|
||||||
|
ep3_only_lobbies.begin(),
|
||||||
|
ep3_only_lobbies.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<const QuestIndex> ServerState::quest_index_for_version(Version version) const {
|
void ServerState::create_load_step_graph() {
|
||||||
return is_ep3(version) ? this->ep3_download_quest_index : this->default_quest_index;
|
this->load_step_graph.add_step("all", {}, nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
void ServerState::dispatch_destroy_lobbies(evutil_socket_t, short, void* ctx) {
|
// In: none
|
||||||
reinterpret_cast<ServerState*>(ctx)->lobbies_to_destroy.clear();
|
// Out: all_addresses
|
||||||
|
this->load_step_graph.add_step("network_addresses", {"all"}, bind(&ServerState::collect_network_addresses, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: bb_private_keys
|
||||||
|
this->load_step_graph.add_step("bb_private_keys", {"all"}, bind(&ServerState::load_bb_private_keys, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: license_index
|
||||||
|
this->load_step_graph.add_step("licenses", {"all"}, bind(&ServerState::load_licenses, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: pc_patch_file_index, bb_patch_file_index, bb_data_gsl
|
||||||
|
this->load_step_graph.add_step("patch_indexes", {"all"}, bind(&ServerState::load_patch_indexes, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: ep3_map_index, ep3_card_index, ep3_card_index_trial, ep3_com_deck_index, ep3_tournament_index
|
||||||
|
this->load_step_graph.add_step("ep3_data", {"all"}, bind(&ServerState::load_ep3_data, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: function_code_index
|
||||||
|
this->load_step_graph.add_step("functions", {"all"}, bind(&ServerState::compile_functions, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: dol_file_index
|
||||||
|
this->load_step_graph.add_step("dol_files", {"all"}, bind(&ServerState::load_dol_files, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: lobbies
|
||||||
|
this->load_step_graph.add_step("lobbies", {"all"}, bind(&ServerState::create_default_lobbies, this));
|
||||||
|
|
||||||
|
// In: bb_patch_file_index
|
||||||
|
// Out: battle_params
|
||||||
|
this->load_step_graph.add_step("battle_params", {"all", "patch_indexes"}, bind(&ServerState::load_battle_params, this));
|
||||||
|
|
||||||
|
// In: bb_patch_file_index
|
||||||
|
// Out: level_table
|
||||||
|
this->load_step_graph.add_step("level_table", {"all", "patch_indexes"}, bind(&ServerState::load_level_table, this));
|
||||||
|
|
||||||
|
// In: bb_patch_file_index
|
||||||
|
// Out: text_index
|
||||||
|
this->load_step_graph.add_step("text_index", {"all", "patch_indexes"}, bind(&ServerState::load_text_index, this));
|
||||||
|
|
||||||
|
// In: text_index (optional)
|
||||||
|
// Out: word_select_table
|
||||||
|
this->load_step_graph.add_step("word_select_table", {"all"}, bind(&ServerState::load_word_select_table, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: item_parameter_tables, mag_evolution_table
|
||||||
|
this->load_step_graph.add_step("item_definitions", {"all"}, bind(&ServerState::load_item_definitions, this));
|
||||||
|
|
||||||
|
// In: text_index, item_parameter_tables
|
||||||
|
// Out: item_name_indexes
|
||||||
|
this->load_step_graph.add_step("item_name_indexes", {"all", "text_index", "item_definitions"}, bind(&ServerState::load_item_name_indexes, this));
|
||||||
|
|
||||||
|
// In: none
|
||||||
|
// Out: rare_item_sets, common_item_sets, armor_random_set, tool_random_set, weapon_random_sets, tekker_adjustment_set
|
||||||
|
this->load_step_graph.add_step("drop_tables", {"all", "item_definitions", "item_name_indexes"}, bind(&ServerState::load_drop_tables, this));
|
||||||
|
|
||||||
|
// In: all_addresses, ep3_card_index, item_name_indexes
|
||||||
|
// Out: config, ep3_lobby_banners, quest_category_index, information menus, proxy destinations menus, team_reward_defs_json
|
||||||
|
this->load_step_graph.add_step("config", {"all", "network_addresses", "ep3_data", "item_name_indexes"}, bind(&ServerState::load_config, this));
|
||||||
|
|
||||||
|
// In: team_reward_defs_json
|
||||||
|
// Out: team_index
|
||||||
|
this->load_step_graph.add_step("teams", {"all", "config"}, bind(&ServerState::load_teams, this));
|
||||||
|
|
||||||
|
// In: quest_category_index
|
||||||
|
// Out: default_quest_index, ep3_download_quest_index
|
||||||
|
this->load_step_graph.add_step("quest_index", {"all", "config"}, bind(&ServerState::load_quest_index, this));
|
||||||
}
|
}
|
||||||
|
|||||||
+66
-58
@@ -25,6 +25,7 @@
|
|||||||
#include "Menu.hh"
|
#include "Menu.hh"
|
||||||
#include "PlayerFilesManager.hh"
|
#include "PlayerFilesManager.hh"
|
||||||
#include "Quest.hh"
|
#include "Quest.hh"
|
||||||
|
#include "StepGraph.hh"
|
||||||
#include "TeamIndex.hh"
|
#include "TeamIndex.hh"
|
||||||
#include "WordSelectTable.hh"
|
#include "WordSelectTable.hh"
|
||||||
|
|
||||||
@@ -63,52 +64,56 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
|||||||
std::shared_ptr<struct event_base> base;
|
std::shared_ptr<struct event_base> base;
|
||||||
|
|
||||||
std::string config_filename;
|
std::string config_filename;
|
||||||
bool is_replay;
|
bool is_replay = false;
|
||||||
|
bool config_loaded = false;
|
||||||
|
bool default_lobbies_created = false;
|
||||||
|
|
||||||
|
StepGraph load_step_graph;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
std::unordered_map<std::string, std::shared_ptr<PortConfiguration>> name_to_port_config;
|
std::unordered_map<std::string, std::shared_ptr<PortConfiguration>> name_to_port_config;
|
||||||
std::unordered_map<uint16_t, std::shared_ptr<PortConfiguration>> number_to_port_config;
|
std::unordered_map<uint16_t, std::shared_ptr<PortConfiguration>> number_to_port_config;
|
||||||
std::string username;
|
std::string username;
|
||||||
uint16_t dns_server_port;
|
uint16_t dns_server_port = 0;
|
||||||
std::vector<std::string> ip_stack_addresses;
|
std::vector<std::string> ip_stack_addresses;
|
||||||
std::vector<std::string> ppp_stack_addresses;
|
std::vector<std::string> ppp_stack_addresses;
|
||||||
bool ip_stack_debug;
|
bool ip_stack_debug = false;
|
||||||
bool allow_unregistered_users;
|
bool allow_unregistered_users = false;
|
||||||
bool allow_pc_nte;
|
bool allow_pc_nte = false;
|
||||||
bool use_temp_licenses_for_prototypes;
|
bool use_temp_licenses_for_prototypes = true;
|
||||||
bool allow_dc_pc_games;
|
bool allow_dc_pc_games = true;
|
||||||
bool allow_gc_xb_games;
|
bool allow_gc_xb_games = true;
|
||||||
uint8_t allowed_drop_modes_v1_v2_normal;
|
uint8_t allowed_drop_modes_v1_v2_normal = 0x1F;
|
||||||
uint8_t allowed_drop_modes_v1_v2_battle;
|
uint8_t allowed_drop_modes_v1_v2_battle = 0x07;
|
||||||
uint8_t allowed_drop_modes_v1_v2_challenge;
|
uint8_t allowed_drop_modes_v1_v2_challenge = 0x07;
|
||||||
uint8_t allowed_drop_modes_v3_normal;
|
uint8_t allowed_drop_modes_v3_normal = 0x1F;
|
||||||
uint8_t allowed_drop_modes_v3_battle;
|
uint8_t allowed_drop_modes_v3_battle = 0x07;
|
||||||
uint8_t allowed_drop_modes_v3_challenge;
|
uint8_t allowed_drop_modes_v3_challenge = 0x07;
|
||||||
uint8_t allowed_drop_modes_v4_normal;
|
uint8_t allowed_drop_modes_v4_normal = 0x1D; // CLIENT not allowed
|
||||||
uint8_t allowed_drop_modes_v4_battle;
|
uint8_t allowed_drop_modes_v4_battle = 0x05;
|
||||||
uint8_t allowed_drop_modes_v4_challenge;
|
uint8_t allowed_drop_modes_v4_challenge = 0x05;
|
||||||
Lobby::DropMode default_drop_mode_v1_v2_normal;
|
Lobby::DropMode default_drop_mode_v1_v2_normal = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v1_v2_battle;
|
Lobby::DropMode default_drop_mode_v1_v2_battle = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v1_v2_challenge;
|
Lobby::DropMode default_drop_mode_v1_v2_challenge = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v3_normal;
|
Lobby::DropMode default_drop_mode_v3_normal = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v3_battle;
|
Lobby::DropMode default_drop_mode_v3_battle = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v3_challenge;
|
Lobby::DropMode default_drop_mode_v3_challenge = Lobby::DropMode::CLIENT;
|
||||||
Lobby::DropMode default_drop_mode_v4_normal;
|
Lobby::DropMode default_drop_mode_v4_normal = Lobby::DropMode::SERVER_SHARED;
|
||||||
Lobby::DropMode default_drop_mode_v4_battle;
|
Lobby::DropMode default_drop_mode_v4_battle = Lobby::DropMode::SERVER_SHARED;
|
||||||
Lobby::DropMode default_drop_mode_v4_challenge;
|
Lobby::DropMode default_drop_mode_v4_challenge = Lobby::DropMode::SERVER_SHARED;
|
||||||
QuestFlagsForDifficulty quest_flag_persist_mask;
|
QuestFlagsForDifficulty quest_flag_persist_mask;
|
||||||
uint64_t persistent_game_idle_timeout_usecs;
|
uint64_t persistent_game_idle_timeout_usecs = 0;
|
||||||
bool ep3_send_function_call_enabled;
|
bool ep3_send_function_call_enabled = false;
|
||||||
bool catch_handler_exceptions;
|
bool catch_handler_exceptions = true;
|
||||||
bool ep3_infinite_meseta;
|
bool ep3_infinite_meseta = false;
|
||||||
std::vector<uint32_t> ep3_defeat_player_meseta_rewards;
|
std::vector<uint32_t> ep3_defeat_player_meseta_rewards = {400, 500, 600, 700, 800};
|
||||||
std::vector<uint32_t> ep3_defeat_com_meseta_rewards;
|
std::vector<uint32_t> ep3_defeat_com_meseta_rewards = {100, 200, 300, 400, 500};
|
||||||
uint32_t ep3_final_round_meseta_bonus;
|
uint32_t ep3_final_round_meseta_bonus = 300;
|
||||||
bool ep3_jukebox_is_free;
|
bool ep3_jukebox_is_free = false;
|
||||||
uint32_t ep3_behavior_flags;
|
uint32_t ep3_behavior_flags = 0;
|
||||||
bool hide_download_commands;
|
bool hide_download_commands = true;
|
||||||
RunShellBehavior run_shell_behavior;
|
RunShellBehavior run_shell_behavior = RunShellBehavior::DEFAULT;
|
||||||
BehaviorSwitch cheat_mode_behavior;
|
BehaviorSwitch cheat_mode_behavior = BehaviorSwitch::OFF_BY_DEFAULT;
|
||||||
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> bb_private_keys;
|
std::vector<std::shared_ptr<const PSOBBEncryption::KeyFile>> bb_private_keys;
|
||||||
std::shared_ptr<const FunctionCodeIndex> function_code_index;
|
std::shared_ptr<const FunctionCodeIndex> function_code_index;
|
||||||
std::shared_ptr<const PatchFileIndex> pc_patch_file_index;
|
std::shared_ptr<const PatchFileIndex> pc_patch_file_index;
|
||||||
@@ -161,21 +166,19 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
|||||||
std::vector<QuestF960Result> quest_F960_success_results;
|
std::vector<QuestF960Result> quest_F960_success_results;
|
||||||
QuestF960Result quest_F960_failure_results;
|
QuestF960Result quest_F960_failure_results;
|
||||||
std::vector<ItemData> secret_lottery_results;
|
std::vector<ItemData> secret_lottery_results;
|
||||||
uint16_t bb_global_exp_multiplier;
|
uint16_t bb_global_exp_multiplier = 1;
|
||||||
|
|
||||||
std::shared_ptr<Episode3::TournamentIndex> ep3_tournament_index;
|
std::shared_ptr<Episode3::TournamentIndex> ep3_tournament_index;
|
||||||
|
|
||||||
uint16_t ep3_card_auction_points;
|
uint16_t ep3_card_auction_points = 0;
|
||||||
uint16_t ep3_card_auction_min_size;
|
uint16_t ep3_card_auction_min_size = 0;
|
||||||
uint16_t ep3_card_auction_max_size;
|
uint16_t ep3_card_auction_max_size = 0;
|
||||||
struct CardAuctionPoolEntry {
|
struct CardAuctionPoolEntry {
|
||||||
uint64_t probability;
|
uint64_t probability;
|
||||||
uint16_t card_id;
|
uint16_t card_id;
|
||||||
uint16_t min_price;
|
uint16_t min_price;
|
||||||
std::string card_name;
|
|
||||||
};
|
};
|
||||||
std::vector<CardAuctionPoolEntry> ep3_card_auction_pool;
|
std::vector<CardAuctionPoolEntry> ep3_card_auction_pool;
|
||||||
std::vector<std::vector<std::string>> ep3_trap_card_names;
|
|
||||||
std::array<std::vector<uint16_t>, 5> ep3_trap_card_ids;
|
std::array<std::vector<uint16_t>, 5> ep3_trap_card_ids;
|
||||||
struct Ep3LobbyBannerEntry {
|
struct Ep3LobbyBannerEntry {
|
||||||
uint32_t type = 1;
|
uint32_t type = 1;
|
||||||
@@ -212,26 +215,28 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
|||||||
std::unordered_set<std::shared_ptr<Lobby>> lobbies_to_destroy;
|
std::unordered_set<std::shared_ptr<Lobby>> lobbies_to_destroy;
|
||||||
std::shared_ptr<struct event> destroy_lobbies_event;
|
std::shared_ptr<struct event> destroy_lobbies_event;
|
||||||
std::vector<std::shared_ptr<Lobby>> public_lobby_search_order;
|
std::vector<std::shared_ptr<Lobby>> public_lobby_search_order;
|
||||||
std::atomic<int32_t> next_lobby_id;
|
std::atomic<int32_t> next_lobby_id = 1;
|
||||||
uint8_t pre_lobby_event;
|
uint8_t pre_lobby_event = 0;
|
||||||
int32_t ep3_menu_song;
|
int32_t ep3_menu_song = -1;
|
||||||
|
|
||||||
std::map<std::string, uint32_t> all_addresses;
|
std::map<std::string, uint32_t> all_addresses;
|
||||||
uint32_t local_address;
|
uint32_t local_address = 0;
|
||||||
uint32_t external_address;
|
uint32_t external_address = 0;
|
||||||
|
|
||||||
bool proxy_allow_save_files;
|
bool proxy_allow_save_files = true;
|
||||||
bool proxy_enable_login_options;
|
bool proxy_enable_login_options = false;
|
||||||
|
|
||||||
std::shared_ptr<ProxyServer> proxy_server;
|
std::shared_ptr<ProxyServer> proxy_server;
|
||||||
std::shared_ptr<Server> game_server;
|
std::shared_ptr<Server> game_server;
|
||||||
|
|
||||||
|
ServerState();
|
||||||
ServerState(std::shared_ptr<struct event_base> base, const std::string& config_filename, bool is_replay);
|
ServerState(std::shared_ptr<struct event_base> base, const std::string& config_filename, bool is_replay);
|
||||||
ServerState(const ServerState&) = delete;
|
ServerState(const ServerState&) = delete;
|
||||||
ServerState(ServerState&&) = delete;
|
ServerState(ServerState&&) = delete;
|
||||||
ServerState& operator=(const ServerState&) = delete;
|
ServerState& operator=(const ServerState&) = delete;
|
||||||
ServerState& operator=(ServerState&&) = delete;
|
ServerState& operator=(ServerState&&) = delete;
|
||||||
void init();
|
void load_objects(const std::string& what);
|
||||||
|
void load_objects(const std::vector<std::string>& what);
|
||||||
|
|
||||||
void add_client_to_available_lobby(std::shared_ptr<Client> c);
|
void add_client_to_available_lobby(std::shared_ptr<Client> c);
|
||||||
void remove_client_from_lobby(std::shared_ptr<Client> c);
|
void remove_client_from_lobby(std::shared_ptr<Client> c);
|
||||||
@@ -278,21 +283,24 @@ struct ServerState : public std::enable_shared_from_this<ServerState> {
|
|||||||
const std::string& gsl_filename = "",
|
const std::string& gsl_filename = "",
|
||||||
const std::string& bb_directory_filename = "") const;
|
const std::string& bb_directory_filename = "") const;
|
||||||
|
|
||||||
JSON load_config() const;
|
void create_load_step_graph();
|
||||||
|
void create_default_lobbies();
|
||||||
void collect_network_addresses();
|
void collect_network_addresses();
|
||||||
void parse_config(const JSON& config_json, bool is_reload);
|
void load_config();
|
||||||
void load_bb_private_keys();
|
void load_bb_private_keys();
|
||||||
void load_licenses();
|
void load_licenses();
|
||||||
void load_teams();
|
void load_teams();
|
||||||
void load_patch_indexes();
|
void load_patch_indexes();
|
||||||
void load_battle_params();
|
void load_battle_params();
|
||||||
void load_level_table();
|
void load_level_table();
|
||||||
void load_item_name_index();
|
void load_text_index();
|
||||||
void load_item_tables();
|
static std::shared_ptr<ItemNameIndex> create_item_name_index_for_version(
|
||||||
static std::shared_ptr<WordSelectTable> load_word_select_table_from_system();
|
Version version, std::shared_ptr<const ItemParameterTable> pmt, std::shared_ptr<const TextIndex> text_index);
|
||||||
|
void load_item_name_indexes();
|
||||||
|
void load_drop_tables();
|
||||||
|
void load_item_definitions();
|
||||||
void load_word_select_table();
|
void load_word_select_table();
|
||||||
void load_ep3_data();
|
void load_ep3_data();
|
||||||
void resolve_ep3_card_names();
|
|
||||||
void load_quest_index();
|
void load_quest_index();
|
||||||
void compile_functions();
|
void compile_functions();
|
||||||
void load_dol_files();
|
void load_dol_files();
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
#include "StepGraph.hh"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
void StepGraph::add_step(const string& name, const vector<string>& depends_on_names, function<void()>&& execute) {
|
||||||
|
auto new_step = make_shared<Step>(Step{.execute = std::move(execute)});
|
||||||
|
this->steps.emplace(name, new_step);
|
||||||
|
|
||||||
|
for (const auto& depends_on_name : depends_on_names) {
|
||||||
|
auto upstream_step = this->steps.at(depends_on_name);
|
||||||
|
upstream_step->downstream_dependencies.emplace_back(new_step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StepGraph::run(const string& start_step_name) {
|
||||||
|
vector<string> start_step_names({start_step_name});
|
||||||
|
this->run(start_step_names);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StepGraph::run(const vector<string>& start_step_names) {
|
||||||
|
// Collect all steps to run
|
||||||
|
deque<shared_ptr<Step>> steps_to_visit;
|
||||||
|
try {
|
||||||
|
for (const auto& start_step_name : start_step_names) {
|
||||||
|
steps_to_visit.emplace_back(this->steps.at(start_step_name));
|
||||||
|
}
|
||||||
|
} catch (const out_of_range&) {
|
||||||
|
throw runtime_error("invalid step name");
|
||||||
|
}
|
||||||
|
unordered_set<shared_ptr<Step>> steps_to_run;
|
||||||
|
while (!steps_to_visit.empty()) {
|
||||||
|
auto step = std::move(steps_to_visit.front());
|
||||||
|
steps_to_visit.pop_front();
|
||||||
|
if (steps_to_run.emplace(step).second) {
|
||||||
|
for (const auto& downstream_step : step->downstream_dependencies) {
|
||||||
|
steps_to_visit.emplace_back(downstream_step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Topological sort: repeatedly take all steps that are not a downstream
|
||||||
|
// dependency of any other step in the set
|
||||||
|
vector<shared_ptr<Step>> steps_order;
|
||||||
|
steps_order.reserve(steps_to_run.size());
|
||||||
|
while (!steps_to_run.empty()) {
|
||||||
|
unordered_set<shared_ptr<Step>> candidate_steps = steps_to_run;
|
||||||
|
for (const auto& step : steps_to_run) {
|
||||||
|
for (const auto& downstream_step : step->downstream_dependencies) {
|
||||||
|
candidate_steps.erase(downstream_step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (candidate_steps.empty()) {
|
||||||
|
throw logic_error("dependency graph contains a cycle");
|
||||||
|
}
|
||||||
|
for (const auto& step : candidate_steps) {
|
||||||
|
steps_to_run.erase(step);
|
||||||
|
steps_order.emplace_back(step);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the steps in order
|
||||||
|
uint64_t run_id = ++this->last_run_id;
|
||||||
|
for (auto step : steps_order) {
|
||||||
|
if (step->last_run_id < run_id) {
|
||||||
|
step->last_run_id = run_id;
|
||||||
|
if (step->execute) {
|
||||||
|
step->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <phosg/Strings.hh>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
struct StepGraph {
|
||||||
|
struct Step {
|
||||||
|
std::vector<std::shared_ptr<Step>> downstream_dependencies;
|
||||||
|
std::function<void()> execute;
|
||||||
|
uint64_t last_run_id = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<Step>> steps;
|
||||||
|
uint64_t last_run_id = 0;
|
||||||
|
|
||||||
|
StepGraph() = default;
|
||||||
|
|
||||||
|
void add_step(const std::string& name, const std::vector<std::string>& depends_on_names, std::function<void()>&& execute);
|
||||||
|
void run(const std::string& start_step);
|
||||||
|
void run(const std::vector<std::string>& start_steps);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user