Commit graph

1325 commits

Author SHA1 Message Date
Kelsi
6763cfcda0 game: fix log level for GameObject CREATE messages (WARNING → DEBUG)
Every GameObject CREATE block was logged at WARNING level, spamming the
warning log with doors, chests, and other world objects. Demote to DEBUG
since this is routine spawn traffic; keep transport detection at INFO since
those are noteworthy.
2026-03-10 09:00:17 -07:00
Kelsi
4f51103cdb game: clear permanent-failure and dead-creature caches on same-map reconnect
After reconnect, `creaturePermanentFailureGuids_` and `deadCreatureGuids_`
could retain stale entries for GUIDs not tracked in `creatureInstances_`
(creatures that failed to load or died before being spawned).  These stale
entries would silently block re-spawning or cause wrong death state on the
fresh CREATE_OBJECTs the server sends after reconnect.

Clear both caches in the reconnect-to-same-map path so server state is
authoritative after every reconnect.
2026-03-10 08:55:23 -07:00
Kelsi
a06ac018ea game: use targeted entity cleanup on reconnect to same map, preserving terrain
The previous reconnect fix caused loadOnlineWorldTerrain to run, which
cleared and reloaded all terrain tiles — unnecessarily heavy for a
reconnect where the map hasn't changed.

New path: when isInitialEntry=true and mapId==loadedMapId_, despawn all
tracked creature/player/GO instances from the renderer (proper cleanup),
clear all pending spawn queues, update player position, and return — the
terrain stays loaded and the server's fresh CREATE_OBJECTs repopulate
entities normally.
2026-03-10 08:50:25 -07:00
Kelsi
d22f4b30ac game: process partial UPDATE_OBJECT packets when a block parse fails
Previously, if any single block in an SMSG_UPDATE_OBJECT packet failed
to parse (e.g. unusual spline flags), the entire packet was dropped and
all entities in it were lost. On busy zones with many CREATE_OBJECTs in
one packet, one bad NPC movement block would silently suppress all NPCs
that followed it in the same packet.

- parseUpdateObject: break instead of return false on block failure,
  so already-parsed blocks are returned to the caller
- handleUpdateObject: fall through to process partial data when parsing
  returns false but some blocks were successfully parsed
2026-03-10 08:38:39 -07:00
Kelsi
54246345bb game: fix NPCs not spawning on reconnect to same map
On disconnect/reconnect to the same map, entityManager was not cleared
and creatureInstances_ still held old entries from the previous session.
When the server re-sent CREATE_OBJECT for the same GUIDs, the spawn
callback's early-return guard (creatureInstances_.count(guid)) silently
dropped every NPC re-spawn, leaving the world empty.

Fixes:
- disconnect() now calls entityManager.clear() to purge stale entities
- WorldEntryCallback gains a bool isInitialEntry parameter (true on first
  login or reconnect, false on in-world teleport/flight landing)
- Same-map optimization path skipped when isInitialEntry=true, so
  loadOnlineWorldTerrain runs its full cleanup and properly despawns old
  creature/player instances before the server refreshes them
2026-03-10 08:35:36 -07:00
Kelsi
baab997da8 ui: fix XP bar overlapping second action bar by positioning above both bars 2026-03-10 08:28:48 -07:00
Kelsi
0a157d3255 game: add area name cache from WorldMapArea.dbc for /who zone display and exploration messages
- Load WorldMapArea.dbc lazily on first use to build areaId→name lookup
- /who results now show [Zone Name] alongside level: 'Name - Level 70 [Stormwind City]'
- SMSG_EXPLORATION_EXPERIENCE now shows 'Discovered Elwynn Forest! Gained X experience.'
  instead of generic 'Discovered new area!' message when the zone name is available
