Commit graph

3 commits

Author SHA1 Message Date
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
Kelsi
961af04b36 fix: gossip banker sent CMSG_BANKER_ACTIVATE twice; deduplicate quest icons
Icon==6 and text=="GOSSIP_OPTION_BANKER" both sent BANKER_ACTIVATE
independently. Banking NPCs match both, so the packet was sent twice —
some servers toggle the bank window open then closed. Added sentBanker
guard so only one packet is sent.

Also extracts classifyGossipQuests() from two identical 30-line blocks
in handleGossipMessage and handleQuestgiverQuestList. The icon→status
mapping (5/6/10=completable, 3/4=incomplete, 2/7/8=available) is now
in one place with a why-comment explaining these are protocol-defined.
2026-03-29 18:53:30 -07:00
Paul
b2710258dc refactor(game): split GameHandler into domain handlers
Extract domain-specific logic from the monolithic GameHandler into
dedicated handler classes, each owning its own opcode registration,
state, and packet parsing:

- CombatHandler: combat, XP, kill, PvP, loot roll (~26 methods)
- SpellHandler: spells, auras, pet stable, talent (~3+ methods)
- SocialHandler: friends, guild, groups, BG, RAF, PvP AFK (~14+ methods)
- ChatHandler: chat messages, channels, GM tickets, server messages,
               defense/area-trigger messages (~7+ methods)
- InventoryHandler: items, trade, loot, mail, vendor, equipment sets,
                    read item (~3+ methods)
- QuestHandler: gossip, quests, completed quest response (~5+ methods)
- MovementHandler: movement, follow, transport (~2 methods)
- WardenHandler: Warden anti-cheat module

Each handler registers its own dispatch table entries via
registerOpcodes(DispatchTable&), called from
GameHandler::registerOpcodeHandlers(). GameHandler retains core
orchestration: auth/session handshake, update-object parsing,
opcode routing, and cross-handler coordination.

game_handler.cpp reduced from ~10,188 to ~9,432 lines.

Also add a POST_BUILD CMake step to symlink Data/ next to the
executable so expansion profiles and opcode tables are found at
runtime when running from build/bin/.
2026-03-28 09:42:37 +03:00