From 775369345c365062a2774d4318d97674d76eb0dc Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Mon, 10 Feb 2025 23:41:59 -0800 Subject: [PATCH] use semantic hash index to fill in gaps in supermap --- src/Map.cc | 85 ++++++++++++++++++++++++++++++++++++------------------ src/Map.hh | 8 ++--- 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/src/Map.cc b/src/Map.cc index a7ae8466..bd114dd9 100644 --- a/src/Map.cc +++ b/src/Map.cc @@ -1015,7 +1015,7 @@ string MapFile::ObjectSetEntry::str() const { this->unused.load()); } -uint64_t MapFile::ObjectSetEntry::semantic_hash() const { +uint64_t MapFile::ObjectSetEntry::semantic_hash(uint8_t floor) const { uint64_t ret = phosg::fnv1a64(&this->base_type, sizeof(this->base_type)); ret = phosg::fnv1a64(&this->group, sizeof(this->group), ret); ret = phosg::fnv1a64(&this->room, sizeof(this->room), ret); @@ -1027,6 +1027,7 @@ uint64_t MapFile::ObjectSetEntry::semantic_hash() const { ret = phosg::fnv1a64(&this->param4, sizeof(this->param4), ret); ret = phosg::fnv1a64(&this->param5, sizeof(this->param5), ret); ret = phosg::fnv1a64(&this->param6, sizeof(this->param6), ret); + ret = phosg::fnv1a64(&floor, sizeof(floor), ret); return ret; } @@ -1059,7 +1060,7 @@ string MapFile::EnemySetEntry::str() const { this->unused.load()); } -uint64_t MapFile::EnemySetEntry::semantic_hash() const { +uint64_t MapFile::EnemySetEntry::semantic_hash(uint8_t floor) const { uint64_t ret = phosg::fnv1a64(&this->base_type, sizeof(this->base_type)); ret = phosg::fnv1a64(&this->num_children, sizeof(this->num_children), ret); ret = phosg::fnv1a64(&this->room, sizeof(this->room), ret); @@ -1074,6 +1075,7 @@ uint64_t MapFile::EnemySetEntry::semantic_hash() const { ret = phosg::fnv1a64(&this->fparam5, sizeof(this->fparam5), ret); ret = phosg::fnv1a64(&this->uparam1, sizeof(this->uparam1), ret); ret = phosg::fnv1a64(&this->uparam2, sizeof(this->uparam2), ret); + ret = phosg::fnv1a64(&floor, sizeof(floor), ret); return ret; } @@ -1088,10 +1090,11 @@ string MapFile::Event1Entry::str() const { this->action_stream_offset.load()); } -uint64_t MapFile::Event1Entry::semantic_hash() const { +uint64_t MapFile::Event1Entry::semantic_hash(uint8_t floor) const { uint64_t ret = phosg::fnv1a64(&this->event_id, sizeof(this->event_id)); ret = phosg::fnv1a64(&this->room, sizeof(this->room), ret); ret = phosg::fnv1a64(&this->wave_number, sizeof(this->wave_number), ret); + ret = phosg::fnv1a64(&floor, sizeof(floor), ret); return ret; } @@ -1803,7 +1806,7 @@ string SuperMap::Event::str() const { const auto& def = this->version(v); if (def.relative_event_index != 0xFFFF) { string action_stream_str = phosg::format_data_string(def.action_stream, def.action_stream_size); - string args_str = def.entry->str(); + string args_str = def.set_entry->str(); ret += phosg::string_printf( " %s:[%04hX => %s+%s]", phosg::name_for_enum(v), @@ -1877,7 +1880,7 @@ void SuperMap::link_object_version(std::shared_ptr obj, Version version, entities.objects.emplace_back(obj); // Add to semantic hash index - uint64_t semantic_hash = set_entry->semantic_hash(); + uint64_t semantic_hash = set_entry->semantic_hash(obj->floor); this->objects_for_semantic_hash[semantic_hash].emplace_back(obj); // Add to room/group index @@ -2398,7 +2401,7 @@ void SuperMap::link_enemy_version_and_children( entities.enemy_sets.emplace_back(ene); // Add to semantic hash index (but only for the root ene) - uint64_t semantic_hash = set_entry->semantic_hash(); + uint64_t semantic_hash = set_entry->semantic_hash(ene->floor); this->enemy_sets_for_semantic_hash[semantic_hash].emplace_back(ene); } @@ -2480,10 +2483,10 @@ void SuperMap::link_event_version( auto& entities = this->version(version); auto& ev_ver = ev->version(version); - if (ev_ver.entry) { + if (ev_ver.set_entry) { throw logic_error("event already linked to version"); } - ev_ver.entry = entry; + ev_ver.set_entry = entry; ev_ver.relative_event_index = entities.events.size(); ev_ver.action_stream = ev_action_stream_start; ev_ver.action_stream_size = ev_action_stream_size; @@ -2491,7 +2494,7 @@ void SuperMap::link_event_version( entities.events.emplace_back(ev); // Add to semantic hash index - uint64_t semantic_hash = entry->semantic_hash(); + uint64_t semantic_hash = entry->semantic_hash(ev->floor); this->events_for_semantic_hash[semantic_hash].emplace_back(ev); // Add to room index @@ -2729,12 +2732,10 @@ static double event_delete_cost(const MapFile::Event1Entry&) { return 1.0; } static double event_edit_cost(const MapFile::Event1Entry& prev, const MapFile::Event1Entry& current) { - // Unlike object and enemy sets, event matching is essentially binary: no - // variance is tolerated in some parameters, but others are entirely ignored. - bool is_same = ((prev.event_id == current.event_id) && - (prev.room == current.room) && - (prev.wave_number == current.wave_number)); - return is_same ? 0.0 : 5.0; + // Unlike objects and enemies, event matching is essentially binary + return ((prev.event_id != current.event_id) || (prev.room != current.room) || (prev.wave_number != current.wave_number)) + ? 5.0 + : 0.0; } void SuperMap::add_map_file(Version this_v, shared_ptr this_map_file) { @@ -2772,10 +2773,11 @@ void SuperMap::add_map_file(Version this_v, shared_ptr this_map_f double (*add_cost)(const EntryT&), double (*delete_cost)(const EntryT&), double (*edit_cost)(const EntryT&, const EntryT& current), - const vector& prev_entities, + const vector>& prev_entities, size_t prev_entities_start_index, auto&& link_existing, - auto&& add_new) { + auto&& add_new, + const unordered_map>>& semantic_hash_index) { auto edit_path = compute_edit_path( prev_sets, prev_set_count, this_sets, this_set_count, add_cost, delete_cost, edit_cost); @@ -2783,10 +2785,28 @@ void SuperMap::add_map_file(Version this_v, shared_ptr this_map_f if (used_prev_entities.size() != this_set_count) { throw std::logic_error("incorrect previous entity list length"); } + unordered_set used_prev_entities_set; + for (const auto& ent : used_prev_entities) { + used_prev_entities_set.emplace(ent.get()); + } - // TODO; // Use semantic hash index to fill in the gaps + // Fill in all entities found by edit distance for (size_t z = 0; z < this_set_count; z++) { auto& prev_ent = used_prev_entities[z]; + + // Use the semantic hash index to fill in gaps if possible + if (!prev_ent) { + try { + for (const auto& ent : semantic_hash_index.at(this_sets[z].semantic_hash(floor))) { + if (!ent->version(this_v).set_entry && !used_prev_entities_set.count(ent.get())) { + prev_ent = ent; + break; + } + } + } catch (const out_of_range&) { + } + } + if (prev_ent) { link_existing(prev_ent, this_v, this_sets + z); } else { @@ -2824,7 +2844,8 @@ void SuperMap::add_map_file(Version this_v, shared_ptr this_map_f prev_entities.objects, prev_entities.object_floor_start_indexes.at(floor), bind(&SuperMap::link_object_version, this, placeholders::_1, placeholders::_2, placeholders::_3), - bind(&SuperMap::add_object, this, placeholders::_1, placeholders::_2, placeholders::_3)); + bind(&SuperMap::add_object, this, placeholders::_1, placeholders::_2, placeholders::_3), + this->objects_for_semantic_hash); } if (!prev_map_file || !prev_map_file->floor(floor).enemy_sets) { @@ -2846,7 +2867,8 @@ void SuperMap::add_map_file(Version this_v, shared_ptr this_map_f prev_entities.enemy_sets, prev_entities.enemy_set_floor_start_indexes.at(floor), bind(&SuperMap::link_enemy_version_and_children, this, placeholders::_1, placeholders::_2, placeholders::_3), - bind(&SuperMap::add_enemy_and_children, this, placeholders::_1, placeholders::_2, placeholders::_3)); + bind(&SuperMap::add_enemy_and_children, this, placeholders::_1, placeholders::_2, placeholders::_3), + this->enemy_sets_for_semantic_hash); } if (!prev_map_file || !prev_map_file->floor(floor).events1) { @@ -2868,7 +2890,8 @@ void SuperMap::add_map_file(Version this_v, shared_ptr this_map_f prev_entities.events, prev_entities.event_floor_start_indexes.at(floor), bind(&SuperMap::link_event_version, this, placeholders::_1, placeholders::_2, placeholders::_3, this_sf.event_action_stream, this_sf.event_action_stream_bytes), - bind(&SuperMap::add_event, this, placeholders::_1, placeholders::_2, placeholders::_3, this_sf.event_action_stream, this_sf.event_action_stream_bytes)); + bind(&SuperMap::add_event, this, placeholders::_1, placeholders::_2, placeholders::_3, this_sf.event_action_stream, this_sf.event_action_stream_bytes), + this->events_for_semantic_hash); } } } @@ -2990,14 +3013,20 @@ SuperMap::EfficiencyStats& SuperMap::EfficiencyStats::operator+=(const Efficienc } std::string SuperMap::EfficiencyStats::str() const { + double object_eff = this->total_object_slots + ? (static_cast(this->filled_object_slots * 100) / static_cast(this->total_object_slots)) + : 0; + double enemy_set_eff = this->total_enemy_set_slots + ? (static_cast(this->filled_enemy_set_slots * 100) / static_cast(this->total_enemy_set_slots)) + : 0; + double event_eff = this->total_event_slots + ? (static_cast(this->filled_event_slots * 100) / static_cast(this->total_event_slots)) + : 0; return phosg::string_printf( "EfficiencyStats[K = %zu/%zu (%lg%%), E = %zu/%zu (%lg%%), W = %zu/%zu (%g%%)]", - this->filled_object_slots, this->total_object_slots, - static_cast(this->filled_object_slots * 100) / static_cast(this->total_object_slots), - this->filled_enemy_set_slots, this->total_enemy_set_slots, - static_cast(this->filled_enemy_set_slots * 100) / static_cast(this->total_enemy_set_slots), - this->filled_event_slots, this->total_event_slots, - static_cast(this->filled_event_slots * 100) / static_cast(this->total_event_slots)); + this->filled_object_slots, this->total_object_slots, object_eff, + this->filled_enemy_set_slots, this->total_enemy_set_slots, enemy_set_eff, + this->filled_event_slots, this->total_event_slots, event_eff); } SuperMap::EfficiencyStats SuperMap::efficiency() const { @@ -3181,7 +3210,7 @@ void SuperMap::verify() const { } } const auto& ev_ver = ev->version(v); - if (!ev_ver.entry) { + if (!ev_ver.set_entry) { throw logic_error("event entry is missing"); } if (ev_ver.relative_event_index != event_index) { diff --git a/src/Map.hh b/src/Map.hh index 8daa11ca..30393615 100644 --- a/src/Map.hh +++ b/src/Map.hh @@ -184,7 +184,7 @@ public: /* 40 */ le_uint32_t unused = 0; // Reserved for pointer in client's memory; unused by server /* 44 */ - uint64_t semantic_hash() const; + uint64_t semantic_hash(uint8_t floor) const; std::string str() const; } __packed_ws__(ObjectSetEntry, 0x44); @@ -211,7 +211,7 @@ public: /* 44 */ le_uint32_t unused = 0; // Reserved for pointer in client's memory; unused by server /* 48 */ - uint64_t semantic_hash() const; + uint64_t semantic_hash(uint8_t floor) const; std::string str() const; } __packed_ws__(EnemySetEntry, 0x48); @@ -241,7 +241,7 @@ public: /* 10 */ le_uint32_t action_stream_offset = 0; /* 14 */ - uint64_t semantic_hash() const; + uint64_t semantic_hash(uint8_t floor) const; std::string str() const; } __packed_ws__(Event1Entry, 0x14); @@ -503,7 +503,7 @@ public: struct Event { struct EventVersion { - const MapFile::Event1Entry* entry = nullptr; + const MapFile::Event1Entry* set_entry = nullptr; uint16_t relative_event_index = 0xFFFF; const void* action_stream = nullptr; size_t action_stream_size = 0;