- Cache is populated once per session and shared across both callsites
2026-03-10 08:06:21 -07:00
Kelsi
846ba58d2e ui,game: show creature names in quest kill count tracker and progress messages
Quest kill count tracker in the HUD now resolves creature names from the
cached creature query results and displays them as "Name: x/y" instead
of bare "x/y". The system chat progress message on kill also includes
the creature name when available, matching retail client behavior.
2026-03-10 07:45:53 -07:00
Kelsi
03c4d59592 game: fix talent state not resetting across login sessions
Replace static-local firstSpecReceived with talentsInitialized_ member
variable, reset in handleLoginVerifyWorld alongside other per-session
state. Also clear learnedTalents_, unspentTalentPoints_, and
activeTalentSpec_ at world entry so reconnects and character switches
start from a clean talent state instead of carrying over stale data.
2026-03-10 07:41:27 -07:00
Kelsi
2a9d26e1ea game,ui: add rest state tracking and rested XP bar overlay
- Track PLAYER_REST_STATE_EXPERIENCE update field for all expansions
  (WotLK=636, Classic=718, TBC=928, Turtle=718)
- Set isResting_ flag from SMSG_SET_REST_START packet
- XP bar shows rested bonus as a lighter purple overlay extending
  beyond the current fill to (currentXp + restedXp) position
- Tooltip text changes to "%u / %u XP  (+%u rested)" when bonus exists
- "zzz" indicator shown at bar right edge while resting
2026-03-10 07:35:30 -07:00
Kelsi
0ea8e55ad4 ui,game,pipeline: player nameplates always-on, level-up ring effect, vanilla tile fallback, warden null guard
- Nameplates: player names always rendered regardless of V-key toggle;
  separate cull distance 40u (players/target) vs 20u (NPCs); cyan name
  color for other players; fade alpha scales with cull distance
- Level-up: add expanding golden ring burst (3 staggered waves, 420u
  max radius) + full-screen flash to renderDingEffect(); M2 LevelUp.m2
  is still attempted as a bonus on top
- Vanilla tile loading: add AssetManager::setBaseFallbackPath() so that
  when the primary manifest is an expansion-specific DBC-only subset
  (e.g. Data/expansions/vanilla/), world terrain files fall back to
  the base Data/ extraction; wired in Application::initialize()
- Warden: map a null guard page at address 0x0 in the Unicorn emulator
  so NULL-pointer reads in the module don't crash with UC_ERR_MAP;
  execution continues past the NULL read for better diagnostics
2026-03-10 07:25:04 -07:00
Kelsi
3cdaf78369 game,warden,assets: fix unknown player names, warden heap overlap, and Vanilla Item.dbc
- game: clear pendingNameQueries on player out-of-range and DESTROY_OBJECT so
  re-entering players get a fresh name query instead of being silently skipped
- game: add 5s periodic name resync scan that re-queries players with empty names
  and no pending query, recovering from dropped CMSG_NAME_QUERY responses
- warden: fix UC_ERR_MAP by moving HEAP_BASE from 0x200000 to 0x20000000; the old
  heap [0x200000, 0x1200000) overlapped the module at 0x400000, causing Unicorn to
  reject the heap mapping and abort emulator initialisation
- warden: add early overlap check between module and heap regions to catch future
  layout bugs at init time
- assets: add loadDBCOptional() which logs at DEBUG level when a DBC is absent,
  for files that are not distributed on all expansions
- assets: use loadDBCOptional for Item.dbc (absent on Vanilla 1.12 clients) and
  fall back to server-sent itemInfoCache displayInfoId for NPC weapon resolution
