use new phosg JSON interface
This commit is contained in:
+122
-143
@@ -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 <typename IntT>
|
||||
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 <typename EnumT>
|
||||
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<EnumT>(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<const JSONObject> 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<JSONObject> Rules::json() const {
|
||||
unordered_map<string, shared_ptr<JSONObject>> 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<JSONObject>(new JSONObject(std::move(dict)));
|
||||
JSON Rules::json() const {
|
||||
unordered_map<string, JSON> 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<uint32_t> 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<const COMDeckDefinition> COMDeckIndex::random_deck() const {
|
||||
}
|
||||
|
||||
} // namespace Episode3
|
||||
|
||||
template <>
|
||||
Episode3::HPType enum_for_name<Episode3::HPType>(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>(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<Episode3::DiceExchangeMode>(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>(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<Episode3::AllowedCards>(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>(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");
|
||||
}
|
||||
}
|
||||
|
||||
+17
-11
@@ -7,6 +7,7 @@
|
||||
#include <phosg/Encoding.hh>
|
||||
#include <phosg/JSON.hh>
|
||||
#include <phosg/Tools.hh>
|
||||
#include <phosg/Types.hh>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -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<const JSONObject> json);
|
||||
std::shared_ptr<JSONObject> 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<Episode3::HPType>(const char* name);
|
||||
template <>
|
||||
const char* name_for_enum<Episode3::HPType>(Episode3::HPType hp_type);
|
||||
template <>
|
||||
Episode3::DiceExchangeMode enum_for_name<Episode3::DiceExchangeMode>(const char* name);
|
||||
template <>
|
||||
const char* name_for_enum<Episode3::DiceExchangeMode>(Episode3::DiceExchangeMode dice_exchange_mode);
|
||||
template <>
|
||||
Episode3::AllowedCards enum_for_name<Episode3::AllowedCards>(const char* name);
|
||||
template <>
|
||||
const char* name_for_enum<Episode3::AllowedCards>(Episode3::AllowedCards allowed_cards);
|
||||
|
||||
+44
-51
@@ -313,7 +313,7 @@ Tournament::Tournament(
|
||||
shared_ptr<const MapIndex> map_index,
|
||||
shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
std::shared_ptr<const JSONObject> 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<JSONObject> Tournament::json() const {
|
||||
unordered_map<string, shared_ptr<JSONObject>> 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<shared_ptr<JSONObject>> teams_list;
|
||||
JSON Tournament::json() const {
|
||||
auto teams_list = JSON::list();
|
||||
for (auto team : this->teams) {
|
||||
unordered_map<string, shared_ptr<JSONObject>> team_dict;
|
||||
team_dict.emplace("max_players", make_json_int(team->max_players));
|
||||
vector<shared_ptr<JSONObject>> 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<JSONObject>(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<shared_ptr<JSONObject>> 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<shared_ptr<Tournament>> TournamentIndex::all_tournaments() const {
|
||||
|
||||
@@ -104,11 +104,11 @@ public:
|
||||
std::shared_ptr<const MapIndex> map_index,
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index,
|
||||
uint8_t number,
|
||||
std::shared_ptr<const JSONObject> json);
|
||||
const JSON& json);
|
||||
~Tournament() = default;
|
||||
void init();
|
||||
|
||||
std::shared_ptr<JSONObject> json() const;
|
||||
JSON json() const;
|
||||
|
||||
uint8_t get_number() const;
|
||||
const std::string& get_name() const;
|
||||
@@ -134,7 +134,7 @@ private:
|
||||
|
||||
std::shared_ptr<const MapIndex> map_index;
|
||||
std::shared_ptr<const COMDeckIndex> com_deck_index;
|
||||
std::shared_ptr<const JSONObject> source_json;
|
||||
JSON source_json;
|
||||
uint8_t number;
|
||||
std::string name;
|
||||
std::shared_ptr<const MapIndex::MapEntry> map;
|
||||
|
||||
+5
-15
@@ -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<string, LogLevel> 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<JSONObject> 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<LogLevel>(name.c_str());
|
||||
} catch (const out_of_range&) {
|
||||
}
|
||||
}
|
||||
|
||||
void set_log_levels_from_json(shared_ptr<JSONObject> 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");
|
||||
|
||||
+1
-1
@@ -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<JSONObject> json);
|
||||
void set_log_levels_from_json(const JSON& json);
|
||||
|
||||
+24
-36
@@ -40,10 +40,10 @@ using namespace std;
|
||||
bool use_terminal_colors = false;
|
||||
|
||||
template <typename T>
|
||||
vector<T> parse_int_vector(shared_ptr<const JSONObject> o) {
|
||||
vector<T> parse_int_vector(const JSON& o) {
|
||||
vector<T> 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<string> data(new string(read_input_data()));
|
||||
shared_ptr<RareItemSet> 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<pair<Episode, vector<vector<EnemyType>>>, 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<uint64_t>(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<uint64_t>(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;
|
||||
}
|
||||
|
||||
|
||||
+19
-25
@@ -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<JSONObject> 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<JSONObject> new_metadata_cache_json = make_json_dict({});
|
||||
JSON new_metadata_cache_json = JSON::dict();
|
||||
|
||||
vector<string> path_directories;
|
||||
function<void(const string&)> 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<JSONObject> 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<uint64_t>(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<shared_ptr<JSONObject>> chunk_crcs_item;
|
||||
vector<JSON> 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<shared_ptr<JSONObject>> 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());
|
||||
|
||||
+8
-9
@@ -21,14 +21,13 @@
|
||||
|
||||
using namespace std;
|
||||
|
||||
QuestCategoryIndex::Category::Category(uint32_t category_id, std::shared_ptr<const JSONObject> 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<const JSONObject> 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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -30,14 +30,14 @@ struct QuestCategoryIndex {
|
||||
std::u16string name;
|
||||
std::u16string description;
|
||||
|
||||
explicit Category(uint32_t category_id, std::shared_ptr<const JSONObject> json);
|
||||
explicit Category(uint32_t category_id, const JSON& json);
|
||||
|
||||
bool matches_flags(uint8_t request) const;
|
||||
};
|
||||
|
||||
std::vector<Category> categories;
|
||||
|
||||
explicit QuestCategoryIndex(std::shared_ptr<const JSONObject> 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;
|
||||
|
||||
+16
-16
@@ -170,27 +170,27 @@ const RELRareItemSet::Table& RELRareItemSet::get_table(
|
||||
return tables[(ep_index * 10 * 4) + (difficulty * 10) + secid];
|
||||
}
|
||||
|
||||
JSONRareItemSet::JSONRareItemSet(std::shared_ptr<const JSONObject> 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<string, GameMode> 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<string, Episode> 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<string, uint8_t> 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<ExpandedDrop>* 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<const JSONObject> 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<const JSONObject> 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];
|
||||
|
||||
+1
-1
@@ -92,7 +92,7 @@ private:
|
||||
|
||||
class JSONRareItemSet : public RareItemSet {
|
||||
public:
|
||||
JSONRareItemSet(std::shared_ptr<const JSONObject> json);
|
||||
explicit JSONRareItemSet(const JSON& json);
|
||||
virtual ~JSONRareItemSet() = default;
|
||||
|
||||
virtual std::vector<ExpandedDrop> get_enemy_specs(GameMode mode, Episode episode, uint8_t difficulty, uint8_t secid, uint8_t rt_index) const;
|
||||
|
||||
+64
-134
@@ -384,9 +384,8 @@ void ServerState::set_port_configuration(
|
||||
}
|
||||
}
|
||||
|
||||
void ServerState::create_menus(shared_ptr<const JSONObject> config_json) {
|
||||
void ServerState::create_menus(const JSON& json) {
|
||||
config_log.info("Creating menus");
|
||||
const auto& d = config_json->as_dict();
|
||||
|
||||
shared_ptr<Menu> information_menu_v2(new Menu(MenuID::INFORMATION, u"Information"));
|
||||
shared_ptr<Menu> information_menu_v3(new Menu(MenuID::INFORMATION, u"Information"));
|
||||
@@ -398,13 +397,12 @@ void ServerState::create_menus(shared_ptr<const JSONObject> 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<const JSONObject> config_json) {
|
||||
ret_pds.clear();
|
||||
|
||||
try {
|
||||
map<string, shared_ptr<JSONObject>> sorted_jsons;
|
||||
for (const auto& it : d.at(key)->as_dict()) {
|
||||
map<string, const JSON&> 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<const JSONObject> 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<const JSONObject> 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<const JSONObject> 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<const JSONObject> 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<const string> ServerState::load_bb_file(
|
||||
@@ -558,33 +546,31 @@ void ServerState::collect_network_addresses() {
|
||||
}
|
||||
}
|
||||
|
||||
shared_ptr<JSONObject> 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<PortConfiguration> parse_port_configuration(
|
||||
shared_ptr<const JSONObject> json) {
|
||||
static vector<PortConfiguration> parse_port_configuration(const JSON& json) {
|
||||
vector<PortConfiguration> 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<const JSONObject> 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<const JSONObject> 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<const JSONObject> config_json) {
|
||||
}
|
||||
this->all_addresses.emplace("<local>", 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<const JSONObject> config_json) {
|
||||
}
|
||||
this->all_addresses.emplace("<external>", 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<const JSONObject> 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<JSONObject> 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<const JSONObject> 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<const JSONObject> 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<const JSONObject> 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() {
|
||||
|
||||
+3
-3
@@ -169,9 +169,9 @@ struct ServerState {
|
||||
const std::string& gsl_filename = "",
|
||||
const std::string& bb_directory_filename = "") const;
|
||||
|
||||
std::shared_ptr<JSONObject> load_config() const;
|
||||
JSON load_config() const;
|
||||
void collect_network_addresses();
|
||||
void parse_config(std::shared_ptr<const JSONObject> 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<const JSONObject> config_json);
|
||||
void create_menus(const JSON& config_json);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user