mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-24 15:33:50 +00:00
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
95 lines
3.5 KiB
C++
95 lines
3.5 KiB
C++
#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 60–200 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
|