2026-03-10 07:00:43 -07:00
Kelsi
dc2aab5e90 perf: limit NPC composite texture processing to 2ms per frame
processAsyncNpcCompositeResults() had no per-frame budget cap, so when
many NPCs finished async skin compositing simultaneously (e.g. right
after world load), all results were finalized in a single frame causing
up to 284ms frame stalls. Apply the same 2ms budget pattern used by
processAsyncCreatureResults. Load screen still processes all pending
composites without the cap (unlimited=true).
2026-03-10 06:47:33 -07:00
Kelsi
ac0fe1bd61 game: clear target auras when switching targets
setTarget() was not clearing targetAuras, leaving stale buff/debuff
icons from the previous target visible on the buff bar until the server
sent SMSG_AURA_UPDATE_ALL for the new target. Reset all slots to empty
on target change so the display is immediately correct.
2026-03-10 06:43:16 -07:00
Kelsi
1e53369869 game: fix player phantom model on SMSG_DESTROY_OBJECT
handleDestroyObject invoked creatureDespawnCallback_ and
gameObjectDespawnCallback_ but not playerDespawnCallback_ for PLAYER
entities. This caused the CharacterRenderer instance for nearby players
to remain alive after they received a DESTROY_OBJECT packet (e.g. when
they teleported or went out of range via server-forced despawn), leaving
phantom models in the world.

Mirror the same despawn logic used for out-of-range removal: call
playerDespawnCallback_ and clean up the per-player bookkeeping maps so
the renderer cleans up the character instance correctly.
2026-03-10 06:40:07 -07:00
Kelsi
ea9c7e68e7 rendering,ui: sync selection circle to renderer instance position
The selection circle was positioned using the entity's game-logic
interpolator (entity->getX/Y/Z), while the actual M2 model is
positioned by CharacterRenderer's independent interpolator (moveInstanceTo).
These two systems can drift apart during movement, causing the circle
to appear under the wrong position relative to the visible model.

Fix: add CharacterRenderer::getInstancePosition / Application::getRenderPositionForGuid
and use the renderer's inst.position for XY (with footZ override for Z)
so the circle always tracks the rendered model exactly. Falls back to
the entity game-logic position when no CharacterRenderer instance exists.
2026-03-10 06:33:44 -07:00
Kelsi
463e3f5ed1 game: fix party frame duplication and player name on entity re-creation
SMSG_GROUP_LIST is a full replacement packet, not a delta. handleGroupList()
was not resetting partyData before parsing, so repeated GROUP_LIST packets
pushed duplicate members onto the existing vector — a 2-player party would
show the same name 5 times if the packet was sent 5 times.

Fix: reset partyData = GroupListData{} before calling GroupListParser::parse().

Also fix player names staying "Unknown" when an entity moves out of range and
comes back: queryPlayerName() now applies the cached name to the new entity
object immediately instead of skipping when the name is already in cache.
This was causing other players' names to appear as unknown after zoning or
crossing render distance boundaries.
2026-03-10 06:21:05 -07:00
Kelsi
a2dd8ee5b5 game,ui: implement MSG_RAID_TARGET_UPDATE and display raid marks
Parse the full and single-update variants of MSG_RAID_TARGET_UPDATE to
track which guid carries each of the 8 raid icons (Star/Circle/Diamond/
Triangle/Moon/Square/Cross/Skull). Marks are cleared on world transfer.

The target frame now shows the Unicode symbol for the target's raid mark
in its faction color to the left of the name. Nameplates show the same
symbol to the left of the unit name for all nearby marked units.
2026-03-10 06:10:29 -07:00
Kelsi
90b8cccac5 ui,game: add second action bar (Shift+1-12 keybinds, slots 12-23)
Expand action bar from 12 to 24 slots (2 bars × 12). Bar 2 is rendered
above bar 1 and loaded from SMSG_ACTION_BUTTONS slots 12-23. Pressing
Shift+number activates the corresponding bar-2 slot. Drag-and-drop,
cooldown overlays, and tooltips work identically on both bars. Bar 2
fades slightly when all its slots are empty to minimize visual noise.
2026-03-10 06:04:43 -07:00
Kelsi
753790ae47 ui: show persistent zone name above minimap
Draws the current zone name centered above the minimap circle using a
gold-colored 12pt label with drop shadow. This gives players a constant
location reference without needing to trigger the full-screen zone flash.
Uses getForegroundDrawList so it renders above the minimap texture.
2026-03-10 05:52:55 -07:00
Kelsi
6f7363fbcb ui: click-to-target via nameplate hit testing
Left-clicking anywhere within a nameplate's bounding box (name text +
health bar) calls setTarget() for that unit. Uses manual mouse position
hit testing since nameplates are drawn on the background DrawList rather
than as ImGui widgets. Click is ignored when ImGui has captured the mouse
(e.g. when a window is open).
2026-03-10 05:50:26 -07:00
Kelsi
3fce3adb39 game: keep contacts_ in sync with SMSG_FRIEND_STATUS updates
When a friend goes online, offline, or is added/removed, update the
contacts_ vector in addition to friendsCache. This ensures the Friends
tab in the Social window always reflects the current state without
needing a full SMSG_CONTACT_LIST/SMSG_FRIEND_LIST refresh.
2026-03-10 05:48:37 -07:00
Kelsi
7cd8e86d3b game,ui: add ContactEntry struct and Friends tab in social frame
Store structured friend data (online status, level, area, class) that
was previously discarded in handleFriendList/handleContactList. New
ContactEntry struct lives in game_handler.hpp; getContacts() exposes it.

