From 466eb49c556ddd6e6fac995afa3ab373373db013 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Mon, 28 Aug 2023 21:06:03 -0700 Subject: [PATCH] use new phosg JSON interface --- notes/ar-codes.txt | 19 +++ src/Episode3/DataIndexes.cc | 265 +++++++++++++++++------------------- src/Episode3/DataIndexes.hh | 28 ++-- src/Episode3/Tournament.cc | 95 ++++++------- src/Episode3/Tournament.hh | 6 +- src/Loggers.cc | 20 +-- src/Loggers.hh | 2 +- src/Main.cc | 60 ++++---- src/PatchFileIndex.cc | 44 +++--- src/Quest.cc | 17 ++- src/Quest.hh | 4 +- src/RareItemSet.cc | 32 ++--- src/RareItemSet.hh | 2 +- src/ServerState.cc | 198 +++++++++------------------ src/ServerState.hh | 6 +- system/config.example.json | 65 +++++---- tests/config.json | 28 ++-- 17 files changed, 404 insertions(+), 487 deletions(-) diff --git a/notes/ar-codes.txt b/notes/ar-codes.txt index d0c36c3b..c2c46f40 100644 --- a/notes/ar-codes.txt +++ b/notes/ar-codes.txt @@ -155,3 +155,22 @@ TODO: Figure out more debug message conditionals (vars/functions) and add them h (Episode 3 USA) Able to find VIP cards offline (but they're still rare) 042C0B20 4800000C + +(Ep3 USA) Hold L when starting battle to enter debug menu +042C5460 4BD3AF78 +040003D8 3C60804A +040003DC 60630518 +040003E0 3C800002 +040003E4 480C9F35 +040003E8 2C030000 +040003EC 4082000C +040003F0 8801001A +040003F4 48000008 +040003F8 3800001A +040003FC 482C5068 + +(Ep3 USA) Dressing room always accessible +041A16FC 38600001 + +(Ep3 USA) Replace Options menu with debug menu +04149E70 38600019 diff --git a/src/Episode3/DataIndexes.cc b/src/Episode3/DataIndexes.cc index f038e256..1126311b 100644 --- a/src/Episode3/DataIndexes.cc +++ b/src/Episode3/DataIndexes.cc @@ -1083,151 +1083,45 @@ void PlayerConfig::encrypt(uint8_t basis) { this->basis = basis; } -HPType hp_type_for_name(const char* name) { - if (!strcmp(name, "DEFEAT_PLAYER")) { - return HPType::DEFEAT_PLAYER; - } else if (!strcmp(name, "DEFEAT_TEAM")) { - return HPType::DEFEAT_TEAM; - } else if (!strcmp(name, "COMMON_HP")) { - return HPType::COMMON_HP; - } else { - throw out_of_range("invalid HP type name"); - } -} -const char* name_for_hp_type(HPType hp_type) { - switch (hp_type) { - case HPType::DEFEAT_PLAYER: - return "DEFEAT_PLAYER"; - case HPType::DEFEAT_TEAM: - return "DEFEAT_TEAM"; - case HPType::COMMON_HP: - return "COMMON_HP"; - default: - throw out_of_range("invalid HP type"); - } -} - -DiceExchangeMode dice_exchange_mode_for_name(const char* name) { - if (!strcmp(name, "HIGH_ATK")) { - return DiceExchangeMode::HIGH_ATK; - } else if (!strcmp(name, "HIGH_DEF")) { - return DiceExchangeMode::HIGH_DEF; - } else if (!strcmp(name, "NONE")) { - return DiceExchangeMode::NONE; - } else { - throw out_of_range("invalid dice exchange mode name"); - } -} -const char* name_for_dice_exchange_mode(DiceExchangeMode dice_exchange_mode) { - switch (dice_exchange_mode) { - case DiceExchangeMode::HIGH_ATK: - return "HIGH_ATK"; - case DiceExchangeMode::HIGH_DEF: - return "HIGH_DEF"; - case DiceExchangeMode::NONE: - return "NONE"; - default: - throw out_of_range("invalid dice exchange mode"); - } -} - -AllowedCards allowed_cards_for_name(const char* name) { - if (!strcmp(name, "ALL")) { - return AllowedCards::ALL; - } else if (!strcmp(name, "N_ONLY")) { - return AllowedCards::N_ONLY; - } else if (!strcmp(name, "N_R_ONLY")) { - return AllowedCards::N_R_ONLY; - } else if (!strcmp(name, "N_R_S_ONLY")) { - return AllowedCards::N_R_S_ONLY; - } else { - throw out_of_range("invalid allowed cards name"); - } -} -const char* name_for_allowed_cards(AllowedCards allowed_cards) { - switch (allowed_cards) { - case AllowedCards::ALL: - return "ALL"; - case AllowedCards::N_ONLY: - return "N_ONLY"; - case AllowedCards::N_R_ONLY: - return "N_R_ONLY"; - case AllowedCards::N_R_S_ONLY: - return "N_R_S_ONLY"; - default: - throw out_of_range("invalid allowed cards"); - } -} - Rules::Rules() { this->clear(); } -template -void get_json_optional_int(const JSONObject::dict_type& dict, IntT& result, const char* key) { - try { - auto value_json = dict.at(key); - if (value_json->is_int()) { - result = value_json->as_int(); - } else if (value_json->is_bool()) { - result = value_json->as_bool() ? 1 : 0; - } else { - throw runtime_error("int-valued field is not int or bool"); - } - } catch (const out_of_range&) { - } -}; - -template -void get_json_optional_enum(const JSONObject::dict_type& dict, EnumT& result, const char* key, EnumT (*parse_str)(const char*)) { - try { - auto value_json = dict.at(key); - if (value_json->is_string()) { - result = parse_str(value_json->as_string().c_str()); - } else if (value_json->is_int()) { - result = static_cast(value_json->as_int()); - } else { - throw runtime_error("enum-valued field is not int or string"); - } - } catch (const out_of_range&) { - } -}; - -Rules::Rules(shared_ptr json) { +Rules::Rules(const JSON& json) { this->clear(); - auto dict = json->as_dict(); - get_json_optional_int(dict, this->overall_time_limit, "overall_time_limit"); - get_json_optional_int(dict, this->phase_time_limit, "phase_time_limit"); - get_json_optional_enum(dict, this->allowed_cards, "allowed_cards", allowed_cards_for_name); - get_json_optional_int(dict, this->min_dice, "min_dice"); - get_json_optional_int(dict, this->max_dice, "max_dice"); - get_json_optional_int(dict, this->disable_deck_shuffle, "disable_deck_shuffle"); - get_json_optional_int(dict, this->disable_deck_loop, "disable_deck_loop"); - get_json_optional_int(dict, this->char_hp, "char_hp"); - get_json_optional_enum(dict, this->hp_type, "hp_type", hp_type_for_name); - get_json_optional_int(dict, this->no_assist_cards, "no_assist_cards"); - get_json_optional_int(dict, this->disable_dialogue, "disable_dialogue"); - get_json_optional_enum(dict, this->dice_exchange_mode, "dice_exchange_mode", dice_exchange_mode_for_name); - get_json_optional_int(dict, this->disable_dice_boost, "disable_dice_boost"); + auto dict = json.as_dict(); + this->overall_time_limit = json.get("overall_time_limit", this->overall_time_limit); + this->phase_time_limit = json.get("phase_time_limit", this->phase_time_limit); + this->allowed_cards = json.get_enum("allowed_cards", this->allowed_cards); + this->min_dice = json.get("min_dice", this->min_dice); + this->max_dice = json.get("max_dice", this->max_dice); + this->disable_deck_shuffle = json.get("disable_deck_shuffle", this->disable_deck_shuffle); + this->disable_deck_loop = json.get("disable_deck_loop", this->disable_deck_loop); + this->char_hp = json.get("char_hp", this->char_hp); + this->hp_type = json.get_enum("hp_type", this->hp_type); + this->no_assist_cards = json.get("no_assist_cards", this->no_assist_cards); + this->disable_dialogue = json.get("disable_dialogue", this->disable_dialogue); + this->dice_exchange_mode = json.get_enum("dice_exchange_mode", this->dice_exchange_mode); + this->disable_dice_boost = json.get("disable_dice_boost", this->disable_dice_boost); } -shared_ptr Rules::json() const { - unordered_map> dict; - dict.emplace("overall_time_limit", make_json_int(this->overall_time_limit)); - dict.emplace("phase_time_limit", make_json_int(this->phase_time_limit)); - dict.emplace("allowed_cards", make_json_str(name_for_allowed_cards(this->allowed_cards))); - dict.emplace("min_dice", make_json_int(this->min_dice)); - dict.emplace("max_dice", make_json_int(this->max_dice)); - dict.emplace("disable_deck_shuffle", make_json_bool(this->disable_deck_shuffle)); - dict.emplace("disable_deck_loop", make_json_bool(this->disable_deck_loop)); - dict.emplace("char_hp", make_json_int(this->char_hp)); - dict.emplace("hp_type", make_json_str(name_for_hp_type(this->hp_type))); - dict.emplace("no_assist_cards", make_json_bool(this->no_assist_cards)); - dict.emplace("disable_dialogue", make_json_bool(this->disable_dialogue)); - dict.emplace("dice_exchange_mode", make_json_str(name_for_dice_exchange_mode(this->dice_exchange_mode))); - dict.emplace("disable_dice_boost", make_json_bool(this->disable_dice_boost)); - return shared_ptr(new JSONObject(std::move(dict))); +JSON Rules::json() const { + unordered_map dict; + dict.emplace("overall_time_limit", this->overall_time_limit); + dict.emplace("phase_time_limit", this->phase_time_limit); + dict.emplace("allowed_cards", name_for_enum(this->allowed_cards)); + dict.emplace("min_dice", this->min_dice); + dict.emplace("max_dice", this->max_dice); + dict.emplace("disable_deck_shuffle", this->disable_deck_shuffle); + dict.emplace("disable_deck_loop", this->disable_deck_loop); + dict.emplace("char_hp", this->char_hp); + dict.emplace("hp_type", name_for_enum(this->hp_type)); + dict.emplace("no_assist_cards", this->no_assist_cards); + dict.emplace("disable_dialogue", this->disable_dialogue); + dict.emplace("dice_exchange_mode", name_for_enum(this->dice_exchange_mode)); + dict.emplace("disable_dice_boost", this->disable_dice_boost); + return JSON(std::move(dict)); } void Rules::set_defaults() { @@ -2106,15 +2000,15 @@ set MapIndex::all_numbers() const { COMDeckIndex::COMDeckIndex(const string& filename) { try { - auto json = JSONObject::parse(load_file(filename)); - for (const auto& def_json : json->as_list()) { + auto json = JSON::parse(load_file(filename)); + for (const auto& def_json : json.as_list()) { auto& def = this->decks.emplace_back(new COMDeckDefinition()); def->index = this->decks.size() - 1; - def->player_name = def_json->at(0)->as_string(); - def->deck_name = def_json->at(1)->as_string(); - auto card_ids_json = def_json->at(2)->as_list(); + def->player_name = def_json.at(0).as_string(); + def->deck_name = def_json.at(1).as_string(); + auto card_ids_json = def_json.at(2); for (size_t z = 0; z < 0x1F; z++) { - def->card_ids[z] = card_ids_json.at(z)->as_int(); + def->card_ids[z] = card_ids_json.at(z).as_int(); } if (!this->decks_by_name.emplace(def->deck_name, def).second) { throw runtime_error("duplicate COM deck name: " + def->deck_name); @@ -2142,3 +2036,88 @@ shared_ptr COMDeckIndex::random_deck() const { } } // namespace Episode3 + +template <> +Episode3::HPType enum_for_name(const char* name) { + if (!strcmp(name, "DEFEAT_PLAYER")) { + return Episode3::HPType::DEFEAT_PLAYER; + } else if (!strcmp(name, "DEFEAT_TEAM")) { + return Episode3::HPType::DEFEAT_TEAM; + } else if (!strcmp(name, "COMMON_HP")) { + return Episode3::HPType::COMMON_HP; + } else { + throw out_of_range("invalid HP type name"); + } +} + +template <> +const char* name_for_enum(Episode3::HPType hp_type) { + switch (hp_type) { + case Episode3::HPType::DEFEAT_PLAYER: + return "DEFEAT_PLAYER"; + case Episode3::HPType::DEFEAT_TEAM: + return "DEFEAT_TEAM"; + case Episode3::HPType::COMMON_HP: + return "COMMON_HP"; + default: + throw out_of_range("invalid HP type"); + } +} + +template <> +Episode3::DiceExchangeMode enum_for_name(const char* name) { + if (!strcmp(name, "HIGH_ATK")) { + return Episode3::DiceExchangeMode::HIGH_ATK; + } else if (!strcmp(name, "HIGH_DEF")) { + return Episode3::DiceExchangeMode::HIGH_DEF; + } else if (!strcmp(name, "NONE")) { + return Episode3::DiceExchangeMode::NONE; + } else { + throw out_of_range("invalid dice exchange mode name"); + } +} + +template <> +const char* name_for_enum(Episode3::DiceExchangeMode dice_exchange_mode) { + switch (dice_exchange_mode) { + case Episode3::DiceExchangeMode::HIGH_ATK: + return "HIGH_ATK"; + case Episode3::DiceExchangeMode::HIGH_DEF: + return "HIGH_DEF"; + case Episode3::DiceExchangeMode::NONE: + return "NONE"; + default: + throw out_of_range("invalid dice exchange mode"); + } +} + +template <> +Episode3::AllowedCards enum_for_name(const char* name) { + if (!strcmp(name, "ALL")) { + return Episode3::AllowedCards::ALL; + } else if (!strcmp(name, "N_ONLY")) { + return Episode3::AllowedCards::N_ONLY; + } else if (!strcmp(name, "N_R_ONLY")) { + return Episode3::AllowedCards::N_R_ONLY; + } else if (!strcmp(name, "N_R_S_ONLY")) { + return Episode3::AllowedCards::N_R_S_ONLY; + } else { + throw out_of_range("invalid allowed cards name"); + } +} + +template <> +const char* name_for_enum(Episode3::AllowedCards allowed_cards) { + switch (allowed_cards) { + case Episode3::AllowedCards::ALL: + return "ALL"; + case Episode3::AllowedCards::N_ONLY: + return "N_ONLY"; + case Episode3::AllowedCards::N_R_ONLY: + return "N_R_ONLY"; + case Episode3::AllowedCards::N_R_S_ONLY: + return "N_R_S_ONLY"; + default: + throw out_of_range("invalid allowed cards"); + } +} diff --git a/src/Episode3/DataIndexes.hh b/src/Episode3/DataIndexes.hh index 16376a46..4df4e126 100644 --- a/src/Episode3/DataIndexes.hh +++ b/src/Episode3/DataIndexes.hh @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -659,18 +660,12 @@ enum class HPType : uint8_t { COMMON_HP = 2, }; -HPType hp_type_for_name(const char* name); -const char* name_for_hp_type(HPType hp_type); - enum class DiceExchangeMode : uint8_t { HIGH_ATK = 0, HIGH_DEF = 1, NONE = 2, }; -DiceExchangeMode dice_exchange_mode_for_name(const char* name); -const char* name_for_dice_exchange_mode(DiceExchangeMode dice_exchange_mode); - enum class AllowedCards : uint8_t { ALL = 0, N_ONLY = 1, @@ -678,9 +673,6 @@ enum class AllowedCards : uint8_t { N_R_S_ONLY = 3, }; -AllowedCards allowed_cards_for_name(const char* name); -const char* name_for_allowed_cards(AllowedCards allowed_cards); - struct Rules { // When this structure is used in a map/quest definition, FF in any of these // fields means the user is allowed to override it. Any non-FF fields are @@ -708,8 +700,8 @@ struct Rules { // likely be more work than it's worth. Rules(); - explicit Rules(std::shared_ptr json); - std::shared_ptr json() const; + explicit Rules(const JSON& json); + JSON json() const; bool operator==(const Rules& other) const; bool operator!=(const Rules& other) const; void clear(); @@ -1115,3 +1107,17 @@ private: }; } // namespace Episode3 + +// TODO: Figure out how to declare these inside the Episode3 namespace. +template <> +Episode3::HPType enum_for_name(const char* name); +template <> +const char* name_for_enum(Episode3::HPType hp_type); +template <> +Episode3::DiceExchangeMode enum_for_name(const char* name); +template <> +const char* name_for_enum(Episode3::DiceExchangeMode dice_exchange_mode); +template <> +Episode3::AllowedCards enum_for_name(const char* name); +template <> +const char* name_for_enum(Episode3::AllowedCards allowed_cards); diff --git a/src/Episode3/Tournament.cc b/src/Episode3/Tournament.cc index 8e5a4075..a4119c23 100644 --- a/src/Episode3/Tournament.cc +++ b/src/Episode3/Tournament.cc @@ -313,7 +313,7 @@ Tournament::Tournament( shared_ptr map_index, shared_ptr com_deck_index, uint8_t number, - std::shared_ptr json) + const JSON& json) : log(string_printf("[Tournament/%02hhX] ", number)), map_index(map_index), com_deck_index(com_deck_index), @@ -326,35 +326,32 @@ void Tournament::init() { bool is_registration_complete; if (this->source_json) { - auto& dict = this->source_json->as_dict(); - this->name = dict.at("name")->as_string(); - this->map = this->map_index->definition_for_number(dict.at("map_number")->as_int()); - this->rules = Rules(dict.at("rules")); - this->is_2v2 = dict.at("is_2v2")->as_bool(); - is_registration_complete = dict.at("is_registration_complete")->as_bool(); + this->name = this->source_json.at("name").as_string(); + this->map = this->map_index->definition_for_number(this->source_json.at("map_number")); + this->rules = Rules(this->source_json.at("rules")); + this->is_2v2 = this->source_json.at("is_2v2"); + is_registration_complete = this->source_json.at("is_registration_complete"); - for (const auto& team_json : dict.at("teams")->as_list()) { - auto& team_dict = team_json->as_dict(); + for (const auto& team_json : this->source_json.at("teams").as_list()) { auto& team = this->teams.emplace_back(new Team( this->shared_from_this(), this->teams.size(), - team_dict.at("max_players")->as_int())); - team->name = team_dict.at("name")->as_string(); - team->password = team_dict.at("password")->as_string(); - team_index_to_rounds_cleared.emplace_back(team_dict.at("num_rounds_cleared")->as_int()); - for (const auto& player_json : team_dict.at("player_specs")->as_list()) { - if (player_json->is_int()) { - uint32_t serial_number = player_json->as_int(); - team->players.emplace_back(serial_number); - this->all_player_serial_numbers.emplace(serial_number); + team_json.at("max_players"))); + team->name = team_json.at("name").as_string(); + team->password = team_json.at("password").as_string(); + team_index_to_rounds_cleared.emplace_back(team_json.at("num_rounds_cleared")); + for (const auto& player_json : team_json.at("player_specs").as_list()) { + if (player_json.is_int()) { + team->players.emplace_back(player_json); + this->all_player_serial_numbers.emplace(player_json); } else { - team->players.emplace_back(this->com_deck_index->deck_for_name(player_json->as_string())); + team->players.emplace_back(this->com_deck_index->deck_for_name(player_json)); } } } this->num_teams = this->teams.size(); - this->source_json.reset(); + this->source_json = nullptr; } else { // Create empty teams @@ -459,34 +456,33 @@ void Tournament::init() { } } -std::shared_ptr Tournament::json() const { - unordered_map> dict; - dict.emplace("name", make_json_str(this->name)); - dict.emplace("map_number", make_json_int(this->map->map.map_number)); - dict.emplace("rules", this->rules.json()); - dict.emplace("is_2v2", make_json_bool(this->is_2v2)); - dict.emplace("is_registration_complete", make_json_bool(this->current_state != State::REGISTRATION)); - - vector> teams_list; +JSON Tournament::json() const { + auto teams_list = JSON::list(); for (auto team : this->teams) { - unordered_map> team_dict; - team_dict.emplace("max_players", make_json_int(team->max_players)); - vector> player_jsons_list; + auto players_list = JSON::list(); for (const auto& player : team->players) { if (player.is_human()) { - player_jsons_list.emplace_back(make_json_int(player.serial_number)); + players_list.emplace_back(player.serial_number); } else { - player_jsons_list.emplace_back(make_json_str(player.com_deck->deck_name)); + players_list.emplace_back(player.com_deck->deck_name); } } - team_dict.emplace("player_specs", make_json_list(std::move(player_jsons_list))); - team_dict.emplace("name", make_json_str(team->name)); - team_dict.emplace("password", make_json_str(team->password)); - team_dict.emplace("num_rounds_cleared", make_json_int(team->num_rounds_cleared)); - teams_list.emplace_back(new JSONObject(std::move(team_dict))); + teams_list.emplace_back(JSON::dict({ + {"max_players", team->max_players}, + {"player_specs", std::move(players_list)}, + {"name", team->name}, + {"password", team->password}, + {"num_rounds_cleared", team->num_rounds_cleared}, + })); } - dict.emplace("teams", make_json_list(std::move(teams_list))); - return shared_ptr(new JSONObject(std::move(dict))); + return JSON::dict({ + {"name", this->name}, + {"map_number", this->map->map.map_number.load()}, + {"rules", this->rules.json()}, + {"is_2v2", this->is_2v2}, + {"is_registration_complete", (this->current_state != State::REGISTRATION)}, + {"teams", std::move(teams_list)}, + }); } uint8_t Tournament::get_number() const { @@ -664,15 +660,13 @@ TournamentIndex::TournamentIndex( return; } - auto json = JSONObject::parse(load_file(this->state_filename)); - - auto& list = json->as_list(); - if (list.size() != 0x20) { + auto json = JSON::parse(load_file(this->state_filename)); + if (json.size() != 0x20) { throw runtime_error("tournament JSON list length is incorrect"); } for (size_t z = 0; z < 0x20; z++) { - if (!list.at(z)->is_null()) { - this->tournaments[z].reset(new Tournament(this->map_index, this->com_deck_index, z, list[z])); + if (!json.at(z).is_null()) { + this->tournaments[z].reset(new Tournament(this->map_index, this->com_deck_index, z, json.at(z))); this->tournaments[z]->init(); } } @@ -683,16 +677,15 @@ void TournamentIndex::save() const { return; } - vector> list; + auto list = JSON::list(); for (size_t z = 0; z < 0x20; z++) { if (this->tournaments[z]) { list.emplace_back(this->tournaments[z]->json()); } else { - list.emplace_back(make_json_null()); + list.emplace_back(nullptr); } } - auto json = make_json_list(std::move(list)); - save_file(this->state_filename, json->serialize(JSONObject::SerializeOption::FORMAT)); + save_file(this->state_filename, list.serialize(JSON::SerializeOption::FORMAT)); } vector> TournamentIndex::all_tournaments() const { diff --git a/src/Episode3/Tournament.hh b/src/Episode3/Tournament.hh index 711ee3b6..be19b79a 100644 --- a/src/Episode3/Tournament.hh +++ b/src/Episode3/Tournament.hh @@ -104,11 +104,11 @@ public: std::shared_ptr map_index, std::shared_ptr com_deck_index, uint8_t number, - std::shared_ptr json); + const JSON& json); ~Tournament() = default; void init(); - std::shared_ptr json() const; + JSON json() const; uint8_t get_number() const; const std::string& get_name() const; @@ -134,7 +134,7 @@ private: std::shared_ptr map_index; std::shared_ptr com_deck_index; - std::shared_ptr source_json; + JSON source_json; uint8_t number; std::string name; std::shared_ptr map; diff --git a/src/Loggers.cc b/src/Loggers.cc index fbf050a2..292ecf40 100644 --- a/src/Loggers.cc +++ b/src/Loggers.cc @@ -21,26 +21,16 @@ PrefixedLogger replay_log("[ReplaySession] ", LogLevel::USE_DEFAULT); PrefixedLogger server_log("[Server] ", LogLevel::USE_DEFAULT); PrefixedLogger static_game_data_log("[StaticGameData] ", LogLevel::USE_DEFAULT); -static LogLevel log_level_for_name(const string& name) { - static const unordered_map levels({ - {"debug", LogLevel::DEBUG}, - {"info", LogLevel::INFO}, - {"warning", LogLevel::WARNING}, - {"error", LogLevel::ERROR}, - {"disabled", LogLevel::DISABLED}, - }); - return levels.at(tolower(name)); -} - static void set_log_level_from_json( - PrefixedLogger& log, shared_ptr d, const char* json_key) { + PrefixedLogger& log, const JSON& d, const char* json_key) { try { - log.min_level = log_level_for_name(d->at(json_key)->as_string()); - } catch (const JSONObject::key_error&) { + string name = toupper(d.at(json_key).as_string()); + log.min_level = enum_for_name(name.c_str()); + } catch (const out_of_range&) { } } -void set_log_levels_from_json(shared_ptr json) { +void set_log_levels_from_json(const JSON& json) { set_log_level_from_json(ax_messages_log, json, "AXMessages"); set_log_level_from_json(channel_exceptions_log, json, "ChannelExceptions"); set_log_level_from_json(client_log, json, "Clients"); diff --git a/src/Loggers.hh b/src/Loggers.hh index 6a86b95c..e6553490 100644 --- a/src/Loggers.hh +++ b/src/Loggers.hh @@ -20,4 +20,4 @@ extern PrefixedLogger replay_log; extern PrefixedLogger server_log; extern PrefixedLogger static_game_data_log; -void set_log_levels_from_json(std::shared_ptr json); +void set_log_levels_from_json(const JSON& json); diff --git a/src/Main.cc b/src/Main.cc index 479c0279..2730ef9c 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -40,10 +40,10 @@ using namespace std; bool use_terminal_colors = false; template -vector parse_int_vector(shared_ptr o) { +vector parse_int_vector(const JSON& o) { vector ret; - for (const auto& x : o->as_list()) { - ret.emplace_back(x->as_int()); + for (const auto& x : o.as_list()) { + ret.emplace_back(x.as_int()); } return ret; } @@ -1303,7 +1303,7 @@ int main(int argc, char** argv) { shared_ptr data(new string(read_input_data())); shared_ptr rs; if (json) { - rs.reset(new JSONRareItemSet(JSONObject::parse(read_input_data()))); + rs.reset(new JSONRareItemSet(JSON::parse(read_input_data()))); } else { rs.reset(new RELRareItemSet(data)); } @@ -1400,7 +1400,7 @@ int main(int argc, char** argv) { return ret; }; - JSONObject::dict_type episodes_dict; + JSON::dict_type episodes_dict; static const array>>, 3> episodes = { make_pair(Episode::EP1, generate_table(Episode::EP1)), make_pair(Episode::EP2, generate_table(Episode::EP2)), @@ -1409,11 +1409,11 @@ int main(int argc, char** argv) { for (const auto& episode_it : episodes) { Episode episode = episode_it.first; const auto& rt_index_to_enemy_type = episode_it.second; - JSONObject::dict_type difficulty_dict; + JSON::dict_type difficulty_dict; for (uint8_t difficulty = 0; difficulty < 4; difficulty++) { - JSONObject::dict_type section_id_dict; + JSON::dict_type section_id_dict; for (uint8_t section_id = 0; section_id < 10; section_id++) { - JSONObject::dict_type collection_dict; + JSON::dict_type collection_dict; for (size_t rt_index = 0; rt_index < rt_index_to_enemy_type.size(); rt_index++) { const auto& enemy_types = rt_index_to_enemy_type[rt_index]; @@ -1427,15 +1427,9 @@ int main(int argc, char** argv) { continue; } - JSONObject::list_type spec_list; - auto frac = reduce_fraction(spec.probability, 0x100000000); - spec_list.emplace_back(make_json_str(string_printf("%" PRIu64 "/%" PRIu64, frac.first, frac.second))); - spec_list.emplace_back(make_json_int(primary_identifier)); - - JSONObject::list_type specs_list; - specs_list.emplace_back(make_json_list(std::move(spec_list))); - auto specs_json = make_json_list(std::move(specs_list)); + JSON::list_type specs_list; + JSON specs_json = {JSON::list({string_printf("%" PRIu64 "/%" PRIu64, frac.first, frac.second), primary_identifier})}; for (const auto& enemy_type : enemy_types) { if (enemy_type_valid_for_episode(episode, enemy_type)) { collection_dict.emplace(name_for_enum(enemy_type), specs_json); @@ -1445,48 +1439,42 @@ int main(int argc, char** argv) { } for (size_t area = 0; area < 0x12; area++) { - JSONObject::list_type area_list; + JSON::list_type area_list; for (const auto& spec : rs.get_box_specs(GameMode::NORMAL, episode, difficulty, section_id, area)) { uint32_t primary_identifier = (spec.item_code[0] << 16) | (spec.item_code[1] << 8) | spec.item_code[2]; if (primary_identifier == 0) { continue; } - - JSONObject::list_type spec_list; - auto frac = reduce_fraction(spec.probability, 0x100000000); - spec_list.emplace_back(make_json_str(string_printf("%" PRIu64 "/%" PRIu64, frac.first, frac.second))); - spec_list.emplace_back(make_json_int(primary_identifier)); - - area_list.emplace_back(make_json_list(std::move(spec_list))); + area_list.emplace_back(JSON::list({string_printf("%" PRIu64 "/%" PRIu64, frac.first, frac.second), primary_identifier})); } if (!area_list.empty()) { collection_dict.emplace( string_printf("Box-%s", name_for_area(episode, area)), - make_json_list(std::move(area_list))); + std::move(area_list)); } } if (!collection_dict.empty()) { - section_id_dict.emplace(name_for_section_id(section_id), make_json_dict(std::move(collection_dict))); + section_id_dict.emplace(name_for_section_id(section_id), std::move(collection_dict)); } } - difficulty_dict.emplace(token_name_for_difficulty(difficulty), make_json_dict(std::move(section_id_dict))); + difficulty_dict.emplace(token_name_for_difficulty(difficulty), std::move(section_id_dict)); } - episodes_dict.emplace(token_name_for_episode(episode), make_json_dict(std::move(difficulty_dict))); + episodes_dict.emplace(token_name_for_episode(episode), std::move(difficulty_dict)); } - JSONObject::dict_type root_dict; - root_dict.emplace("Normal", make_json_dict(std::move(episodes_dict))); - auto root_json = make_json_dict(std::move(root_dict)); - string json_data = root_json->serialize( - JSONObject::SerializeOption::FORMAT | - JSONObject::SerializeOption::HEX_INTEGERS | - JSONObject::SerializeOption::SORT_DICT_KEYS); - write_output_data(json_data.data(), json_data.size()); + JSON::dict_type root_dict; + root_dict.emplace("Normal", std::move(episodes_dict)); + JSON root_json = std::move(root_dict); + string json_data = root_json.serialize( + JSON::SerializeOption::FORMAT | + JSON::SerializeOption::HEX_INTEGERS | + JSON::SerializeOption::SORT_DICT_KEYS); + write_output_data(json_data.data(), json_data.size()); break; } diff --git a/src/PatchFileIndex.cc b/src/PatchFileIndex.cc index 4649c7bf..1bfd9993 100644 --- a/src/PatchFileIndex.cc +++ b/src/PatchFileIndex.cc @@ -33,20 +33,20 @@ PatchFileIndex::PatchFileIndex(const string& root_dir) : root_dir(root_dir) { string metadata_cache_filename = root_dir + "/.metadata-cache.json"; - shared_ptr metadata_cache_json; + JSON metadata_cache_json; try { string metadata_text = load_file(metadata_cache_filename); - metadata_cache_json = JSONObject::parse(metadata_text); + metadata_cache_json = JSON::parse(metadata_text); patch_index_log.info("Loaded patch metadata cache from %s", metadata_cache_filename.c_str()); } catch (const exception& e) { - metadata_cache_json = make_json_dict({}); + metadata_cache_json = JSON::dict(); patch_index_log.warning("Cannot load patch metadata cache from %s: %s", metadata_cache_filename.c_str(), e.what()); } // Assuming it's rare for patch files to change, we skip writing the metadata // cache if no files were changed at all (which should usually be the case) bool should_write_metadata_cache = false; - shared_ptr new_metadata_cache_json = make_json_dict({}); + JSON new_metadata_cache_json = JSON::dict(); vector path_directories; function collect_dir = [&](const string& dir) -> void { @@ -75,12 +75,12 @@ PatchFileIndex::PatchFileIndex(const string& root_dir) f->name = item; string compute_crc32s_message; // If not empty, should compute crc32s - shared_ptr cache_item_json; + JSON cache_item_json; try { - cache_item_json = metadata_cache_json->at(relative_item_path); - auto cache_item = metadata_cache_json->at(relative_item_path)->as_list(); - uint64_t cached_size = cache_item.at(0)->as_int(); - uint64_t cached_mtime = cache_item.at(1)->as_int(); + cache_item_json = metadata_cache_json.at(relative_item_path); + auto cache_item = metadata_cache_json.at(relative_item_path).as_list(); + uint64_t cached_size = cache_item.at(0); + uint64_t cached_mtime = cache_item.at(1); if (static_cast(st.st_mtime) != cached_mtime) { throw runtime_error("file has been modified"); } @@ -88,9 +88,9 @@ PatchFileIndex::PatchFileIndex(const string& root_dir) throw runtime_error("file size has changed"); } f->size = cached_size; - f->crc32 = cache_item.at(2)->as_int(); - for (const auto& chunk_crc32_json : cache_item.at(3)->as_list()) { - f->chunk_crcs.emplace_back(chunk_crc32_json->as_int()); + f->crc32 = cache_item.at(2); + for (const auto& chunk_crc32_json : cache_item.at(3).as_list()) { + f->chunk_crcs.emplace_back(chunk_crc32_json.as_int()); } } catch (const exception& e) { @@ -106,25 +106,19 @@ PatchFileIndex::PatchFileIndex(const string& root_dir) } // File was modified or cache item was missing; make a new cache item - vector> chunk_crcs_item; + vector chunk_crcs_item; for (uint32_t chunk_crc : f->chunk_crcs) { - chunk_crcs_item.emplace_back(make_json_int(chunk_crc)); + chunk_crcs_item.emplace_back(chunk_crc); } - vector> new_cache_item({ - make_json_int(f->size), - make_json_int(st.st_mtime), - make_json_int(f->crc32), - make_json_list(std::move(chunk_crcs_item)), - }); - new_metadata_cache_json->as_dict().emplace( - relative_item_path, make_json_list(std::move(new_cache_item))); + new_metadata_cache_json.as_dict().emplace( + relative_item_path, JSON::list({f->size, st.st_mtime, f->crc32, std::move(chunk_crcs_item)})); should_write_metadata_cache = true; } else { // File was not modified and cache item was valid; just use the // existing cache item - new_metadata_cache_json->as_dict().emplace( - relative_item_path, cache_item_json); + new_metadata_cache_json.as_dict().emplace( + relative_item_path, std::move(cache_item_json)); } this->files_by_patch_order.emplace_back(f); @@ -148,7 +142,7 @@ PatchFileIndex::PatchFileIndex(const string& root_dir) if (should_write_metadata_cache) { try { - save_file(metadata_cache_filename, new_metadata_cache_json->serialize()); + save_file(metadata_cache_filename, new_metadata_cache_json.serialize()); patch_index_log.info("Saved patch metadata cache to %s", metadata_cache_filename.c_str()); } catch (const exception& e) { patch_index_log.warning("Cannot save patch metadata cache to %s: %s", metadata_cache_filename.c_str(), e.what()); diff --git a/src/Quest.cc b/src/Quest.cc index 70988b76..ab0fe36b 100644 --- a/src/Quest.cc +++ b/src/Quest.cc @@ -21,14 +21,13 @@ using namespace std; -QuestCategoryIndex::Category::Category(uint32_t category_id, std::shared_ptr json) +QuestCategoryIndex::Category::Category(uint32_t category_id, const JSON& json) : category_id(category_id) { - const auto& l = json->as_list(); - this->flags = l.at(0)->as_int(); - this->type = l.at(1)->as_string().at(0); - this->short_token = l.at(2)->as_string(); - this->name = decode_sjis(l.at(3)->as_string()); - this->description = decode_sjis(l.at(4)->as_string()); + this->flags = json.at(0); + this->type = json.at(1).as_string().at(0); + this->short_token = json.at(2).as_string(); + this->name = decode_sjis(json.at(3)); + this->description = decode_sjis(json.at(4)); } bool QuestCategoryIndex::Category::matches_flags(uint8_t request) const { @@ -40,9 +39,9 @@ bool QuestCategoryIndex::Category::matches_flags(uint8_t request) const { return request & this->flags; } -QuestCategoryIndex::QuestCategoryIndex(std::shared_ptr json) { +QuestCategoryIndex::QuestCategoryIndex(const JSON& json) { uint32_t next_category_id = 1; - for (const auto& it : json->as_list()) { + for (const auto& it : json.as_list()) { this->categories.emplace_back(next_category_id++, it); } } diff --git a/src/Quest.hh b/src/Quest.hh index f39bf0c8..d3b9ed9d 100644 --- a/src/Quest.hh +++ b/src/Quest.hh @@ -30,14 +30,14 @@ struct QuestCategoryIndex { std::u16string name; std::u16string description; - explicit Category(uint32_t category_id, std::shared_ptr json); + explicit Category(uint32_t category_id, const JSON& json); bool matches_flags(uint8_t request) const; }; std::vector categories; - explicit QuestCategoryIndex(std::shared_ptr json); + explicit QuestCategoryIndex(const JSON& json); const Category& find(char type, const std::string& short_token) const; const Category& at(uint32_t category_id) const; diff --git a/src/RareItemSet.cc b/src/RareItemSet.cc index 08e15f38..3f97f15e 100644 --- a/src/RareItemSet.cc +++ b/src/RareItemSet.cc @@ -170,27 +170,27 @@ const RELRareItemSet::Table& RELRareItemSet::get_table( return tables[(ep_index * 10 * 4) + (difficulty * 10) + secid]; } -JSONRareItemSet::JSONRareItemSet(std::shared_ptr json) { - for (const auto& mode_it : json->as_dict()) { +JSONRareItemSet::JSONRareItemSet(const JSON& json) { + for (const auto& mode_it : json.as_dict()) { static const unordered_map mode_keys( {{"Normal", GameMode::NORMAL}, {"Battle", GameMode::BATTLE}, {"Challenge", GameMode::CHALLENGE}, {"Solo", GameMode::SOLO}}); GameMode mode = mode_keys.at(mode_it.first); - for (const auto& episode_it : mode_it.second->as_dict()) { + for (const auto& episode_it : mode_it.second.as_dict()) { static const unordered_map episode_keys( {{"Episode1", Episode::EP1}, {"Episode2", Episode::EP2}, {"Episode4", Episode::EP4}}); Episode episode = episode_keys.at(episode_it.first); - for (const auto& difficulty_it : episode_it.second->as_dict()) { + for (const auto& difficulty_it : episode_it.second.as_dict()) { static const unordered_map difficulty_keys( {{"Normal", 0}, {"Hard", 1}, {"VeryHard", 2}, {"Ultimate", 3}}); uint8_t difficulty = difficulty_keys.at(difficulty_it.first); - for (const auto& section_id_it : difficulty_it.second->as_dict()) { + for (const auto& section_id_it : difficulty_it.second.as_dict()) { uint8_t section_id = section_id_for_name(section_id_it.first); auto& collection = this->collections[this->key_for_params(mode, episode, difficulty, section_id)]; - for (const auto& item_it : section_id_it.second->as_dict()) { + for (const auto& item_it : section_id_it.second.as_dict()) { vector* target; if (starts_with(item_it.first, "Box-")) { uint8_t area = area_for_name(item_it.first.substr(4)); @@ -206,15 +206,15 @@ JSONRareItemSet::JSONRareItemSet(std::shared_ptr json) { target = &collection.rt_index_to_specs[index]; } - for (const auto& spec_json : item_it.second->as_list()) { - auto& spec_list = spec_json->as_list(); + for (const auto& spec_json : item_it.second.as_list()) { + auto& spec_list = spec_json.as_list(); auto& d = target->emplace_back(); auto prob_desc = spec_list.at(0); - if (prob_desc->is_int()) { - d.probability = spec_list.at(0)->as_int(); - } else if (prob_desc->is_string()) { - auto tokens = split(prob_desc->as_string(), '/'); + if (prob_desc.is_int()) { + d.probability = prob_desc; + } else if (prob_desc.is_string()) { + auto tokens = split(prob_desc, '/'); if (tokens.size() != 2) { throw runtime_error("invalid probability specification"); } @@ -228,13 +228,13 @@ JSONRareItemSet::JSONRareItemSet(std::shared_ptr json) { } auto item_desc = spec_list.at(1); - if (item_desc->is_int()) { - uint32_t item_code = spec_list.at(1)->as_int(); + if (item_desc.is_int()) { + uint32_t item_code = item_desc; d.item_code[0] = (item_code >> 16) & 0xFF; d.item_code[1] = (item_code >> 8) & 0xFF; d.item_code[2] = item_code & 0xFF; - } else if (item_desc->is_string()) { - ItemData data(item_desc->as_string()); + } else if (item_desc.is_string()) { + ItemData data(item_desc.as_string()); d.item_code[0] = data.data1[0]; d.item_code[1] = data.data1[1]; d.item_code[2] = data.data1[2]; diff --git a/src/RareItemSet.hh b/src/RareItemSet.hh index e6e0df08..ed5bd171 100644 --- a/src/RareItemSet.hh +++ b/src/RareItemSet.hh @@ -92,7 +92,7 @@ private: class JSONRareItemSet : public RareItemSet { public: - JSONRareItemSet(std::shared_ptr json); + explicit JSONRareItemSet(const JSON& json); virtual ~JSONRareItemSet() = default; virtual std::vector get_enemy_specs(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t rt_index) const; diff --git a/src/ServerState.cc b/src/ServerState.cc index 09563b4a..c45cb077 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -384,9 +384,8 @@ void ServerState::set_port_configuration( } } -void ServerState::create_menus(shared_ptr config_json) { +void ServerState::create_menus(const JSON& json) { config_log.info("Creating menus"); - const auto& d = config_json->as_dict(); shared_ptr information_menu_v2(new Menu(MenuID::INFORMATION, u"Information")); shared_ptr information_menu_v3(new Menu(MenuID::INFORMATION, u"Information")); @@ -398,13 +397,12 @@ void ServerState::create_menus(shared_ptr config_json) { u"Return to the\nmain menu", MenuItem::Flag::INVISIBLE_IN_INFO_MENU); { uint32_t item_id = 0; - for (const auto& item : d.at("InformationMenuContents")->as_list()) { - auto& v = item->as_list(); - information_menu_v2->items.emplace_back(item_id, decode_sjis(v.at(0)->as_string()), - decode_sjis(v.at(1)->as_string()), 0); - information_menu_v3->items.emplace_back(item_id, decode_sjis(v.at(0)->as_string()), - decode_sjis(v.at(1)->as_string()), MenuItem::Flag::REQUIRES_MESSAGE_BOXES); - information_contents->emplace_back(decode_sjis(v.at(2)->as_string())); + for (const auto& item : json.at("InformationMenuContents").as_list()) { + u16string name = decode_sjis(item.at(0)); + u16string short_desc = decode_sjis(item.at(1)); + information_menu_v2->items.emplace_back(item_id, name, short_desc, 0); + information_menu_v3->items.emplace_back(item_id, name, short_desc, MenuItem::Flag::REQUIRES_MESSAGE_BOXES); + information_contents->emplace_back(decode_sjis(item.at(2))); item_id++; } } @@ -417,8 +415,8 @@ void ServerState::create_menus(shared_ptr config_json) { ret_pds.clear(); try { - map> sorted_jsons; - for (const auto& it : d.at(key)->as_dict()) { + map sorted_jsons; + for (const auto& it : json.at(key).as_dict()) { sorted_jsons.emplace(it.first, it.second); } @@ -429,10 +427,9 @@ void ServerState::create_menus(shared_ptr config_json) { uint32_t item_id = 0; for (const auto& item : sorted_jsons) { - const string& netloc_str = item.second->as_string(); + const string& netloc_str = item.second; const string& description = "$C7Remote server:\n$C6" + netloc_str; - ret->items.emplace_back(item_id, decode_sjis(item.first), - decode_sjis(description), 0); + ret->items.emplace_back(item_id, decode_sjis(item.first), decode_sjis(description), 0); ret_pds.emplace_back(parse_netloc(netloc_str)); item_id++; } @@ -451,7 +448,7 @@ void ServerState::create_menus(shared_ptr config_json) { this->proxy_destinations_xb, "ProxyDestinations-XB"); try { - const string& netloc_str = d.at("ProxyDestination-Patch")->as_string(); + const string& netloc_str = json.at("ProxyDestination-Patch"); this->proxy_destination_patch = parse_netloc(netloc_str); config_log.info("Patch server proxy is enabled with destination %s", netloc_str.c_str()); for (auto& it : this->name_to_port_config) { @@ -464,7 +461,7 @@ void ServerState::create_menus(shared_ptr config_json) { this->proxy_destination_patch.second = 0; } try { - const string& netloc_str = d.at("ProxyDestination-BB")->as_string(); + const string& netloc_str = json.at("ProxyDestination-BB"); this->proxy_destination_bb = parse_netloc(netloc_str); config_log.info("BB proxy is enabled with destination %s", netloc_str.c_str()); for (auto& it : this->name_to_port_config) { @@ -477,18 +474,9 @@ void ServerState::create_menus(shared_ptr config_json) { this->proxy_destination_bb.second = 0; } - try { - this->welcome_message = decode_sjis(d.at("WelcomeMessage")->as_string()); - } catch (const out_of_range&) { - } - try { - this->pc_patch_server_message = decode_sjis(d.at("PCPatchServerMessage")->as_string()); - } catch (const out_of_range&) { - } - try { - this->bb_patch_server_message = decode_sjis(d.at("BBPatchServerMessage")->as_string()); - } catch (const out_of_range&) { - } + this->welcome_message = decode_sjis(json.get("WelcomeMessage", "")); + this->pc_patch_server_message = decode_sjis(json.get("PCPatchServerMessage", "")); + this->bb_patch_server_message = decode_sjis(json.get("BBPatchServerMessage", "")); } shared_ptr ServerState::load_bb_file( @@ -558,33 +546,31 @@ void ServerState::collect_network_addresses() { } } -shared_ptr ServerState::load_config() const { +JSON ServerState::load_config() const { config_log.info("Loading configuration"); - return JSONObject::parse(load_file(this->config_filename)); + return JSON::parse(load_file(this->config_filename)); } -static vector parse_port_configuration( - shared_ptr json) { +static vector parse_port_configuration(const JSON& json) { vector ret; - for (const auto& item_json_it : json->as_dict()) { - auto item_list = item_json_it.second->as_list(); + for (const auto& item_json_it : json.as_dict()) { + auto item_list = item_json_it.second.as_list(); PortConfiguration& pc = ret.emplace_back(); pc.name = item_json_it.first; - pc.port = item_list[0]->as_int(); - pc.version = version_for_name(item_list[1]->as_string().c_str()); - pc.behavior = server_behavior_for_name(item_list[2]->as_string().c_str()); + pc.port = item_list[0]; + pc.version = version_for_name(item_list[1].as_string().c_str()); + pc.behavior = server_behavior_for_name(item_list[2].as_string().c_str()); } return ret; } -void ServerState::parse_config(shared_ptr config_json) { +void ServerState::parse_config(const JSON& json) { config_log.info("Parsing configuration"); - const auto& d = config_json->as_dict(); - this->name = decode_sjis(d.at("ServerName")->as_string()); + this->name = decode_sjis(json.at("ServerName").as_string()); try { - this->username = d.at("User")->as_string(); + this->username = json.at("User").as_string(); if (this->username == "$SUDO_USER") { const char* user_from_env = getenv("SUDO_USER"); if (!user_from_env) { @@ -595,9 +581,9 @@ void ServerState::parse_config(shared_ptr config_json) { } catch (const out_of_range&) { } - this->set_port_configuration(parse_port_configuration(d.at("PortConfiguration"))); + this->set_port_configuration(parse_port_configuration(json.at("PortConfiguration"))); - auto local_address_str = d.at("LocalAddress")->as_string(); + auto local_address_str = json.at("LocalAddress").as_string(); try { this->local_address = this->all_addresses.at(local_address_str); string addr_str = string_for_address(this->local_address); @@ -609,7 +595,7 @@ void ServerState::parse_config(shared_ptr config_json) { } this->all_addresses.emplace("", this->local_address); - auto external_address_str = d.at("ExternalAddress")->as_string(); + auto external_address_str = json.at("ExternalAddress").as_string(); try { this->external_address = this->all_addresses.at(external_address_str); string addr_str = string_for_address(this->external_address); @@ -621,83 +607,33 @@ void ServerState::parse_config(shared_ptr config_json) { } this->all_addresses.emplace("", this->external_address); - try { - this->dns_server_port = d.at("DNSServerPort")->as_int(); - } catch (const out_of_range&) { - this->dns_server_port = 0; - } + this->dns_server_port = json.get("DNSServerPort", this->dns_server_port); try { - for (const auto& item : d.at("IPStackListen")->as_list()) { - this->ip_stack_addresses.emplace_back(item->as_string()); + for (const auto& item : json.at("IPStackListen").as_list()) { + this->ip_stack_addresses.emplace_back(item.as_string()); } } catch (const out_of_range&) { } - try { - this->ip_stack_debug = d.at("IPStackDebug")->as_bool(); - } catch (const out_of_range&) { - } + this->ip_stack_debug = json.get("IPStackDebug", this->ip_stack_debug); + this->allow_unregistered_users = json.get("AllowUnregisteredUsers", this->allow_unregistered_users); + this->item_tracking_enabled = json.get("EnableItemTracking", this->item_tracking_enabled); + this->drops_enabled = json.get("EnableDrops", this->drops_enabled); + this->episode_3_send_function_call_enabled = json.get("EnableEpisode3SendFunctionCall", this->episode_3_send_function_call_enabled); + this->catch_handler_exceptions = json.get("CatchHandlerExceptions", this->catch_handler_exceptions); + this->proxy_allow_save_files = json.get("ProxyAllowSaveFiles", this->proxy_allow_save_files); + this->proxy_enable_login_options = json.get("ProxyEnableLoginOptions", this->proxy_enable_login_options); + this->ep3_behavior_flags = json.get("Episode3BehaviorFlags", this->ep3_behavior_flags); + this->ep3_card_auction_points = json.get("CardAuctionPoints", this->ep3_card_auction_points); try { - this->allow_unregistered_users = d.at("AllowUnregisteredUsers")->as_bool(); - } catch (const out_of_range&) { - this->allow_unregistered_users = true; - } - - try { - this->item_tracking_enabled = d.at("EnableItemTracking")->as_bool(); - } catch (const out_of_range&) { - this->item_tracking_enabled = true; - } - - try { - this->drops_enabled = d.at("EnableDrops")->as_bool(); - } catch (const out_of_range&) { - this->drops_enabled = true; - } - - try { - this->episode_3_send_function_call_enabled = d.at("EnableEpisode3SendFunctionCall")->as_bool(); - } catch (const out_of_range&) { - this->episode_3_send_function_call_enabled = false; - } - - try { - this->catch_handler_exceptions = d.at("CatchHandlerExceptions")->as_bool(); - } catch (const out_of_range&) { - this->catch_handler_exceptions = true; - } - - try { - this->proxy_allow_save_files = d.at("ProxyAllowSaveFiles")->as_bool(); - } catch (const out_of_range&) { - this->proxy_allow_save_files = true; - } - try { - this->proxy_enable_login_options = d.at("ProxyEnableLoginOptions")->as_bool(); - } catch (const out_of_range&) { - this->proxy_enable_login_options = false; - } - - try { - this->ep3_behavior_flags = d.at("Episode3BehaviorFlags")->as_int(); - } catch (const out_of_range&) { - this->ep3_behavior_flags = 0; - } - - try { - this->ep3_card_auction_points = d.at("CardAuctionPoints")->as_int(); - } catch (const out_of_range&) { - this->ep3_card_auction_points = 0; - } - try { - auto i = d.at("CardAuctionSize"); - if (i->is_int()) { - this->ep3_card_auction_min_size = i->as_int(); + const auto& i = json.at("CardAuctionSize"); + if (i.is_int()) { + this->ep3_card_auction_min_size = i; this->ep3_card_auction_max_size = this->ep3_card_auction_min_size; } else { - this->ep3_card_auction_min_size = i->as_list().at(0)->as_int(); - this->ep3_card_auction_max_size = i->as_list().at(1)->as_int(); + this->ep3_card_auction_min_size = i.at(0); + this->ep3_card_auction_max_size = i.at(1); } } catch (const out_of_range&) { this->ep3_card_auction_min_size = 0; @@ -705,22 +641,15 @@ void ServerState::parse_config(shared_ptr config_json) { } try { - for (const auto& it : d.at("CardAuctionPool")->as_dict()) { + for (const auto& it : json.at("CardAuctionPool").as_dict()) { const auto& card_name = it.first; - const auto& card_cfg_json = it.second->as_list(); - this->ep3_card_auction_pool.emplace(card_name, make_pair(card_cfg_json.at(0)->as_int(), card_cfg_json.at(1)->as_int())); + const auto& card_cfg_json = it.second.as_list(); + this->ep3_card_auction_pool.emplace(card_name, make_pair(card_cfg_json.at(0), card_cfg_json.at(1))); } } catch (const out_of_range&) { } - shared_ptr log_levels_json; - try { - log_levels_json = d.at("LogLevels"); - } catch (const out_of_range&) { - } - if (log_levels_json.get()) { - set_log_levels_from_json(log_levels_json); - } + set_log_levels_from_json(json.get("LogLevels", JSON::dict_type())); for (const string& filename : list_directory("system/blueburst/keys")) { if (!ends_with(filename, ".nsk")) { @@ -733,13 +662,14 @@ void ServerState::parse_config(shared_ptr config_json) { config_log.info("%zu Blue Burst key file(s) loaded", this->bb_private_keys.size()); try { - bool run_shell = d.at("RunInteractiveShell")->as_bool(); - this->run_shell_behavior = run_shell ? ServerState::RunShellBehavior::ALWAYS : ServerState::RunShellBehavior::NEVER; + this->run_shell_behavior = json.at("RunInteractiveShell").as_bool() + ? ServerState::RunShellBehavior::ALWAYS + : ServerState::RunShellBehavior::NEVER; } catch (const out_of_range&) { } try { - const string& behavior = d.at("CheatModeBehavior")->as_string(); + const string& behavior = json.at("CheatModeBehavior").as_string(); if (behavior == "Off") { this->cheat_mode_behavior = CheatModeBehavior::OFF; } else if (behavior == "OffByDefault") { @@ -753,8 +683,8 @@ void ServerState::parse_config(shared_ptr config_json) { } try { - auto v = d.at("LobbyEvent"); - uint8_t event = v->is_int() ? v->as_int() : event_for_name(v->as_string()); + auto v = json.at("LobbyEvent"); + uint8_t event = v.is_int() ? v.as_int() : event_for_name(v.as_string()); this->pre_lobby_event = event; for (const auto& l : this->all_lobbies()) { l->event = event; @@ -762,13 +692,10 @@ void ServerState::parse_config(shared_ptr config_json) { } catch (const out_of_range&) { } - try { - this->ep3_menu_song = d.at("Episode3MenuSong")->as_int(); - } catch (const out_of_range&) { - } + this->ep3_menu_song = json.get("Episode3MenuSong", this->ep3_menu_song); try { - this->quest_category_index.reset(new QuestCategoryIndex(d.at("QuestCategories"))); + this->quest_category_index.reset(new QuestCategoryIndex(json.at("QuestCategories"))); } catch (const exception& e) { throw runtime_error(string_printf( "QuestCategories is missing or invalid in config.json (%s) - see config.example.json for an example", e.what())); @@ -825,7 +752,7 @@ void ServerState::load_level_table() { void ServerState::load_item_tables() { try { config_log.info("Loading JSON rare item table"); - auto json = JSONObject::parse(load_file("system/blueburst/rare-table.json")); + auto json = JSON::parse(load_file("system/blueburst/rare-table.json")); this->rare_item_set.reset(new JSONRareItemSet(json)); } catch (const exception& e) { config_log.info("Failed to load JSON rare item table: %s", e.what()); @@ -900,6 +827,9 @@ void ServerState::load_ep3_data() { this->ep3_tournament_index.reset(new Episode3::TournamentIndex( this->ep3_map_index, this->ep3_com_deck_index, tournament_state_filename, true)); } + + config_log.info("Resolving Episode 3 card auction pool"); + TODO; } void ServerState::load_quest_index() { diff --git a/src/ServerState.hh b/src/ServerState.hh index 5aab95ac..4d788257 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -169,9 +169,9 @@ struct ServerState { const std::string& gsl_filename = "", const std::string& bb_directory_filename = "") const; - std::shared_ptr load_config() const; + JSON load_config() const; void collect_network_addresses(); - void parse_config(std::shared_ptr config_json); + void parse_config(const JSON& config_json); void load_licenses(); void load_patch_indexes(); void load_battle_params(); @@ -181,5 +181,5 @@ struct ServerState { void load_quest_index(); void compile_functions(); void load_dol_files(); - void create_menus(std::shared_ptr config_json); + void create_menus(const JSON& config_json); }; diff --git a/system/config.example.json b/system/config.example.json index c4f12061..8d7463eb 100644 --- a/system/config.example.json +++ b/system/config.example.json @@ -163,58 +163,58 @@ // Specify which kinds of logging you want to be enabled. This allows you to // make the terminal more or less noisy when players are connected, so you can // see only the log messages you care about. The log levels are, in decreasing - // order of verbosity, "debug", "info", "warning", "error", and "disabled". + // order of verbosity, "DEBUG", "INFO", "WARNING", "ERROR", and "DISABLED". "LogLevels": { // AX messages are messages sent to the terminal with the $ax command. - "AXMessages": "info", + "AXMessages": "INFO", // Channel exceptions are messages about clients disconnecting unexpectedly, // or other unexpected network-level events. - "ChannelExceptions": "info", + "ChannelExceptions": "INFO", // Client messages describe events that are specific to a single client's // connection or game state. - "Clients": "info", + "Clients": "INFO", // Command data messages show the raw data for all commands sent and // received, on both the game server and proxy server. If stderr is a // terminal, these messages are colored as well; green is for commands sent // by the client, yellow is for commands sent by newserv, and red is for - // commands sent by the remote server (for proxy server sessions). - "CommandData": "info", + // commands sent by the remote server (in proxy server sessions). + "CommandData": "INFO", // Config messages describe server-wide events, and generally only occur // during the startup procedure. - "Config": "info", + "Config": "INFO", // DNS server messages describe erroneous queries that the DNS server does // not respond to. Normal DNS queries do not generate any log messages. - "DNSServer": "info", + "DNSServer": "INFO", // Function compiler messages describe PowerPC function call assembly // events, which generally only occur during startup. - "FunctionCompiler": "info", + "FunctionCompiler": "INFO", // IP stack simulator messages describe clients connecting and disconnecting // via the IP stack interface, and errors that occur at the simulated // network level within the simulator. This log is fairly verbose at the // info level, so by default we suppress those messages. - "IPStackSimulator": "warning", + "IPStackSimulator": "WARNING", // License manager messages describe the creation of new license files. - "LicenseManager": "info", + "LicenseManager": "INFO", // Lobby messages describe creation and deletion of lobbies and games, as // well as item tracking events within games. - "Lobbies": "info", + "Lobbies": "INFO", // Patch file index messages describe finding and preloading the patch files // available for download to BB and PC clients. - "PatchFileIndex": "info", + "PatchFileIndex": "INFO", // Player data messages describe the loading and saving of player and // account data files. - "PlayerData": "info", + "PlayerData": "INFO", // Proxy server messages describe clients connecting and disconnecting from // the proxy server, as well as events that occur in each session. - "ProxyServer": "info", + "ProxyServer": "INFO", // Replay messages are generated when replaying a session log (usually // during functional testing). - "Replay": "info", + "Replay": "INFO", // Game server messages describe clients connecting and disconnecting from // the game server. - "GameServer": "info", + "GameServer": "INFO", // Static game data messages describe the loading of any kind of game data. - "StaticGameData": "info", + "StaticGameData": "INFO", }, // By default, the server only allows users who are registered in the license @@ -315,13 +315,32 @@ // enter an auction, the auctioned cards are drawn with replacement from the // distribution specified here. "CardAuctionPoints": 30, - "CardAuctionSize": [2, 4], + "CardAuctionSize": [6, 8], "CardAuctionPool": { // "CardName": [RelativeFrequency, MinPrice] - "Red Sword": [500, 8], - "Hildeblue": [400, 10], - "Grants": [300, 15], - "Megid": [700, 6], + "Beat+": [500, 4], + "Berserk+": [800, 7], + "Biboo": [500, 6], + "Chaos Bringer": [900, 3], + "Charity+": [800, 4], + "Counter+": [800, 9], + "Dark Flow": [600, 11], + "Dolmolm": [500, 8], + "Egg Rappy": [800, 2], + "Epsilon": [300, 7], + "Gather+": [800, 8], + "Gibbles+": [700, 6], + "Grants": [200, 4], + "Hallo Rappy+": [800, 3], + "Heart of Poumn": [600, 5], + "Heaven Punisher": [500, 6], + "Lavis Cannon": [400, 7], + "Piety": [800, 5], + "Rag Rappy+": [800, 2], + "Rich+": [700, 2], + "Snail Pace": [900, 3], + "Striker of Chao": [500, 5], + "Thread+": [800, 8], }, // Quest category configuration. See README.md for information on how quest diff --git a/tests/config.json b/tests/config.json index 8cc8b72d..b4d11a32 100644 --- a/tests/config.json +++ b/tests/config.json @@ -58,20 +58,20 @@ }, "LogLevels": { - "AXMessages": "info", - "ChannelExceptions": "info", - "Clients": "info", - "CommandData": "info", - "Config": "info", - "DNSServer": "info", - "FunctionCompiler": "info", - "IPStackSimulator": "info", - "LicenseManager": "info", - "Lobbies": "info", - "PlayerData": "info", - "ProxyServer": "info", - "GameServer": "info", - "StaticGameData": "info", + "AXMessages": "INFO", + "ChannelExceptions": "INFO", + "Clients": "INFO", + "CommandData": "INFO", + "Config": "INFO", + "DNSServer": "INFO", + "FunctionCompiler": "INFO", + "IPStackSimulator": "INFO", + "LicenseManager": "INFO", + "Lobbies": "INFO", + "PlayerData": "INFO", + "ProxyServer": "INFO", + "GameServer": "INFO", + "StaticGameData": "INFO", }, "AllowUnregisteredUsers": true,