Every custom-event fire resolved its name to a slot via
Event::Custom::Lookup - a linear strcmp scan over the ~46-entry
reservation table, on every single fire. The slot itself was already
cached per table build; only the name->reservation hop was repeated.
On a client that is purely CPU-bound, that is wasted work on paths
that burst exactly when frames are scarce (nameplate churn, cast spam,
loot rolls, mouse/modifier input).
AutoReserve now remembers its reservation index (the ctor's dedup
resolves duplicate names to the same entry) and exposes Slot() - two
loads. Reload safety is unchanged and stays centralized: Slot() reads
through the shared table PrepareForReload/RetryClaims maintain, so the
value can never go stale. Lookup is deleted; LookupByName remains for
engine-defined names.
All fire sites converted to the instance handle:
- CastEvents: the k* name constants become the AutoReserve instances,
so all 19 fire sites stay textually identical; the Fire/FireReticle/
FireRemote helpers take the reservation by reference.
- equipmentset: reservations move into Set.h as C++17 inline consts so
the Data and Api TUs share one handle.
- Cache::QueryLoad::Register takes the AutoReserve pointer instead of
an event-name string (creature/gameobject callers updated).
- Tts keeps its lazy VanillaTTS-stand-down reservations via
null-guarded pointers set at registration.
- GlobalMouse/InputEvents/lossofcontrol instances moved above their
fire sites (static-init list order is irrelevant).
The repo was internally inconsistent: the LICENSE file is GPL v3 but every
src/ header carried the LGPL v3 notice and the README said "LGPL v3 or
later". LGPL was also the wrong fit — this is an injected mod plus a
bundled addon, not a library other code links against, so LGPL's linking
exception buys nothing.
It's also required: src/voicechat/Tts.cpp is ported from awesome_wotlk's
VoiceChat.cpp, which is GPL v3, and a derivative of GPL code cannot be
relicensed under more permissive terms. Dependencies stay compatible —
MinHook (BSD-2) and the nampower-mirrored packet shapes (BSD) are
GPL-compatible, and SAPI is covered by the system-library exception.
Swept "GNU Lesser General Public License" -> "GNU General Public License"
across all 257 src/ headers and changed the README license line to match
the GPL v3 LICENSE file. Comment-only; no build impact.
GameObject analog of C_CreatureInfo, reading the gameobject cache
(gameobjectcache.wdb, SMSG_GAMEOBJECT_QUERY_RESPONSE):
- GetGameObjectInfoByID -> { gameObjectID, name, type, displayID } | nil
(synchronous peek)
- RequestLoadGameObjectByID -> bool, fires GAMEOBJECT_DATA_LOAD_RESULT
when an uncached GO arrives.
The GO cache is a sibling class of the creature cache: same hash-map
shape but a different vtable and a larger entry (loaded flag at
entry+0x98 vs +0x50), so it has its own _GetRecord (FUN_00558560) and
response parser (FUN_005588E0). Generalized Cache::QueryLoad to take a
per-cache _GetRecord via Register and to hook BOTH parsers (creature +
gameobject), dispatching completion by cache instance. Renamed the
creature offsets FUN_CACHE_* -> FUN_CREATURE_* now that "generic" was a
misnomer.
GO field offsets: name@+0x08 verified (FUN_0062dc10 reads [block+0x08]),
type@+0x00 / displayID@+0x04 from the wire/WDB order. Verified in-game:
Mesa Elevator 47297 type 11 displayID 360, Windrunner 176250 type 15
displayID 7087, and RequestLoadGameObjectByID populated an uncached GO
("Lighting Impact Large", type 5). Creature path re-verified after the
refactor (GetCreatureInfoByID + the load event still work).
RequestLoadCreatureByID asynchronously fetches an uncached creature:
issues SMSG_CREATURE_QUERY and fires CREATURE_DATA_LOAD_RESULT(id,
success) when the response lands (or on timeout); fires success
synchronously on a cache hit. Mirrors C_Item.RequestLoadItemData.
New Cache::QueryLoad dispatcher generalizes Item::Data's
pending/timeout/event machinery so multiple query caches share one
response-handler hook. MinHook allows only one hook per target, and
the creature/gameobject (and other non-item/quest) caches all route
their SMSG_*_QUERY_RESPONSE through the generic parser FUN_00556E20 —
so we hook it once and dispatch by cache instance to the right pending
set + completion event. Item/quest use their own parsers, so no
collision. The gameobject cache will register on this same dispatcher.
Verified in-game: RequestLoadCreatureByID(10184) on an uncached Onyxia
returned true, fired CREATURE_DATA_LOAD_RESULT, and populated the cache
(type 2 Dragonkin, rank 3 world boss, displayID 8570).