UI: the O-key Social window (formerly guild-only) now has a Friends tab.
- Shows online/offline status dot, name, level, and AFK/DND label
- Pressing O when not in a guild opens Social directly on the Friends tab
- The window title changed from "Guild" to "Social" for accuracy
- Non-guild players no longer get a "not in a guild" rejection on O press
2026-03-10 05:46:03 -07:00
Kelsi
f98cc32947 docs: update Discord invite link 2026-03-10 05:40:53 -07:00
Kelsi
bbf0c0b22c ui: show nameplates for all nearby units, not just target
The V toggle previously only rendered a nameplate for the currently
targeted unit. Now all nearby units get a nameplate when nameplates are
enabled, matching WoW's native behaviour:

- Target: nameplate shown up to 40 units, gold border highlight
- All other units: shown up to 20 units, dark border (no highlight)
- Fade-out range, hostility colour, level label, and health bar logic
  are all unchanged — only the per-entity distance culling changes
2026-03-10 05:38:52 -07:00
Kelsi
65c4bd1a17 ui: WoW-style clock-sweep cooldown overlay on action bar
Replace the plain yellow text cooldown overlay with a proper clock-sweep:
- Dark fan spanning the elapsed fraction of the cooldown, sweeping
  clockwise from 12 o'clock (matches WoW's native cooldown look)
- White remaining-time text with drop-shadow centered on the icon
- Minutes shown as "Xm" for cooldowns >= 60s, seconds otherwise
- Fan radius set to 1.5× the icon half-width to cover corners on the
  square icon; works for both icon and empty (label-only) slots
2026-03-10 05:36:59 -07:00
Kelsi
983c64720d ui: show party member dots on minimap
Draw a dot for each online party member that has reported a position via
SMSG_PARTY_MEMBER_STATS. Leader gets a gold dot, others get blue. A
white outline ring is drawn around each dot, and hovering over it shows
the member's name as a tooltip. Out-of-range members are silently
skipped by the existing projectToMinimap clamp logic.

Axis mapping follows the same convention as minimap pings: server posX
(east/west) → canonical Y, server posY (north/south) → canonical X.
2026-03-10 05:33:21 -07:00
Kelsi
962c640ff5 ui: add raid frames, quest log scroll-to-quest, and quest tracking polish
Raid frames:
- When groupType=1 (raid), render compact grid-style raid frames instead
  of the vertical party list that would overflow for 25/40-man groups
- Members organized by subgroup (G1-G8), up to 5 rows per subgroup column
- Each cell shows: name, health bar (green/yellow/red), power bar (class color)
- Clicking a cell targets the member; border highlight for current target
- Frames anchored above action bar area, centered horizontally

Quest log scroll-to-quest:
- openAndSelectQuest(questId) selects the quest AND scrolls the list pane
  to show it (SetScrollHereY on the first render frame after open)
- One-shot scroll: scrollToSelected_ cleared after first use so normal
  scroll behavior is unaffected afterward

