diff --git a/src/CommonItemSet.cc b/src/CommonItemSet.cc index 11950985..d2531651 100644 --- a/src/CommonItemSet.cc +++ b/src/CommonItemSet.cc @@ -1,11 +1,123 @@ #include "CommonItemSet.hh" #include "AFSArchive.hh" +#include "EnemyType.hh" #include "GSLArchive.hh" #include "StaticGameData.hh" using namespace std; +template +JSON to_json(const parray& v) { + auto ret = JSON::list(); + for (size_t z = 0; z < Count; z++) { + ret.emplace_back(v[z]); + } + return ret; +} + +template +JSON to_json(const parray, Count>& v) { + auto ret = JSON::list(); + for (size_t z = 0; z < Count; z++) { + ret.emplace_back(to_json(v[z])); + } + return ret; +} + +template +JSON to_json(const CommonItemSet::Table::Range& v) { + if (v.min == v.max) { + return JSON(v.min); + } else { + return JSON::list({v.min, v.max}); + } +} + +template +JSON to_json(const parray, Count1>& v) { + auto ret = JSON::list(); + for (size_t z = 0; z < Count1; z++) { + ret.emplace_back(to_json(v[z])); + } + return ret; +} + +JSON CommonItemSet::Table::json() const { + JSON enemy_meseta_ranges_json = JSON::dict(); + JSON enemy_type_drop_probs_json = JSON::dict(); + JSON enemy_item_classes_json = JSON::dict(); + for (size_t z = 0; z < 0x64; z++) { + static const array episodes = {Episode::EP1, Episode::EP2, Episode::EP4}; + for (Episode episode : episodes) { + JSON enemy_meseta_ranges_episode_json = JSON::dict(); + JSON enemy_type_drop_probs_episode_json = JSON::dict(); + JSON enemy_item_classes_episode_json = JSON::dict(); + for (auto type : enemy_types_for_rare_table_index(episode, z)) { + string type_str = name_for_enum(type); + enemy_meseta_ranges_episode_json.emplace(type_str, to_json(this->enemy_meseta_ranges[z])); + enemy_type_drop_probs_episode_json.emplace(type_str, this->enemy_type_drop_probs[z]); + enemy_item_classes_episode_json.emplace(type_str, this->enemy_item_classes[z]); + } + string name = name_for_episode(episode); + enemy_meseta_ranges_json.emplace(name, std::move(enemy_meseta_ranges_episode_json)); + enemy_type_drop_probs_json.emplace(name, std::move(enemy_type_drop_probs_episode_json)); + enemy_item_classes_json.emplace(name, std::move(enemy_item_classes_episode_json)); + } + } + return JSON::dict({ + {"BaseWeaponTypeProbTable", to_json(this->base_weapon_type_prob_table)}, + {"SubtypeBaseTable", to_json(this->subtype_base_table)}, + {"SubtypeAreaLengthTable", to_json(this->subtype_area_length_table)}, + {"GrindProbTable", to_json(this->grind_prob_table)}, + {"ArmorShieldTypeIndexProbTable", to_json(this->armor_shield_type_index_prob_table)}, + {"ArmorSlotCountProbTable", to_json(this->armor_slot_count_prob_table)}, + {"EnemyMesetaRanges", std::move(enemy_meseta_ranges_json)}, + {"EnemyTypeDropProbs", std::move(enemy_type_drop_probs_json)}, + {"EnemyItemClasses", std::move(enemy_item_classes_json)}, + {"BoxMesetaRanges", to_json(this->box_meseta_ranges)}, + {"HasRareBonusValueProbTable", this->has_rare_bonus_value_prob_table}, + {"BonusValueProbTable", to_json(this->bonus_value_prob_table)}, + {"NonRareBonusProbSpec", to_json(this->nonrare_bonus_prob_spec)}, + {"BonusTypeProbTable", to_json(this->bonus_type_prob_table)}, + {"SpecialMult", to_json(this->special_mult)}, + {"SpecialPercent", to_json(this->special_percent)}, + {"ToolClassProbTable", to_json(this->tool_class_prob_table)}, + {"TechniqueIndexProbTable", to_json(this->technique_index_prob_table)}, + {"TechniqueLevelRanges", to_json(this->technique_level_ranges)}, + {"ArmorOrShieldTypeBias", this->armor_or_shield_type_bias}, + {"UnitMaxStarsTable", to_json(this->unit_max_stars_table)}, + {"BoxItemClassProbTable", to_json(this->box_item_class_prob_table)}, + }); +} + +JSON CommonItemSet::json() const { + auto modes_dict = JSON::dict(); + static const array modes = {GameMode::NORMAL, GameMode::BATTLE, GameMode::CHALLENGE, GameMode::SOLO}; + for (const auto& mode : modes) { + auto episodes_dict = JSON::dict(); + static const array episodes = {Episode::EP1, Episode::EP2, Episode::EP4}; + for (const auto& episode : episodes) { + auto difficulty_dict = JSON::dict(); + for (uint8_t difficulty = 0; difficulty < 4; difficulty++) { + auto section_id_dict = JSON::dict(); + for (uint8_t section_id = 0; section_id < 10; section_id++) { + try { + auto table = this->get_table(episode, mode, difficulty, section_id); + section_id_dict.emplace(name_for_section_id(section_id), table->json()); + } catch (const runtime_error&) { + } + } + difficulty_dict.emplace(token_name_for_difficulty(difficulty), std::move(section_id_dict)); + } + episodes_dict.emplace(token_name_for_episode(episode), std::move(difficulty_dict)); + } + modes_dict.emplace(name_for_mode(mode), std::move(episodes_dict)); + } + + return modes_dict; +} + CommonItemSet::Table::Table(const StringReader& r, bool is_big_endian, bool is_v3) { if (is_big_endian) { this->parse_itempt_t(r, is_v3); diff --git a/src/CommonItemSet.hh b/src/CommonItemSet.hh index 329a5fe3..90f40ac9 100644 --- a/src/CommonItemSet.hh +++ b/src/CommonItemSet.hh @@ -2,6 +2,7 @@ #include #include +#include #include "GSLArchive.hh" #include "PSOEncryption.hh" @@ -45,6 +46,7 @@ public: parray, 7> box_item_class_prob_table; void print_enemy_table(FILE* stream) const; + JSON json() const; private: template @@ -256,6 +258,7 @@ public: }; std::shared_ptr get_table(Episode episode, GameMode mode, uint8_t difficulty, uint8_t secid) const; + JSON json() const; protected: CommonItemSet() = default; diff --git a/src/HTTPServer.cc b/src/HTTPServer.cc index bcf8d5c0..43025c4d 100644 --- a/src/HTTPServer.cc +++ b/src/HTTPServer.cc @@ -844,6 +844,16 @@ JSON HTTPServer::generate_ep3_cards_json(bool trial) const { return index->definitions_json(); } +JSON HTTPServer::generate_common_tables_json() const { + auto [set_v2, set_v3_v4] = call_on_event_thread, shared_ptr>>(this->state->base, [&]() { + return make_pair(this->state->common_item_set_v2, this->state->common_item_set_v3_v4); + }); + return JSON::dict({ + {"v1_v2", set_v2->json()}, + {"v3_v4", set_v3_v4->json()}, + }); +} + JSON HTTPServer::generate_rare_tables_json() const { auto sets = call_on_event_thread>>(this->state->base, [&]() { return this->state->rare_item_sets; @@ -880,9 +890,10 @@ JSON HTTPServer::generate_rare_table_json(const std::string& table_name) const { void HTTPServer::handle_request(struct evhttp_request* req) { shared_ptr ret; uint32_t serialize_options = 0; - try { - string uri = evhttp_request_get_uri(req); + uint64_t start_time = now(); + string uri = evhttp_request_get_uri(req); + try { std::unordered_multimap query; size_t query_pos = uri.find('?'); if (query_pos != string::npos) { @@ -902,6 +913,7 @@ void HTTPServer::handle_request(struct evhttp_request* req) { auto endpoints_json = JSON::list({ "/y/data/ep3-cards", "/y/data/ep3-cards-trial", + "/y/data/common-tables", "/y/data/rare-tables", "/y/data/rare-tables/", "/y/data/config", @@ -918,6 +930,8 @@ void HTTPServer::handle_request(struct evhttp_request* req) { ret = make_shared(this->generate_ep3_cards_json(false)); } else if (uri == "/y/data/ep3-cards-trial") { ret = make_shared(this->generate_ep3_cards_json(true)); + } else if (uri == "/y/data/common-tables") { + ret = make_shared(this->generate_common_tables_json()); } else if (uri == "/y/data/rare-tables") { ret = make_shared(this->generate_rare_tables_json()); } else if (!strncmp(uri.c_str(), "/y/data/rare-tables/", 20)) { @@ -955,13 +969,22 @@ void HTTPServer::handle_request(struct evhttp_request* req) { return; } + uint64_t handler_end = now(); unique_ptr out_buffer(evbuffer_new(), evbuffer_free); string* serialized = new string(ret->serialize(JSON::SerializeOption::ESCAPE_CONTROLS_ONLY | serialize_options)); + size_t size = serialized->size(); + uint64_t serialize_end = now(); auto cleanup = +[](const void*, size_t, void* s) -> void { delete reinterpret_cast(s); }; evbuffer_add_reference(out_buffer.get(), serialized->data(), serialized->size(), cleanup, serialized); this->send_response(req, 200, "application/json", out_buffer.get()); + + string handler_time = format_duration(handler_end - start_time); + string serialize_time = format_duration(serialize_end - handler_end); + string size_str = format_size(size); + server_log.info("[HTTPServer] %s in [handler: %s, serialize: %s, size: %s]", + uri.c_str(), handler_time.c_str(), serialize_time.c_str(), size_str.c_str()); } void HTTPServer::thread_fn() { diff --git a/src/HTTPServer.hh b/src/HTTPServer.hh index 69845f74..f7c9c410 100644 --- a/src/HTTPServer.hh +++ b/src/HTTPServer.hh @@ -70,6 +70,7 @@ protected: JSON generate_all_json() const; JSON generate_ep3_cards_json(bool trial) const; + JSON generate_common_tables_json() const; JSON generate_rare_tables_json() const; JSON generate_rare_table_json(const std::string& table_name) const; };