Commit graph

1367 commits

Author SHA1 Message Date
Kelsi
06a83537cf chore: re-remove dead functions reintroduced by PR #39 merge
The Lua refactor branch was based before the cleanup commit and
brought back allMacroCommands, getMacroShowtooltipArg (game_screen),
lfgJoinResultString, lfgTeleportDeniedString (game_handler).
2026-04-03 03:37:22 -07:00
Paul
a2814ab082 Merge commit '7f4c274e35' into chore/refactor-lua-engine 2026-04-03 07:35:57 +03:00
Paul
a916270a13 chore(lua): refactor addon Lua engine API + progress docs
- Refactor Lua addon integration:
  - Update CMakeLists.txt for addon build paths
  - Enhance addons API headers and Lua engine interface
  - Add new Lua API addon modules (`lua_api_helpers`, `lua_api_registrations`, `lua_services`, `lua_action_api`, `lua_inventory_api`, `lua_quest_api`, `lua_social_api`, `lua_spell_api`, `lua_system_api`, `lua_unit_api`)
  - Update implementation in addon_manager.cpp, lua_engine.cpp, application.cpp, game_handler.cpp
2026-04-03 07:31:06 +03:00
Kelsi
fe1c4c622b chore: remove dead functions left behind by handler extractions
685 lines of unused code duplicated into extracted handler files
(entity_controller, spell_handler, quest_handler, warden_handler,
social_handler, action_bar_panel, chat_panel, window_manager)
during PRs #33-#38. Build is now warning-free.
2026-04-02 14:47:04 -07:00
Paul
5af9f7aa4b chore(renderer): extract AnimationController and remove audio pass-throughs
Extract ~1,500 lines of character animation state from Renderer into a dedicated
AnimationController class, and complete the AudioCoordinator migration by removing
all 10 audio pass-through getters from Renderer.

AnimationController:
- New: include/rendering/animation_controller.hpp (182 lines)
- New: src/rendering/animation_controller.cpp (1,703 lines)
- Moves: locomotion state machine (50+ members), mount animation (40+ members),
  emote system, footstep triggering, surface detection, melee combat animations
- Renderer holds std::unique_ptr<AnimationController> and delegates completely
- AnimationController accesses audio via renderer_->getAudioCoordinator()

Audio caller migration:
- Migrate ~60 external callers from renderer->getXManager() to AudioCoordinator
  directly, grouped by access pattern:
  - UIServices: settings_panel, game_screen, toast_manager, chat_panel,
    combat_ui, window_manager
  - GameServices: game_handler, spell_handler, inventory_handler, quest_handler,
    social_handler, combat_handler
  - Application singleton: application.cpp, auth_screen.cpp, lua_engine.cpp
- Remove 10 pass-through getter definitions from renderer.cpp
- Remove 10 pass-through getter declarations from renderer.hpp
- Remove individual audio manager forward declarations from renderer.hpp
- Redirect 69 internal renderer.cpp audio calls to audioCoordinator_ directly
- game_handler.cpp: withSoundManager template uses services_.audioCoordinator;
  MFP changed from &Renderer::getUiSoundManager to &AudioCoordinator::getUiSoundManager
- GameServices struct: add AudioCoordinator* audioCoordinator member
- settings_panel: applyAudioVolumes(Renderer*) -> applyAudioVolumes(AudioCoordinator*)
2026-04-02 13:06:31 +03:00
Paul
b6e4e405b6 Merge commit 'ea8b0d9305' into chore/game-screen-extract 2026-03-31 20:17:21 +03:00
Paul
0e6aaeb44e fix warnings, remove phases from commentaries 2026-03-31 20:11:28 +03:00
Kelsi
5ad225313d fix: revert Warden HASH_RESULT fallback — silence is correct behavior
ChromieCraft/AzerothCore tolerates no HASH_RESULT response (continues
session without Warden checks), but immediately kicks on a WRONG hash.
The previous commit sent a fallback SHA1 which the server rejected,
breaking login that was working before.