Quest tracker:
- Clicking a tracked quest now calls openAndSelectQuest() — opens the log
  AND jumps to that specific quest rather than just opening to top
2026-03-10 05:26:16 -07:00
Kelsi
fb80b125bd Fix post-hearthstone asset gaps and add quest tracker interactivity
Hearthstone post-teleport fix:
- Expand same-map hearthstone precache from 5x5 to 9x9 tiles so workers
  have more tiles parsed before the player arrives at the bind point
- After same-map teleport arrival, enqueue the full load-radius tile grid
  (17x17 = 289 tiles) at the new position so background workers immediately
  start loading all WMOs/M2s visible from the new location

Quest tracker improvements:
- Clicking a quest in the tracker now opens the Quest Log (L)
- Remove NoInputs flag so the tracker window receives mouse events
- Show only tracked quests in tracker; fall back to all quests if none tracked
- Add Track/Untrack button in Quest Log details panel
- Abandoning a quest automatically untracks it
- Track state stored in GameHandler::trackedQuestIds_ (per-session)
2026-03-10 05:18:45 -07:00
Kelsi
682f47f66b game: downgrade high-frequency per-interaction LOG_INFO/WARNING to LOG_DEBUG
Demote parse-level diagnostic logs that fire on every game interaction:
- TBC/Classic gossip, quest details, quest rewards: LOG_INFO → LOG_DEBUG
- WotLK gossip, quest details/reward/request-items: LOG_INFO → LOG_DEBUG
- Attack start/stop, XP gain, loot, name query, vendor, party: LOG_INFO → LOG_DEBUG
- TBC SMSG_UPDATE_OBJECT has_transport fallback: LOG_WARNING → LOG_DEBUG
- TBC parseAuraUpdate not-in-TBC diagnostic: LOG_WARNING → LOG_DEBUG
- Turtle SMSG_MONSTER_MOVE WotLK fallback: LOG_WARNING → LOG_DEBUG

These all fire multiple times per second during normal gameplay.
2026-03-10 05:09:43 -07:00
Kelsi
f0233c092b game: downgrade false-positive LOG_WARNING calls for normal game events
SMSG_SHOW_BANK and SMSG_BUY_BANK_SLOT_RESULT are normal interactions
that were incorrectly logged at WARNING level. LOGIN_VERIFY_WORLD coord
dump is diagnostic detail, not a warning condition. Downgraded:
- SMSG_SHOW_BANK: LOG_WARNING → LOG_INFO
- SMSG_BUY_BANK_SLOT_RESULT: LOG_WARNING → LOG_INFO
- SMSG_TRANSFER_PENDING: LOG_WARNING → LOG_INFO (from previous session)
- SMSG_NEW_WORLD: LOG_WARNING → LOG_INFO (from previous session)
- LOGIN_VERIFY_WORLD coord dump: LOG_WARNING → LOG_DEBUG
2026-03-10 04:56:42 -07:00
Kelsi
4dab5daf79 game: remove duplicate initial-spells LOG_INFO and downgrade debug spell list
- world_packets.cpp::InitialSpellsParser::parse already logs spell count
  at LOG_INFO; remove the duplicate count from handleInitialSpells()
- Downgrade verbose format-detection LOG_INFO to LOG_DEBUG (packet size,
  format name, first-10 spell IDs) — these are diagnostic details that
  clutter INFO output without adding operational value
2026-03-10 04:52:22 -07:00
Kelsi
4972472b2a security+game: downgrade auth credential and high-frequency LOG_INFO to LOG_DEBUG
- AUTH HASH logs (sessionKey, hash input, digest): session key material
  must never appear in production logs at INFO level — downgrade to DEBUG
- SMSG_AUTH_CHALLENGE field details (seeds, unknown1): downgrade to DEBUG;
  keep one INFO line with format name for connection diagnostics
- SMSG_MOTD per-line content: downgrade to DEBUG; keep INFO line count
- Transport position update per-entity: fires on every update for each
  entity riding a transport — downgrade to DEBUG
