You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Item cache: hook response handler instead of racing the engine
`Item:CreateFromItemID(X):ContinueOnItemLoad(cb)` was leaving items
permanently uncached when pfQuest called it at addon-load /
PLAYER_ENTERING_WORLD against an empty WDB. Three intertwined bugs:
1. `GetItemInfoInstant` returned no values for uncached items, so
`ItemMixin:GetItemID()` returned nil and `AddCallback(nil, cb)`
crashed with "table index is nil".
2. `DoesItemExistByID` returned false for uncached items, which made
`IsItemEmpty()` return true and erroneously gated `ContinueOnItemLoad`
on cache state — modern semantic is "is this a valid ID", independent
of cache.
3. Our `RequestLoadItemData` path eagerly called `CacheFetch` with our
own callback at addon-load. That created a cache entry before the
engine's natural inventory prefetch had a chance to run; the engine
then saw the entry already existed and skipped its own SMSG, leaving
the entry permanently pending. `GetItemInfo` retries didn't help —
subsequent `_GetRecord` calls just appended callbacks to the dead
list, no new query was sent.
Fixes:
- `GetItemInfoInstant` always pushes the itemID (echoed from input) and
the remaining 6 fields when cached / nils when not. No cache touch.
- `DoesItemExistByID` returns `itemID > 0`. No cache touch.
- `RequestLoadItemData` tracks itemIDs in a pending set without touching
the cache. We hook `FUN_ITEMSTATS_CACHE_RESPONSE` (the engine's
`SMSG_ITEM_QUERY_SINGLE_RESPONSE` handler at `0x0055BDB0`) and sweep
pending after the engine fills entries — observing the natural
prefetch instead of racing it. `WorldTick` only handles escalation
(after ~60 ticks, kick a query for items the engine doesn't prefetch
itself, e.g. quest rewards) and the hard timeout.
Also fix the truthiness bug in modern (id, success) events:
`Event::Custom::Fire("%d%d", id, success)` was pushing `success` as a
Lua number — both 0 and 1 are truthy, so `if success then` fired the
success branch on failed loads. New `Event::Custom::FireIdSuccess`
pushes `1` for success and `nil` for failure (via the engine's
`lua_pushstring(NULL) → lua_pushnil` tail-jump). `ITEM_DATA_LOAD_RESULT`,
`GET_ITEM_INFO_RECEIVED`, and `QUEST_DATA_LOAD_RESULT` now all use this.
0 commit comments