Restore the skip behavior for WotLK/TBC: stay silent on HASH_REQUEST
when no CR match exists, and advance to WAIT_CHECKS so the rest of the
session proceeds normally. Turtle/Classic servers still get the fallback
hash since they're lenient about wrong values.
2026-03-31 01:18:58 -07:00
Kelsi
f3f7511105 fix: send Warden HASH_RESULT fallback instead of skipping response
Previously, WotLK/TBC servers with no CR match would skip the
HASH_REQUEST response entirely to "avoid account bans". This caused
a guaranteed kick-on-timeout for ALL WotLK servers including
permissive ones like ChromieCraft/AzerothCore.

Now sends a best-effort fallback hash (SHA1 of module image or raw
data) for all server types. Permissive servers accept this and
continue the session normally. Strict servers (Warmane) will reject
it but only kick — same outcome as the previous skip behavior, just
faster feedback.

For strict servers, the correct fix remains providing a .cr file
with pre-computed seed→reply entries for each module.
2026-03-31 01:10:43 -07:00
Kelsi
681e25a4f2 fix: clean up unused parameter style in entity_controller
Use nameless parameters instead of /*comment*/ syntax for unused
args in IObjectTypeHandler interface defaults and overrides.
2026-03-31 00:55:40 -07:00
Kelsi
11561184e6 fix: silence all -Wunused-parameter warnings in entity_controller
Suppress 9 unused parameter warnings in IObjectTypeHandler interface
methods and their overrides by commenting out parameter names:
- Base class: onCreate/onValuesUpdate/onMovementUpdate default empty
  implementations (parameters intentionally unused in base)
- ItemTypeHandler::onCreate: entity param forwarded only to onCreateItem
  which doesn't need it
- CorpseTypeHandler::onCreate: entity param not needed for corpse spawn

Build now produces zero warnings (excluding third-party stb headers).
2026-03-31 00:48:03 -07:00
Kelsi
32bb0becc8 fix: replace placeholder Warden RSA modulus with real Blizzard key
Replace the incorrectly extracted RSA-2048 modulus (which contained
the exponent bytes embedded inside it) with the verified Blizzard
public key used across all pre-Cataclysm clients (1.12.1, 2.4.3,
3.3.5a).

Key confirmed against two independent sources:
- namreeb/WardenSigning ClientKey.hpp (72 verified sniffed modules)
- SkullSecurity wiki Warden_Modules documentation

The modulus starts with 0x6BCE F52D... and ends with ...03F4 AFC7.
Exponent remains 65537 (0x010001).

Verification algorithm: SHA1(module_data + "MAIEV.MOD"), 0xBB-padded
to 256 bytes, RSA verify-recover with raw (no-padding) mode.

Signature failures are non-fatal (log warning, continue loading) so
private-server modules signed with custom keys still work. This is
necessary because servers like ChromieCraft/AzerothCore may use their
own signing keys.

Also update warden_module.hpp status: all implementation items now .
2026-03-30 22:50:47 -07:00
Kelsi
88d047d2fb feat: implement Warden API binding / IAT patching for module imports
Complete the last major Warden stub — the import table parser that
resolves Windows API calls in loaded modules. This is the critical
missing piece for strict servers like Warmane.

Implementation:
- Parse Warden module import table from decompressed data (after
  relocation entries): alternating libraryName\0 / functionName\0
  pairs, terminated by null library name
- For each import, look up the emulator's pre-registered stub address
  (VirtualAlloc, GetTickCount, ReadProcessMemory, etc.)
- Auto-stub unrecognized APIs with a no-op returning 0 — prevents
  module crashes on unimplemented Windows functions
- Patch each IAT slot (sequential dwords at module image base) with
  the resolved stub address
- Add WardenEmulator::getAPIAddress() public accessor for IAT lookups
- Fix initialization order: bindAPIs() now runs inside initializeModule()
  after emulator setup but before entry point call