2026-03-10 04:51:01 -07:00
Kelsi
dd8c2cbb20 game: downgrade per-item-query LOG_INFO to LOG_DEBUG in game_handler
queryItemInfo and handleItemQueryResponse fire for every item in
inventory, loot windows, vendor lists, and mail — potentially dozens
of times at login or when any container is opened.  Downgrade to
LOG_DEBUG to reduce noise.  Also downgrade useItemById search traces
to LOG_DEBUG; the final warning (item not found) stays at LOG_WARNING.
2026-03-10 04:48:33 -07:00
Kelsi
f22845b238 game: downgrade trainer/initial-spells diagnostic LOG_INFO to LOG_DEBUG
Debug-labeled LOG_INFO calls in handleTrainerList and handleInitialSpells
fire every time the trainer window opens or the player logs in, producing
noisy output that obscures meaningful events.

- handleTrainerList: known spells list dump, hardcoded prerequisite checks
  (527/25312), and per-spell detail lines → LOG_DEBUG
  Keep one LOG_INFO for the spell count summary (meaningful lifecycle event)
- handleInitialSpells: hardcoded spell presence checks (527/988/1180) →
  LOG_DEBUG; replace with a single LOG_INFO for spell count summary
2026-03-10 04:46:42 -07:00
Kelsi
31ae689b2c game: fix TBC SMSG_QUESTGIVER_QUEST_DETAILS parsing by promoting Classic override to TbcPacketParsers
TBC 2.4.3 and Classic 1.12 share the same SMSG_QUESTGIVER_QUEST_DETAILS
format.  WotLK 3.3.5a adds three extra fields (informUnit u64, flags u32,
isFinished u8) that the base QuestDetailsParser::parse handles.  TBC had no
override, so it fell through to the WotLK heuristic which read flags+isFinished
as if they were TBC fields, misaligning choiceCount, rewardMoney, and rewardXp.

Fix: move parseQuestDetails from ClassicPacketParsers to TbcPacketParsers.
Classic inherits it unchanged (formats are identical).  Both expansions now
correctly parse: no informUnit, activateAccept(u8), suggestedPlayers(u32),
emote section, variable choice/reward item counts, rewardMoney, and rewardXp.
2026-03-10 04:37:03 -07:00
Kelsi
475e0c213c rendering: downgrade per-NPC-spawn LOG_INFO spam to LOG_DEBUG in application.cpp
Model batch submesh IDs and NPC geoset lists fire on every NPC spawn and
produce excessive log noise in normal gameplay. Downgrade to LOG_DEBUG.
Also downgrade per-equipment-slot DBC lookups from LOG_INFO to LOG_DEBUG.
2026-03-10 04:30:01 -07:00
Kelsi
f533373050 game: downgrade combat/charEnum per-entry LOG_INFO to LOG_DEBUG in WotLK parsers
Consistent with the same cleanup for Classic/TBC parsers. Melee hit, spell
damage, and spell heal logs fire on every combat event and belong at DEBUG
level. Per-character detail lines in the WotLK CharEnum parser are also
consolidated to a single DEBUG line per character.
2026-03-10 04:25:45 -07:00
Kelsi
d7ef40c9d7 game: downgrade combat/charEnum per-entry LOG_INFO to LOG_DEBUG in Classic/TBC parsers
Combat event logs (melee hit, spell damage, spell heal) fire on every combat
event and should be DEBUG-level. The per-character detail lines in parseCharEnum
are also moved to DEBUG — the summary line stays at INFO.
2026-03-10 04:23:54 -07:00
Kelsi
3bee0882cc game: fix Classic parseQuestDetails missing rewardXp field
Vanilla 1.12 SMSG_QUESTGIVER_QUEST_DETAILS includes rewardXp (uint32)
after rewardMoney, same as WotLK. Without this read the XP reward was
always 0 in the quest accept dialog for Classic.
2026-03-10 04:20:13 -07:00
Kelsi
5c830216be Remove debug LOG_INFO spam from applyEquipment; use DBC layout for texture fields
The verbose diagnostic logs added in 16cdde8 for Classic equipment debugging
are no longer needed now that the CSV string-detection bug is fixed. Remove
them to eliminate log spam on every character screen open.

