Kelsidavis-WoWee/include/game/flat_field_map.hpp
Kelsi ef52065c71 perf(packets): replace std::map field storage with sorted flat vector
UpdateBlock::fields and Entity::fields were both std::map<uint16_t,
uint32_t>. Every UPDATE_OBJECT packet allocated one red-black-tree
node per parsed field (typically ~20 fields × dozens of blocks per
packet), trashing the allocator and producing pointer-chained
iteration on every consumer scan.

Introduce FlatFieldMap (include/game/flat_field_map.hpp): a sorted
std::vector<std::pair<uint16_t, uint32_t>> wrapper with the same
iterator-based API the existing 50+ call sites use — find(), end(),
operator[], range-for, ->first/->second on iterators. The parser
walks the bitmask low-to-high so append_sorted() is O(1) amortized;
Entity::setField falls back to insert_or_assign which is O(log N)
search + O(N) shift but N is small (~60 fields per entity).

Switched every signature on the path:
- UpdateBlock::fields, Entity::fields storage
- extractContainerFields / detectInventorySlotBases /
  applyInventoryFields / updateOtherPlayerVisibleItems
- detectPlayerMountChange / maybeDetectCoinageIndex /
  applyPlayerStatFields / extractPlayerAppearance
- extractSkillFields / extractExploredZoneFields /
  applyQuestStateFromFields
- lastPlayerFields_ snapshot
2026-05-14 13:06:22 -07:00

95 lines
3.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <cstdint>
#include <vector>
#include <utility>
#include <algorithm>
namespace wowee {
namespace game {
/**
* Sorted flat-vector field map shared by UpdateBlock and Entity.
*
* Replaces std::map<uint16_t, uint32_t> for both the per-packet UpdateBlock
* fields (built monotonically by the parser) and the persistent Entity
* fields (mutated by setField). For typical 60200 entries the flat layout
* is dramatically friendlier to the allocator and the cache than a
* red-black tree, while preserving the same iterator-based API the
* existing call sites already use.
*
* Iterator dereferences return std::pair<uint16_t, uint32_t> so existing
* code using `it->first` / `it->second` and range-based for over
* `[key, val]` continues to compile unchanged.
*/
class FlatFieldMap {
public:
using value_type = std::pair<uint16_t, uint32_t>;
using const_iterator = std::vector<value_type>::const_iterator;
using iterator = std::vector<value_type>::iterator;
bool empty() const noexcept { return data_.empty(); }
size_t size() const noexcept { return data_.size(); }
void clear() { data_.clear(); }
void reserve(size_t n) { data_.reserve(n); }
iterator begin() { return data_.begin(); }
iterator end() { return data_.end(); }
const_iterator begin() const { return data_.begin(); }
const_iterator end() const { return data_.end(); }
const_iterator cbegin() const { return data_.cbegin(); }
const_iterator cend() const { return data_.cend(); }
// Append at end. Caller must ensure key is strictly greater than the
// previously appended key — used by the UPDATE_OBJECT parser which
// walks the bitmask low-to-high.
void append_sorted(uint16_t key, uint32_t value) {
data_.emplace_back(key, value);
}
// Insert in sorted position, or overwrite the existing value.
void insert_or_assign(uint16_t key, uint32_t value) {
auto it = std::lower_bound(
data_.begin(), data_.end(), key,
[](const value_type& p, uint16_t k) { return p.first < k; });
if (it != data_.end() && it->first == key) {
it->second = value;
} else {
data_.insert(it, value_type(key, value));
}
}
// Map-style operator[] — used by Entity::setField. Returns a reference
// to the (existing or newly inserted) value.
uint32_t& operator[](uint16_t key) {
auto it = std::lower_bound(
data_.begin(), data_.end(), key,
[](const value_type& p, uint16_t k) { return p.first < k; });
if (it != data_.end() && it->first == key) return it->second;
return data_.insert(it, value_type(key, 0u))->second;
}
const value_type& back() const { return data_.back(); }
value_type& back() { return data_.back(); }
const_iterator find(uint16_t key) const {
auto it = std::lower_bound(
data_.begin(), data_.end(), key,
[](const value_type& p, uint16_t k) { return p.first < k; });
if (it != data_.end() && it->first == key) return it;
return data_.end();
}
iterator find(uint16_t key) {
auto it = std::lower_bound(
data_.begin(), data_.end(), key,
[](const value_type& p, uint16_t k) { return p.first < k; });
if (it != data_.end() && it->first == key) return it;
return data_.end();
}
private:
std::vector<value_type> data_;
};
} // namespace game
} // namespace wowee