diff --git a/src/Main.cc b/src/Main.cc index 2e8b58e8..9d33493d 100644 --- a/src/Main.cc +++ b/src/Main.cc @@ -2637,7 +2637,7 @@ Action a_load_maps_test( s->load_maps(false); SuperMap::EfficiencyStats all_free_maps_eff; - for (const auto& it : s->supermaps) { + for (const auto& it : s->supermap_for_free_play_key) { auto episode = static_cast((it.first >> 28) & 7); auto mode = static_cast((it.first >> 26) & 3); uint8_t difficulty = (it.first >> 24) & 3; diff --git a/src/Quest.cc b/src/Quest.cc index 82f9df7e..640a160a 100644 --- a/src/Quest.cc +++ b/src/Quest.cc @@ -636,7 +636,7 @@ QuestIndex::QuestIndex( } else if (phosg::ends_with(filename, ".dlq")) { file_data = decode_dlq_data(phosg::load_file(file_path)); filename.resize(filename.size() - 4); - } else if (phosg::ends_with(filename, ".txt")) { + } else if (phosg::ends_with(filename, ".bin.txt")) { string include_dir = phosg::dirname(file_path); file_data = assemble_quest_script(phosg::load_file(file_path), include_dir); filename.resize(filename.size() - 4); @@ -891,11 +891,6 @@ QuestIndex::QuestIndex( static_game_data_log.warning("(%s) Failed to index quest file: (%s)", basename.c_str(), e.what()); } } - - // Create supermaps for all quests that need them (all non-Ep3 quests) - for (const auto& it : this->quests_by_number) { - it.second->get_supermap(-1); - } } phosg::JSON QuestIndex::json() const { diff --git a/src/QuestScript.cc b/src/QuestScript.cc index 6f7d9767..8090cbbc 100644 --- a/src/QuestScript.cc +++ b/src/QuestScript.cc @@ -2874,11 +2874,11 @@ opcodes_by_name_for_version(Version v) { if (!(def.flags & vf)) { continue; } - if (def.name && !index.emplace(def.name, &def).second) { + if (def.name && !index.emplace(phosg::tolower(def.name), &def).second) { throw logic_error(phosg::string_printf("duplicate definition for opcode %04hX", def.opcode)); } if (def.qedit_name) { - string lower_qedit_name = phosg::tolower(def.qedit_name); + string lower_qedit_name = phosg::tolower(phosg::tolower(def.qedit_name)); if ((lower_qedit_name != def.name) && !index.emplace(lower_qedit_name, &def).second) { throw logic_error(phosg::string_printf("duplicate definition for opcode %04hX", def.opcode)); } diff --git a/src/ServerState.cc b/src/ServerState.cc index 341b9fe6..abaa109d 100644 --- a/src/ServerState.cc +++ b/src/ServerState.cc @@ -1567,8 +1567,8 @@ void ServerState::load_maps(bool from_non_event_thread) { config_log.info("Loading free play map files"); - unordered_map> map_file_for_source_hash; - map, NUM_VERSIONS>> map_files; + unordered_map> new_map_file_for_source_hash; + map, NUM_VERSIONS>> new_map_files_for_free_play_key; for (Version v : ALL_ARPG_SEMANTIC_VERSIONS) { const array episodes = {Episode::EP1, Episode::EP2, Episode::EP4}; for (Episode episode : episodes) { @@ -1592,7 +1592,7 @@ void ServerState::load_maps(bool from_non_event_thread) { auto variation_maxes = sdt->num_free_play_variations_for_floor(episode, mode == GameMode::SOLO, floor); for (size_t var_layout = 0; var_layout < variation_maxes.layout; var_layout++) { for (size_t var_entities = 0; var_entities < variation_maxes.entities; var_entities++) { - uint32_t supermap_key = this->supermap_key(episode, mode, difficulty, floor, var_layout, var_entities); + uint32_t free_play_key = this->free_play_key(episode, mode, difficulty, floor, var_layout, var_entities); auto objects_filename = sdt->map_filename_for_variation( episode, mode, floor, var_layout, var_entities, SDT::FilenameType::OBJECT_SETS); @@ -1611,13 +1611,13 @@ void ServerState::load_maps(bool from_non_event_thread) { (events_data ? phosg::fnv1a64(*events_data) : 0)); shared_ptr map_file; try { - map_file = map_file_for_source_hash.at(source_hash); + map_file = new_map_file_for_source_hash.at(source_hash); } catch (const out_of_range&) { map_file = make_shared(floor, objects_data, enemies_data, events_data); if (map_file->source_hash() != source_hash) { throw logic_error("incorrect source hash"); } - map_file_for_source_hash.emplace(map_file->source_hash(), map_file); + new_map_file_for_source_hash.emplace(map_file->source_hash(), map_file); } // Uncomment for debugging @@ -1629,7 +1629,7 @@ void ServerState::load_maps(bool from_non_event_thread) { // floor, // var_layout, // var_entities, - // supermap_key, + // free_play_key, // map_file->source_hash(), // objects_filename.empty() ? "(none)" : objects_filename.c_str(), // objects_data ? "present" : "missing", @@ -1641,7 +1641,7 @@ void ServerState::load_maps(bool from_non_event_thread) { // events_data ? "present" : "missing", // map_file->count_events()); - map_files[supermap_key].at(static_cast(v)) = map_file; + new_map_files_for_free_play_key[free_play_key].at(static_cast(v)) = map_file; } } } @@ -1651,74 +1651,86 @@ void ServerState::load_maps(bool from_non_event_thread) { } } - config_log.info("Constructing free play supermaps"); - - unordered_map> supermap_for_source_hash_sum; - unordered_map> new_supermaps; - for (const auto& it : map_files) { - uint64_t source_hash_sum = 0; - for (auto map_file : it.second) { - source_hash_sum += map_file ? map_file->source_hash() : 0; - } - - Episode episode = static_cast((it.first >> 28) & 7); - // Uncomment for debugging - // auto mode = static_cast((it.first >> 26) & 3); - // uint8_t difficulty = (it.first >> 24) & 3; - // uint8_t floor = (it.first >> 16) & 0xFF; - // uint8_t layout = (it.first >> 8) & 0xFF; - // uint8_t entities = (it.first >> 0) & 0xFF; - // fprintf(stderr, "SuperMap for %s %s %s %02hhX %02hhX %02hhX (%08" PRIX32 "): %016" PRIX64 " from", - // name_for_episode(episode), - // name_for_mode(mode), - // name_for_difficulty(difficulty), - // floor, - // layout, - // entities, - // it.first, - // source_hash_sum); - // for (const auto& map_file : it.second) { - // if (map_file) { - // fprintf(stderr, " %016" PRIX64, map_file->source_hash()); - // } else { - // fprintf(stderr, " ----------------"); - // } - // } - // fputc('\n', stderr); - - shared_ptr supermap; - try { - supermap = supermap_for_source_hash_sum.at(source_hash_sum); - static_game_data_log.info("Linking existing free play supermap %016" PRIX64 " for key %08" PRIX32, source_hash_sum, it.first); - } catch (const out_of_range&) { - supermap = make_shared(episode, it.second); - supermap_for_source_hash_sum.emplace(source_hash_sum, supermap); - static_game_data_log.info("Constructed free play supermap %016" PRIX64 " for key %08" PRIX32, source_hash_sum, it.first); - } - new_supermaps.emplace(it.first, supermap); - } - - auto set = [s = this->shared_from_this(), new_supermaps = std::move(new_supermaps)]() { - s->supermaps = std::move(new_supermaps); + auto set = [s = this->shared_from_this(), + new_map_file_for_source_hash = std::move(new_map_file_for_source_hash), + new_map_files_for_free_play_key = std::move(new_map_files_for_free_play_key)]() { + s->map_file_for_source_hash = std::move(new_map_file_for_source_hash); + s->map_files_for_free_play_key = std::move(new_map_files_for_free_play_key); + s->supermap_for_source_hash_sum.clear(); + s->supermap_for_free_play_key.clear(); }; this->forward_or_call(from_non_event_thread, std::move(set)); } +shared_ptr ServerState::get_free_play_supermap( + Episode episode, GameMode mode, uint8_t difficulty, uint8_t floor, uint32_t layout, uint32_t entities) { + uint32_t free_play_key = this->free_play_key(episode, mode, difficulty, floor, layout, entities); + try { + return this->supermap_for_free_play_key.at(free_play_key); + } catch (const out_of_range&) { + } + + const array, NUM_VERSIONS>* map_files; + try { + map_files = &this->map_files_for_free_play_key.at(free_play_key); + } catch (const out_of_range&) { + static_game_data_log.info("No maps exist for key %08" PRIX32 "; cannot construct supermap", free_play_key); + this->supermap_for_free_play_key.emplace(free_play_key, nullptr); + return nullptr; + } + + uint64_t source_hash_sum = 0; + for (auto map_file : *map_files) { + source_hash_sum += map_file ? map_file->source_hash() : 0; + } + + // Uncomment for debugging + // fprintf(stderr, "SuperMap for %s %s %s %02hhX %02hhX %02hhX (%08" PRIX32 "): %016" PRIX64 " from", + // name_for_episode(episode), + // name_for_mode(mode), + // name_for_difficulty(difficulty), + // floor, + // layout, + // entities, + // free_play_key, + // source_hash_sum); + // for (const auto& map_file : it.second) { + // if (map_file) { + // fprintf(stderr, " %016" PRIX64, map_file->source_hash()); + // } else { + // fprintf(stderr, " ----------------"); + // } + // } + // fputc('\n', stderr); + + shared_ptr supermap; + try { + supermap = this->supermap_for_source_hash_sum.at(source_hash_sum); + static_game_data_log.info("Linking existing free play supermap %016" PRIX64 " for key %08" PRIX32, source_hash_sum, free_play_key); + } catch (const out_of_range&) { + supermap = make_shared(episode, *map_files); + this->supermap_for_source_hash_sum.emplace(source_hash_sum, supermap); + static_game_data_log.info("Constructed free play supermap %016" PRIX64 " for key %08" PRIX32, source_hash_sum, free_play_key); + } + this->supermap_for_free_play_key.emplace(free_play_key, supermap); + return supermap; +} + vector> ServerState::supermaps_for_variations( Episode episode, GameMode mode, uint8_t difficulty, - const Variations& variations) const { + const Variations& variations) { vector> ret; for (size_t floor = 0; floor < 0x12; floor++) { Variations::Entry e; if (floor < variations.entries.size()) { e = variations.entries[floor]; } - ret.push_back(this->get_supermap(episode, mode, difficulty, floor, e.layout, e.entities)); + ret.push_back(this->get_free_play_supermap(episode, mode, difficulty, floor, e.layout, e.entities)); if (ret.back()) { static_game_data_log.info("Using supermap %08" PRIX32 " for floor %02zX layout %" PRIX32 " entities %" PRIX32, - this->supermap_key(episode, mode, difficulty, floor, e.layout, e.entities), + this->free_play_key(episode, mode, difficulty, floor, e.layout, e.entities), floor, e.layout.load(), e.entities.load()); } else { static_game_data_log.info("No supermap available for floor %02zX layout %" PRIX32 " entities %" PRIX32, diff --git a/src/ServerState.hh b/src/ServerState.hh index f0a2657f..a6b712b4 100644 --- a/src/ServerState.hh +++ b/src/ServerState.hh @@ -172,7 +172,10 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr function_code_index; std::shared_ptr pc_patch_file_index; std::shared_ptr bb_patch_file_index; - std::unordered_map> supermaps; // Keyed by supermap_key + std::unordered_map> map_file_for_source_hash; + std::map, NUM_VERSIONS>> map_files_for_free_play_key; + std::unordered_map> supermap_for_source_hash_sum; + std::unordered_map> supermap_for_free_play_key; std::shared_ptr bb_stream_files_cache; std::shared_ptr bb_system_cache; std::shared_ptr gba_files_cache; @@ -403,7 +406,7 @@ struct ServerState : public std::enable_shared_from_this { std::shared_ptr generate_patch_server_config(bool is_bb) const; void update_dependent_server_configs() const; - static constexpr uint32_t supermap_key( + static constexpr uint32_t free_play_key( Episode episode, GameMode mode, uint8_t difficulty, uint8_t floor, uint32_t layout, uint32_t entities) { return (static_cast(episode) << 28) | (static_cast(mode) << 26) | @@ -412,19 +415,13 @@ struct ServerState : public std::enable_shared_from_this { (static_cast(layout) << 8) | (static_cast(entities) << 0); } - inline std::shared_ptr get_supermap( - Episode episode, GameMode mode, uint8_t difficulty, uint8_t floor, uint32_t layout, uint32_t entities) const { - try { - return this->supermaps.at(this->supermap_key(episode, mode, difficulty, floor, layout, entities)); - } catch (const std::out_of_range&) { - return nullptr; - } - } + std::shared_ptr get_free_play_supermap( + Episode episode, GameMode mode, uint8_t difficulty, uint8_t floor, uint32_t layout, uint32_t entities); std::vector> supermaps_for_variations( Episode episode, GameMode mode, uint8_t difficulty, - const Variations& variations) const; + const Variations& variations); // The following functions may only be called from a non-event thread if they // take a from_non_event_thread argument; any function that does not have this