Also replace the hardcoded `14 + region` texture field lookup with the same
DBC-layout-aware array pattern used in game_screen.cpp::updateCharacterTextures,
so texture field indices are correctly resolved per expansion.
2026-03-10 04:16:27 -07:00
Kelsi
b31a2a66b6 tools: fix DBC string-column detection false positives in both dbc_to_csv and asset_extract
The string-column auto-detector in both tools had two gaps that caused small
integer fields (RaceID=1, SexID=0/1, BaseSection, ColorIndex) to be falsely
classified as string columns, corrupting the generated CSVs:

1. No boundary check: a value of N was accepted as a valid string offset even
   when N landed inside a longer string (e.g. offset 3 inside "Character\...").
   Fix: precompute valid string-start boundaries (offset 0 plus every position
   immediately after a null byte); reject offsets that are not boundaries.

2. No diversity check: a column whose only non-zero value is 1 would pass the
   boundary test because offset 1 is always a valid boundary (it follows the
   mandatory null at offset 0). Fix: require at least 2 distinct non-empty
   string values before marking a column as a string column. Columns like
   SexID (all values are 0 or 1, resolving to "" and the same path fragment)
   are integer fields, not string fields.

Both dbc_to_csv and asset_extract now produce correct column metadata,
e.g. CharSections.dbc yields "strings=6,7,8" instead of "strings=0,1,...,9".
2026-03-10 03:49:06 -07:00
Kelsi
5b06a62d91 game: fix Classic/TBC movement ACKs silently dropped by isClassicLikeExpansion guard
Five movement control response handlers (speed change, move-root, move-flag
change, knock-back, teleport) had guards of the form !isClassicLikeExpansion()
or isClassicLikeExpansion() that prevented ACKs from ever being sent on
Classic/Turtle.  Each handler already contained correct legacyGuidAck logic
(full uint64 for Classic/TBC, packed GUID for WotLK) that was unreachable
due to the outer guard.

Classic servers (CMaNGOS/VMaNGOS/ChromieCraft) expect all of these ACKs.
Without them the server stalls the player's speed update, keeps root state
desynced, or generates movement hacks.  Fix by removing the erroneous
expansion guard and relying on the existing legacyGuidAck path.

Affected: handleForceSpeedChange, handleForceMoveRootState,
          handleForceMoveFlagChange, handleMoveKnockBack, handleTeleport.
2026-03-10 03:30:24 -07:00
Kelsi
4a213d8da8 tools/game: fix dbc_to_csv false-positive string detection + clear DBC cache on expansion switch
dbc_to_csv: The string-column auto-detector would mark integer fields (e.g.
RaceID=1, SexID=0, BaseSection=0-4) as string columns whenever their small
values were valid string-block offsets that happened to land inside longer
strings.  Fix by requiring that an offset point to a string *boundary* (offset
0 or immediately after a null byte) rather than any valid position — this
eliminates false positives from integer fields whose values accidentally alias
path substrings.  Affected CSVs (CharSections, ItemDisplayInfo for Classic/TBC)
can now be regenerated correctly.

game_handler: clearDBCCache() is already called by application.cpp before
resetDbcCaches(), but also add it inside resetDbcCaches() as a defensive
measure so that future callers of resetDbcCaches() alone also flush stale
expansion-specific DBC data (CharSections, ItemDisplayInfo, etc.).
2026-03-10 03:27:30 -07:00
Kelsi
29ca9809b1 game: fix Classic parseQuestDetails missing emote section before reward items
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run
Vanilla 1.12 SMSG_QUESTGIVER_QUEST_DETAILS includes an emote section
between suggestedPlayers and the choice/reward item lists:
  activateAccept(u8) + suggestedPlayers(u32) +
  emoteCount(u32) + [delay(u32) + type(u32)] × emoteCount +
  choiceCount(u32) + choices + rewardCount(u32) + rewards + money(u32)