The full Warden pipeline is now: RC4 decrypt → RSA verify → zlib
decompress → parse executable → relocate → create emulator → register
API hooks → bind imports (IAT patch) → call entry point → extract
exported functions (packetHandler, tick, generateRC4Keys, unload).
2026-03-30 22:38:05 -07:00
Kelsi
248d131af7 feat: implement Warden module callbacks (sendPacket, validateModule, generateRC4)
Implement the three stubbed Warden module callbacks that were previously
TODO placeholders:

- **sendPacket**: Encrypts module output via WardenCrypto RC4 and sends
  as CMSG_WARDEN_DATA through the game socket. Enables modules to send
  responses back to the server (required for strict servers like Warmane).

- **validateModule**: Compares the module's provided 16-byte MD5 hash
  against the hash received during download. Logs error on mismatch
  (indicates corrupted module transit).

- **generateRC4**: Derives new encrypt/decrypt RC4 keys from a 16-byte
  seed using SHA1Randx, then replaces the active WardenCrypto key state.
  Handles mid-session re-keying requested by the module.

Architecture:
- Add setCallbackDependencies() to inject WardenCrypto* and socket send
  function into WardenModule before load() is called
- Use thread_local WardenModule* so C function pointer callbacks (which
  can't capture state) can reach the module's dependencies during init
- Wire dependencies from WardenHandler before module load

Also update warden_module.hpp status markers — RSA verification, zlib,
executable parsing, relocation, and Unicorn emulation are all implemented
(were incorrectly marked as TODO). Only API binding/IAT patching and
RSA modulus verification against real WoW.exe remain as gaps.
2026-03-30 20:29:26 -07:00
Kelsi
2c50cc94e1 docs: add why-comments to TBC parsers, bell audio, portrait preview
- packet_parsers_tbc: explain spline waypoint cap (DoS prevention),
  spline compression flags (Catmull-Rom 0x80000 / linear 0x2000 use
  uncompressed format, others use packed delta), spell hit target cap
  (128 >> real AOE max of ~20), guild roster cap (1000 safety limit)
- ambient_sound_manager: explain 1.5s bell toll spacing — matches
  retail WoW cadence, allows each toll to ring out before the next
- character_preview.hpp: explain 4:5 portrait aspect ratio for
  full-body character display in creation/selection screen
2026-03-30 17:26:13 -07:00
Kelsi
92369c1cec docs: add why-comments to rendering, packets, and UI code
- charge_effect: explain inversesqrt guard (prevents NaN on stationary
  character) and dust accumulator rate (30 particles/sec * 16ms)
- swim_effects: explain why insect pipeline disables depth test
  (screen-space sprites must render above water geometry)
- packet_parsers_classic: explain spline waypoint cap (DoS prevention)
  and packed GUID compression format (non-zero bytes only, mask byte)
- talent_screen: explain class ID to bitmask conversion (1-indexed
  WoW class IDs → power-of-2 mask for TalentTab.classMask matching)
- auth_screen: explain login music volume reduction (80% so UI sounds
  remain audible over background track)
2026-03-30 17:23:07 -07:00
Kelsi
e8a4a7402f fix: clamp player percentage stats, add scale field why-comment
- entity_controller: clamp block/dodge/parry/crit/rangedCrit percentage
  fields to [0..100] after memcpy from update fields — guards against
  NaN/Inf from corrupted packets reaching the UI renderer
- entity_controller: add why-comment on OBJECT_FIELD_SCALE_X raw==0
  check — IEEE 754 0.0f is all-zero bits, so raw==0 means the field
  was never populated; keeping default 1.0f prevents invisible entities
2026-03-30 15:48:30 -07:00
Kelsi
fe7912b5fa fix: prevent buffer overflows in Warden PE parsing
- Add bounds checks to readLE32/readLE16 — malformed Warden modules
  could cause out-of-bounds reads on untrusted PE data
- Fix unsigned underflow in PE section loading: if rawDataOffset or
  virtualAddr exceeds buffer size, the subtraction wrapped to a huge
  uint32_t causing memcpy to read/write far beyond bounds. Now skips
  the section entirely and uses std::min with pre-validated maxima
2026-03-30 15:33:03 -07:00
Kelsi
b39f0f3605 refactor: name GUID type and LFG role constants, add why-comments
- world_packets: name kGuidTypeMask/kGuidTypePet/kGuidTypeVehicle
  for chat receiver GUID type detection, with why-comment explaining
  WoW's bits-48-63 entity type encoding and 0xF0FF mask purpose
- lua_engine: name kRoleTank/kRoleHealer/kRoleDamager (0x02/0x04/0x08)
  for WotLK LFG role bitmask, add context on Leader bit (0x01) and
  source packets (SMSG_GROUP_LIST / SMSG_LFG_ROLE_CHECK_UPDATE)
2026-03-30 15:28:18 -07:00
Kelsi
a389fd2ef4 refactor: name SRP/Warden crypto constants, add why-comments
- srp: name kEphemeralBytes (19 = 152 bits, matches Blizzard client)
  and kMaxEphemeralAttempts (100) with why-comment explaining A != 0
  mod N requirement and near-zero failure probability
- warden_module: add why-comment on 0x400000 module base (default
  PE image base for 32-bit Windows executables)
- warden_module: name kRsaSignatureSize (256 = RSA-2048) with
  why-comment explaining signature stripping (placeholder modulus
  can't verify Blizzard's signatures)
2026-03-30 15:12:27 -07:00
Kelsi
d2a7d79f60 refactor: add why-comments to zone tiles, audio cache, socket buffer
- zone_manager: document tile-to-zone key encoding (tileX * 100 + tileY,
  safe because tileY < 64 < 100) and explain that ranges are empirically
  derived from the retail WoW map grid
- audio_engine: expand sample rate comment — miniaudio defaults to
  device rate causing pitch distortion if not set explicitly; name
  kMaxCachedSounds constant with memory budget explanation
- tcp_socket: add why-comment on 4 KB recv buffer sizing — covers
  typical 20-500 byte packets and worst-case ~2 KB UPDATE_OBJECT
2026-03-30 14:52:51 -07:00
Kelsi
8c7db3e6c8 refactor: name FNV-1a/transport constants, fix dead code, add comments
- vk_context: name FNV-1a hash constants (kFnv1aOffsetBasis/kFnv1aPrime)
  with why-comment on algorithm choice for sampler cache
- transport_manager: collapse redundant if/else that both set
  looping=false into single unconditional assignment, add why-comment
  explaining the time-closed path design
- transport_manager: hoist duplicate kMinFallbackZOffset constants out
  of separate if-blocks, add why-comment on icebreaker Z clamping
- entity: expand velocity smoothing comment — explain 65/35 EMA ratio
  and its tradeoff (jitter suppression vs direction change lag)
2026-03-30 14:48:06 -07:00
Kelsi
a940859e6a refactor: name auth security flags, log JSON parse failures
- auth_handler: define kSecurityFlagPin/MatrixCard/Authenticator
  constants (0x01/0x02/0x04) with why-comment explaining WoW login
  challenge securityFlags byte, replace all bare hex literals
- expansion_profile: log warning on jsonInt() parse failure instead
  of silently returning default — makes malformed expansion.json
  diagnosable without debugger
2026-03-30 14:43:50 -07:00
Kelsi
28e5cd9281 refactor: replace magic bag slot offset 19 with FIRST_BAG_EQUIP_SLOT
- Add Inventory::FIRST_BAG_EQUIP_SLOT = 19 constant with why-comment
  explaining WoW equip slot layout (bags occupy slots 19-22)
- Replace all 19 occurrences of magic number 19 in bag slot calculations
  across inventory_handler, spell_handler, inventory, and game_handler
- Add UNIT_FIELD_FLAGS / UNIT_FLAG_PVP comment in combat_handler
- Add why-comment on network packet budget constants (prevent server
  data bursts from starving the render loop)
2026-03-30 14:20:39 -07:00
Kelsi
6dfac314ee fix: remove dead code, name constants, add why-comments
- renderer: remove no-op assignment (mountAnims_.stand = 0 when already 0)
- renderer: add why-comments on blacksmith WMO ID 96048 (ambient forge
  sounds) with TODO for other smithy buildings
- terrain_renderer: replace 1e30f sentinel with numeric_limits::max(),
  name terrain view distance constant (1200 units ≈ 9 ADT tiles)
- social_handler: add missing LFG case 15, document case 0 nullptr
  return (success = no error message), add enum name comments
2026-03-30 14:10:32 -07:00
Kelsi
4acebff65c refactor: extract fallback textures, add why-comments, name WMO constant
- character_renderer: extract duplicated fallback texture creation
  (white/transparent/flat-normal) into createFallbackTextures() — was
  copy-pasted between initialize() and clear()
- wmo_renderer: replace magic 8192 with kMaxRetryTracked constant,
  add why-comment explaining the fallback-retry set cap (Dalaran has
  2000+ unique WMO groups)
- quest_handler: add why-comment on reqCount=0 fallback — escort/event
  quests can report kill credit without objective counts in query response
2026-03-30 14:06:30 -07:00
Kelsi
f313eec24e refactor: replace magic slot offset 23 with NUM_EQUIP_SLOTS, simplify channel search
- Replace all 11 occurrences of magic number 23 in backpack slot
  calculations with Inventory::NUM_EQUIP_SLOTS across inventory_handler,
  spell_handler, and inventory.cpp
- Add why-comment to NUM_EQUIP_SLOTS explaining WoW slot layout
  (equipment 0-22, backpack starts at 23 in bag 0xFF)
- Add why-comment on 0x80000000 bit mask in item query response
  (high bit flags negative/missing entry response)
- Replace manual channel membership loops with std::find in
  chat_handler.cpp (YOU_JOINED and PLAYER_ALREADY_MEMBER cases)
- Add why-comment on PLAYER_ALREADY_MEMBER reconnect edge case
2026-03-30 14:01:34 -07:00
Kelsi
a9ce22f315 refactor: extract findOnUseSpellId helper, add warden hash comment
- spell_handler: extract duplicated item on-use spell lookup into
  findOnUseSpellId() — was copy-pasted in useItemBySlot and useItemInBag
- warden_handler: add why-comment explaining the door model HMAC-SHA1
  hash table (wall-hack detection for unmodified 3.3.5a client data)
2026-03-30 13:56:45 -07:00
Kelsi
76f493f7d9 refactor: replace goto with structured control flow
- spell_handler.cpp: replace goto-done with do/while(false) for pet
  spell packet parsing — bail on truncated data while always firing
  events afterward
- water_renderer.cpp: replace goto-found_neighbor with immediately
  invoked lambda to break out of nested neighbor search loops
2026-03-30 13:47:14 -07:00
Kelsi Rae Davis
fe080bed4b
Merge pull request #31 from ldmonster/chore/break-application-from-gamehandler
[chore] refactor: Break Application::getInstance() from GameHandler
2026-03-30 13:26:29 -07:00
Paul
a86efaaa18 [refactor] Break Application::getInstance() from GameHandler
Introduce `GameServices` struct — an explicit dependency bundle that
`Application` populates and passes to `GameHandler` at construction time.
Eliminates all 47 hidden `Application::getInstance()` calls in
`src/game/*.cpp`, completing SOLID-D (dependency-inversion) cleanup.

Changes:
- New `include/game/game_services.hpp` — `struct GameServices` carrying
  pointers to `Renderer`, `AssetManager`, `ExpansionRegistry`, and two
  taxi-mount display IDs
- `GameHandler(GameServices&)` replaces default constructor; exposes
  `services() const` accessor for domain handlers
- `Application` holds `game::GameServices gameServices_`; populates it
  after all subsystems are created, then constructs `GameHandler`
  (fixes latent init-order bug: `GameHandler` was previously created
  before `AssetManager` / `ExpansionRegistry`)
- `game_handler.cpp`: duplicate `isActiveExpansion` / `isClassicLikeExpansion` /
  `isPreWotlk` anonymous-namespace helpers removed; `game_utils.hpp`
  included instead
- All domain handlers (`InventoryHandler`, `SpellHandler`, `MovementHandler`,
  `CombatHandler`, `QuestHandler`, `SocialHandler`, `WardenHandler`) replace
  `Application::getInstance().getXxx()` with `owner_.services().xxx`
2026-03-30 09:17:42 +03:00
Kelsi
169595433a debug: add GO interaction diagnostics at every decision point
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
Adds [GO-DIAG] WARNING-level logs at:
- Right-click dispatch (raypick hit / re-interact with target)
- interactWithGameObject entry + all BLOCKED paths
- SMSG_SPELL_GO (wasInTimedCast, lastGoGuid, pendingGoGuid state)
- SMSG_LOOT_RESPONSE (items, gold, guid)
- Raypick candidate GO positions (entity pos + hit center + radius)

These logs will pinpoint exactly where the interaction fails:
- No GO-DIAG lines = GOs not in entity manager / not visible
- Raypick GO pos=(0,0,0) = GO position not set from update block
- BLOCKED = guard condition preventing interaction
- SPELL_GO wasInTimedCast=false = timer race (already fixed)
2026-03-29 23:09:28 -07:00
Kelsi
5e83d04f4a fix: GO cast timer fallback cleared state before SMSG_SPELL_GO arrived
The client-side cast timer expires ~50-200ms before the server sends
SMSG_SPELL_GO (float precision + frame timing). Previously the fallback
called resetCastState() which set casting_=false and currentCastSpellId_
=0. When SMSG_SPELL_GO arrived moments later, wasInTimedCast evaluated
to false (false && spellId==0), so the loot path (CMSG_LOOT via
lastInteractedGoGuid_) was never taken. Quest chests never opened.

Now the fallback skips resetCastState() for GO interaction casts, letting
the cast bar sit at 100% until SMSG_SPELL_GO arrives and handles cleanup
properly with wasInTimedCast=true.
2026-03-29 22:58:49 -07:00
Kelsi
cfbae93ce3 fix: client timer fallback re-sent CMSG_GAMEOBJ_USE and cleared loot guid
When the client-side cast timer expired slightly before SMSG_SPELL_GO
arrived, the fallback at update():1367 called performGameObjectInteraction
Now which sent a DUPLICATE CMSG_GAMEOBJ_USE to the server (confusing its
GO state machine), then resetCastState() cleared lastInteractedGoGuid_.
When SMSG_SPELL_GO finally arrived, the guid was gone so CMSG_LOOT was
never sent — quest chests produced no loot window.

Fix: the fallback no longer re-sends USE (server drives the interaction
via SMSG_SPELL_GO). resetCastState() no longer clears
lastInteractedGoGuid_ so the SMSG_SPELL_GO handler can still send LOOT.
2026-03-29 22:45:17 -07:00
Kelsi
785f03a599 fix: stale GO interaction guard broke future casts; premature LOOT interfered
Two remaining GO interaction bugs:

1. pendingGameObjectInteractGuid_ was never cleared after SMSG_SPELL_GO
   or SMSG_CAST_FAILED, leaving it stale. This suppressed CMSG_CANCEL_CAST
   for ALL subsequent spell casts (not just GO casts), causing the server
   to think the player was still casting when they weren't.

2. For chest-like GOs, CMSG_LOOT was sent simultaneously with
   CMSG_GAMEOBJ_USE. If the server starts a timed cast ("Opening"),
   the GO isn't lootable until the cast completes — the premature LOOT
   gets an empty response or is dropped, potentially corrupting the
   server's loot state. Now defers LOOT to handleSpellGo which sends it
   after the cast completes (via lastInteractedGoGuid_).
2026-03-29 22:36:30 -07:00
Kelsi
6b5e924027 fix: GO interaction casts canceled by any movement — quest credit lost
pendingGameObjectInteractGuid_ was always cleared to 0 right before
the interaction, which defeated the cancel-protection guard in
cancelCast(). Any positional movement (WASD, jump) during a GO
interaction cast (e.g., "Opening" on a quest chest) sent
CMSG_CANCEL_CAST to the server, aborting the interaction and
preventing quest objective credit.

Now sets pendingGameObjectInteractGuid_ to the GO guid so:
1. cancelCast() skips CMSG_CANCEL_CAST for GO-triggered casts
2. The cast-completion fallback can re-trigger loot after timer expires
3. isGameObjectInteractionCasting() returns true during GO casts
2026-03-29 22:22:20 -07:00
Kelsi
c1c28d4216 fix: quest objective GOs never granted credit — REPORT_USE skipped for chests
CMSG_GAMEOBJ_REPORT_USE was only sent for non-chest GOs. Chest-type
(type=3) and name-matched chest-like GOs (Bundle of Wood, etc.) went
through a separate path that sent CMSG_GAMEOBJ_USE + CMSG_LOOT but
skipped REPORT_USE. On AzerothCore, REPORT_USE triggers the server-side
HandleGameobjectReportUse which calls GossipHello on the GO script —
this is where many quest objective scripts grant credit.

Restructured so CMSG_GAMEOBJ_USE is sent first for all GO types,
then chest-like GOs additionally send CMSG_LOOT, and REPORT_USE fires
for everything except mailboxes.
2026-03-29 22:04:23 -07:00
Kelsi
8376757f7e cleanup: migrate 20 remaining setReadPos(getSize()) to skipAll()
The Packet::skipAll() method was introduced to replace the verbose
setReadPos(getSize()) pattern. 186 instances were migrated earlier,
but 20 survived in domain handler files created after the migration.
Also removes a redundant single-element for-loop wrapper around
SMSG_LOOT_CLEAR_MONEY registration.
2026-03-29 21:51:03 -07:00
Kelsi
ae32b27d6c fix: misplaced brace included 4 unrelated handlers inside instance-difficulty loop
Same class of bug as inventory_handler fix b9ecc26f. The for-loop over
{SMSG_INSTANCE_DIFFICULTY, MSG_SET_DUNGEON_DIFFICULTY} was missing its
closing brace, so GUILD_DECLINE, RAF_EXPIRED, RAF_FAILURE, and
PVP_AFK_RESULT registrations executed inside the loop body — each
registered twice (once per opcode). Currently harmless since duplicate
registration is idempotent, but structurally wrong.
2026-03-29 21:50:55 -07:00
Kelsi
01ab2a315c fix: achievement message silently truncated by char[256] snprintf
Long achievement names combined with sender name could exceed 256
bytes, silently cutting the message mid-word in chat. Replaced with
std::string concatenation which grows dynamically.
2026-03-29 21:35:56 -07:00
Kelsi
e42f8f1c03 fix: misleading indentation on reputation addon event dispatch
The two fireAddonEvent calls were indented as if conditional on
repChangeCallback_ but actually execute unconditionally (no braces).
Fixed indentation and added clarifying comment.
2026-03-29 21:35:49 -07:00
Kelsi
5583573beb fix: eliminate last std::rand() calls — music shuffle and UI weather
zone_manager.cpp used std::rand() for music track selection with modulo
bias and global state. game_screen.cpp used std::rand() for rain/snow
particle positions. Both now use local std::mt19937 seeded from
random_device. Also removes the global srand(time(nullptr)) call since
no code depends on the C rand() seed anymore.

No std::rand() or srand() calls remain in the codebase.
2026-03-29 21:01:51 -07:00
Kelsi
294c91d84a fix: migrate 197 unsafe packet bounds checks to hasRemaining/getRemainingSize
All domain handler files used 'packet.getSize() - packet.getReadPos()'
which underflows to ~2^64 when readPos exceeds size (documented in
commit ed63b029). The game_handler.cpp and packet_parsers were migrated
to hasRemaining(N) in an earlier cleanup, but the domain handlers were
created after that migration by the PR #23 split, copying the old
unsafe patterns back in. Now uses hasRemaining(N) for comparisons and
getRemainingSize() for assignments across all 7 handler files.
2026-03-29 20:53:26 -07:00
Kelsi
fa15a3de1f fix: transport elapsed time lost millisecond precision after ~4.5 hours
elapsedTime_ was float (32-bit, ~7 significant digits). At 16384
seconds the float can only represent integers, so elapsedTime_*1000
jumps in 1-second steps — ships and elevators visibly jerk. Changed to
double (53-bit mantissa) which maintains sub-millisecond precision for
~285 million years. Also changed lastServerUpdate to double to match.
2026-03-29 20:14:45 -07:00
Kelsi
c1f6364814 cleanup: remove misleading (void)flags — variable IS used for crit check
The cast falsely suggests flags is unused but it's read on the next
line for isCrit = (flags & 0x02). Also inlines the periodicLog discard.
2026-03-29 20:05:45 -07:00
Kelsi
59bbeaca62 fix: ::toupper/::tolower UB on signed char at 5 remaining call sites
std::toupper(int) and std::tolower(int) have undefined behavior when
passed a negative value. These sites passed raw signed char without
casting to unsigned char first, unlike the rest of the codebase which
already uses the correct pattern. Affects auth (account names), world
packets, and mount sound path matching.
2026-03-29 19:58:36 -07:00
Kelsi
c4d2b1709e fix: SMSG_RANDOM_ROLL parsed fields in wrong order — garbled /roll output
WotLK format is min(4)+max(4)+result(4)+guid(8)=20 bytes. The parser
read guid(8) first (treating min|max as a uint64), then targetGuid(8)
(non-existent field), then the actual values at wrong offsets. Every
/roll message showed garbled numbers and a bogus roller identity.

Also adds a hasRemaining guard for the 64 bytes of damage/armor/resist
fields in the item query parser — previously read past end with silent
zero-fill on truncated packets.
2026-03-29 19:51:07 -07:00
Kelsi
e5b4e86600 fix: misleading indentation on BAG_UPDATE/UNIT_INVENTORY_CHANGED emits
The two emit calls were indented 12 spaces (suggesting a nested block)
instead of 8 (matching the enclosing if). Same class of maintenance
trap as the PLAYER_ALIVE/PLAYER_UNGHOST fix in b3abf04d.
2026-03-29 19:31:29 -07:00
Kelsi
061a21da8f fix: guild event string never appended; /leave left stale party state
1. Contradictory condition (!numStrings && numStrings >= 1) was always
   false, so unknown guild event messages never included the server's
   context string. Fixed to just numStrings >= 1.

2. leaveParty() only sent the packet without clearing partyData or
   firing addon events, so /leave left party frames visible until the
   server pushed an update. Now delegates to leaveGroup() which handles
   both the packet and local state cleanup.
2026-03-29 19:31:21 -07:00
Kelsi
3da3638790 cleanup: remove misleading (void)reasonType — variable IS used below
The cast falsely suggests reasonType is unused, but it's read on lines
3699-3702 for AFK/vote-kick differentiation. Same class of issue as
the (void)isPlayerTarget fix in commit 6731e584.
2026-03-29 19:23:05 -07:00