The parser was skipping the emote section, causing the emote count to
be misread as the choice item count. Quests with emotes would show
zero choice items and shifted/missing reward and money data.
2026-03-10 03:09:12 -07:00
Kelsi
16cdde82b3 rendering: add diagnostic logging to applyEquipment for Classic equipment debugging
Log each equipment item's displayModel, inventoryType, and DBC lookup result
to help identify why Classic character equipment does not render correctly.
Also log ItemDisplayInfo.dbc field count, found texture names per region,
and missing texture paths so the exact failure point is visible in logs.
2026-03-10 02:23:54 -07:00
Kelsi
967cedba0e game: fix Classic/TBC SMSG_QUESTGIVER_QUEST_LIST quest title and questCount parsing
Classic 1.12 and TBC 2.4.3 don't include questFlags(u32) + isRepeatable(u8)
before each quest title in SMSG_QUESTGIVER_QUEST_LIST. WotLK 3.3.5a added
those 5 bytes. The previous code read them speculatively for all expansions
and only rewound on empty title, which failed for any non-empty title.

Also fix questCount always reading as uint8 (all WoW versions use u8 here).
The old u32/u8 heuristic could misread 4 bytes instead of 1, misaligning all
subsequent quest item reads.
2026-03-10 01:54:01 -07:00
Kelsi
7270a4e690 game: fix TBC 2.4.3 SMSG_CAST_FAILED missing parseCastFailed override
TBC 2.4.3 SMSG_CAST_FAILED format is spellId(u32) + result(u8), same as
Classic. WotLK added a castCount(u8) prefix before spellId. TbcPacketParsers
lacked a parseCastFailed override, so it fell through to the WotLK base
which read one extra byte as castCount, shifting the spellId read by one
byte and corrupting the spell ID and result for every failed cast on TBC.

- Add TbcPacketParsers::parseCastFailed override: reads spellId(4)+result(1)
- ClassicPacketParsers already overrides this (enum shift +1), so Classic unaffected
2026-03-10 01:30:06 -07:00
Kelsi
528b796dff game: fix Classic 1.12 SMSG_AUCTION_LIST_RESULT enchant slot count
Classic 1.12 auction entries contain only 1 enchant slot (3 uint32s),
while TBC and WotLK expanded this to 3 enchant slots (9 uint32s). Parsing
Classic auction results with the WotLK parser consumed 24 extra bytes per
entry (two extra enchant slots), corrupting randomPropertyId, stackCount,
ownerGuid, pricing and expiry data for every auction item.

- AuctionListResultParser::parse() gains a numEnchantSlots parameter (default 3)
- Classic path reads 1 enchant slot; TBC/WotLK read 3
- handleAuctionListResult/OwnerList/BidderList pass isClassicLikeExpansion()?1:3
2026-03-10 01:25:27 -07:00
Kelsi
8dd4bc80ec game: fix Classic 1.12 SMSG_TRAINER_LIST per-spell field layout
Classic 1.12 trainer list entries lack the profDialog and profButton
uint32 fields (8 bytes) that TBC/WotLK added before reqLevel. Instead,
reqLevel immediately follows spellCost, and a trailing unk uint32 appears
at the end of each entry. Parsing the WotLK format for Classic caused
misalignment from the third field onward, corrupting state, cost, level,
skill, and chain data for all trainer spells.

- TrainerListParser::parse() gains a isClassic bool parameter (default false)
- Classic path: cost(4) → reqLevel(1) → reqSkill... → chainNode3 → unk(4)
- WotLK/TBC path: cost(4) → profDialog(4) → profButton(4) → reqLevel(1) → reqSkill...
- handleTrainerList() passes isClassicLikeExpansion() as the flag
2026-03-10 01:20:41 -07:00