Resolved a melee desync where attacks could fail unless constantly moving through target.
Movement opcode handling:
- In 0x006B compatibility path, prefer compressed-move batch detection before weather alias routing.
- Route 0x006B payloads shaped like SMSG_COMPRESSED_MOVES sub-packets to handleCompressedMoves().
Root/unroot state synchronization:
- Added handling for SMSG_FORCE_MOVE_ROOT and SMSG_FORCE_MOVE_UNROOT.
- Added handleForceMoveRootState() to apply server-authoritative ROOT flag locally.
- Send CMSG_FORCE_MOVE_ROOT_ACK / CMSG_FORCE_MOVE_UNROOT_ACK with current movement payload and counter.
Melee facing stability:
- Added periodic facing sync during auto-attack (MSG_MOVE_SET_FACING) when angular drift to target exceeds threshold.
- Keeps server front-arc/facing checks aligned while stationary meleeing.
Noise reduction / stream hygiene:
- Consume optional legacy/system packets safely: SMSG_FRIEND_LIST, SMSG_IGNORE_LIST, SMSG_ADDON_INFO, SMSG_EXPECTED_SPAM_RECORDS.
Validation:
- Rebuilt successfully with cmake --build build -j32.
Introduce data-driven opcode registry with canonical and alias sources:
- Added Data/opcodes/canonical.json as the single canonical LogicalOpcode set.
- Added Data/opcodes/aliases.json for cross-core naming aliases (CMaNGOS/AzerothCore/local legacy).
Added generator and generated include fragments:
- tools/gen_opcode_registry.py emits include/game/opcode_enum_generated.inc, include/game/opcode_names_generated.inc, and include/game/opcode_aliases_generated.inc.
- include/game/opcode_table.hpp now consumes generated enum entries.
- src/game/opcode_table.cpp now consumes generated name and alias tables.
Loader canonicalization behavior:
- OpcodeTable::nameToLogical canonicalizes incoming JSON opcode names via alias table before enum lookup, so implementation code stays stable while expansion maps can use different core spellings.
Validation and build integration:
- Added tools/validate_opcode_maps.py to validate canonical contract across expansions.
- Added CMake targets opcodes-generate and opcodes-validate.
- wowee target now depends on opcodes-generate so generated headers stay current.
Validation/build run:
- cmake -S . -B build
- cmake --build build --target opcodes-generate opcodes-validate
- cmake --build build -j32
Classic: synchronized Data/expansions/classic/opcodes.json to /home/k/Desktop/classicopcodes.h with exact symbol/value parity (0 missing, 0 mismatches).
WotLK: synchronized Data/expansions/wotlk/opcodes.json to /home/k/Desktop/azerothcoreOpcodes.h and aligned symbol names to AzerothCore naming.
Logical opcode layer: expanded include/game/opcode_table.hpp and src/game/opcode_table.cpp to include missing canonical opcode symbols required by synced tables, and removed legacy alias fallback block so canonical names are used directly.
Gameplay/handler updates included from ongoing fixes: duel/taxi stale opcode cleanup, level-up/sound handling adjustments, and related parser/packet references updated to match canonical opcode identifiers.
Validated by successful full build: cmake --build build -j32.
Switch level-up callback triggering to SMSG_LEVELUP_INFO/SMSG_LEVELUP_INFO_ALT so animation and SFX are server-authoritative.
Parse the new level directly from the level-up packet, update cached player/character level state, and fire callback only when level increases.
Remove duplicate callback firing from UNIT_FIELD_LEVEL update-field handling to prevent double-triggered level-up effects when values sync after the packet.
handleMonsterMove called MonsterMoveParser::parse() directly (WotLK
format with extra uint8 after PackedGuid) instead of going through
the polymorphic packetParsers_->parseMonsterMove(). Added parseVanilla
override to ClassicPacketParsers so classic/turtle use the correct
vanilla format.
Update field extraction in both CREATE_OBJECT and VALUES handlers to check
specific fields (maxHealth, level, faction, etc.) before power/maxpower range
checks. In Classic 1.12.1, power indices 23-27 are adjacent to maxHealth (28),
and maxPower indices 29-33 are adjacent to level (34) and faction (35), so
range checks like "key >= powerBase && key < powerBase+7" were incorrectly
capturing those fields.
Add build-aware WoW.exe selection and runtime global patching for Warden
SYSTEM_INFO, EndScene, WorldEnables, and LastHardwareAction chains. Fix
Classic opcodes and auth session addon data format for CMaNGOS compatibility.
Bag bar: left-click drag bags to swap positions using CMSG_SWAP_ITEM
with INVENTORY_SLOT_BAG_0 (255). Local optimistic swap for instant
feedback. Camera controller now respects ImGui WantCaptureMouse.
Vendor auto-open bags only triggers once per session.
Fix opcodes: CMSG_GAMEOBJECT_USE 0x01B→0x0B1 (typo caused
SMSG_FORCEACTIONSHOW spam), CMSG_CANCEL_AURA 0x033→0x136,
SMSG_SELL_ITEM 0x1A4→0x1A1.
Use actual WoW 3.3.5a PlayerExertions and Vox sound paths from MPQ
manifests for attack grunts, wounds, and death sounds. Handle Blizzard
naming quirks (HumanFeamle typo, OrcMale no Final suffix, Scourge→Undead).
Add COMBAT_IDLE animation state with ready weapon stance between swings.
Restore deleted MPQ sound manifest docs.
Wire CombatSoundManager into SMSG_ATTACKERSTATEUPDATE for weapon swing,
impact, and miss sounds. Add attack grunt and wound vocalizations to
ActivitySoundManager using correct WoW MPQ PC-suffix paths. Trigger
attack animation on SMSG_SPELL_GO for warrior melee abilities. Add
client-side melee range and facing checks to prevent server rejections.
Snap charge arrival to target's current position for reliable melee range.
Replace 2D screen-space ding rings with real WoW LevelUp.m2 particle/geometry
effect. Fix FBlock particle color parsing (C3Vector floats, not CImVector bytes)
which was producing blue/red instead of golden yellow. Spell effect models bypass
particle dampeners, glow sprite conversion, Mod→Additive blend override, and all
collision (floor/wall/camera) to prevent camera zoom-in. Other players' level-ups
trigger the 3D effect at their position with group chat notification. F7 hotkey
for testing.
- Correct UNIT_FIELD_BYTES_0 to index 23 (was incorrectly 56) in update fields JSON and table
- Replace single power/maxPower with powers[7]/maxPowers[7] arrays indexed by power type
- Add setPowerByType/setMaxPowerByType helpers for setting specific power types
- getSpellCastResultString now returns "Not enough rage/energy/focus/runic power" based on player power type instead of always "Not enough mana"
- Fix NPC combat messages concatenating onto previous lines by combining prefix+body into single renderTextWithLinks call instead of TextWrapped+SameLine
- Improve generic error strings to be more WoW-authentic (Invalid target, Target not in line of sight, etc.)
Mod blend (GL_DST_COLOR, GL_ZERO) multiplies the framebuffer by texture color,
so dark pixels create visible black rectangles. Use a variable colorKeyThreshold
uniform (0.7 for Mod/Mod2x, 0.08 default) to discard dark pixels from these
batches while preserving normal colorKeyBlack behavior elsewhere.
The pixel content glow detection (>60% dark = glow) was too aggressive,
flagging dark metal textures on sconces as glow textures and making
structural geometry transparent. The forced additive blending for
colorKeyBlack batches compounded the issue.
Reverted both. Added per-batch diagnostic logging for models containing
"light", "lamp", or "lantern" to identify the actual blend modes and
material flags on Stormwind bridge lamps.
force additive blending for colorKeyBlack batches
Two key changes:
1. Detect glow-like textures by analyzing pixel content during loading:
textures that are >60% near-black with some bright pixels are flagged
as colorKeyBlack regardless of filename. This catches glow textures
like Stormwind street lamps whose paths don't contain keywords like
"lamp" or "lantern".
2. Force additive blending (mode 3) for batches with colorKeyBlack
textures that have blendMode 1 or 2. These textures are designed for
additive blending where black = transparent, but some M2 files specify
Alpha or AlphaKey blend modes which cause black areas to render as
solid dark disks instead of being invisible.
Two fixes:
1. shouldUseGlowSprite now requires the UNLIT material flag (0x01) for
the colorKeyBlack+flameLikeModel path. Previously, structural geometry
(torch handles, sconce brackets) on flame-like models got replaced
with glow sprites because their texture paths contained keywords like
"torch" in the directory name, setting colorKeyBlack on non-glow
textures. Requiring UNLIT ensures only actual glow/emissive batches
become sprites while lit structural geometry renders normally.
2. Fragment shader now discards near-black (maxRGB < 0.1) for ALL unlit
non-opaque batches, not just additive blend modes. Glow effects on
lanterns/lamps that use blendMode 1 (AlphaKey) or 2 (Alpha) instead
of 3 (Additive) now properly discard their black backgrounds.
Three-part fix for glow textures showing opaque black rectangles instead
of being transparent:
1. Pass blend mode to fragment shader via uBlendMode uniform. For additive
blend modes (3=Add, 6=BlendAdd), discard near-black fragments (maxRGB
< 0.1) since they contribute nothing visually but render as dark
rectangles against sky/terrain.
2. Expand colorKeyBlack texture keyword detection to include "lamp",
"lantern", "glow", "flare", "brazier", "campfire", "bonfire" in
addition to the existing "candle", "flame", "fire", "torch".
3. Expand flameLikeModel detection for glow sprite conversion to include
"brazier", "campfire", "bonfire". Also compute glow centers for
colorKeyBlack batches (not just blendMode >= 3) so glow sprites
position correctly for all flame-like objects.
All WoW versions use exactly 5 damage entries in SMSG_ITEM_QUERY_SINGLE_RESPONSE.
The previous heuristic defaulted to parsed2 (2 entries) and only switched to
parsed5 for armor items based on a non-zero armor check — but that check was
circular: it needed parsed5.armor>0 to switch to parsed5, which only happens
if parsed5 is already the selected result. Flipping the default to parsed5 and
falling back to parsed2 only when parsed2 clearly identifies a weapon item
(damage+delay present, parsed5 doesn't match) fixes armor for armor items
without breaking weapon stat parsing.
The WotLK item query parser assumed BuyCount is always present as a
separate field (Flags + Flags2 + BuyCount + BuyPrice + SellPrice = 5
fields). Some server variants omit BuyCount, shifting every subsequent
field by 4 bytes and causing armor to be read from the wrong offset.
Now read 5 fields and validate InventoryType (must be 0-28). If it
falls outside that range, rewind and re-parse with 4 fields (no
BuyCount), which recovers correct alignment. Elevated item query log
from DEBUG to INFO so the parsed armor value is visible in output.
The character stats panel was showing Armor: 0 because summing armor
from item query responses is fragile (depends on correct BuyCount/damage
block parsing). Instead, read the server-authoritative total armor
directly from UNIT_FIELD_RESISTANCES (physical resistance, index 0)
in the player entity update fields.
Added UNIT_FIELD_RESISTANCES to the UF enum and all four expansion
JSON files with correct wire indices:
WotLK 3.3.5a: 99 (NPC_FLAGS=82 + emotestate + stat×5 + posstat×5 + negstat×5)
TBC 2.4.3: 185 (NPC_FLAGS=168 + same relative layout)
Classic 1.12: 154 (NPC_FLAGS=147 + emotestate + stat×5, no posstat/negstat)
Turtle WoW: 154 (same as Classic)
Stats panel uses server armor when > 0, falls back to summed item-query
armor otherwise. Armor rating resets to 0 on world entry and is updated
from both CREATE_OBJECT and VALUES update blocks.
Warriors (rage) and rogues/druids (energy) had no power bar rendered
when maxPower was 0 — the server often omits UNIT_FIELD_MAXPOWER1 for
rage-based classes since rage starts at 0. Rage and energy always cap
at 100, so default maxPower to 100 when the server sends 0 for those
power types. Also unified bar height to 18px (matching health bar) and
added proper power-type colour to the target frame bar (was always blue).
The old `// lgtm [cpp/...]` comments used a space (invalid syntax) and
were placed on preceding lines rather than inline with the flagged code.
GitHub's CodeQL action v3 requires `// codeql[query-id]` on the same
line as the flagged expression. All four alert sites updated:
- world_socket.cpp: encryptCipher/decryptCipher.init() (protocol RC4)
- warden_module.cpp: decryptRC4() call (Warden protocol RC4)
- warden_crypto.cpp: initRC4() calls (Warden stream cipher init)
- game_handler.cpp: wardenLoadedModule_->load() (MD5+RC4 via Warden)
All uses are protocol-mandated by Blizzard's WoW/Warden spec and cannot
be replaced without breaking server interoperability.
TBC 2.4.3 SMSG_ITEM_QUERY_SINGLE_RESPONSE differs from WotLK: no Flags2,
no BuyCount, statsCount-many stat pairs (not always 10), and no
ScalingStatDistribution/ScalingStatValue. Without this override,
TbcPacketParsers fell back to the WotLK parser and misread stats/armor
with a cascading 16-byte offset. Classic (Vanilla) was already safe via
its own independent ClassicPacketParsers::parseItemQueryResponse().
SMSG_ITEM_QUERY_SINGLE_RESPONSE in WotLK 3.3.5a sends BuyCount as a
separate field before BuyPrice. The parser was skipping only one of the
two fields, shifting every subsequent read by 4 bytes. This caused
statsCount to be read from ContainerSlots (always 0 for non-bags) so
no stat pairs were parsed, and the armor field was read from the wrong
offset in the damage block — leaving all stat bonuses and armor at 0.
Also moved armor above stat bonuses in the item tooltip to match WoW's
canonical tooltip layout (armor, then green stat lines).
Previously other players jittered because the entity sat frozen at its
destination between movement packets, then snapped to the new start
position on the next packet (stop-pop-stop-pop at ~10 Hz).
Entity interpolation now tracks a smoothed velocity and dead-reckons
past the end of each packet window, so the entity keeps gliding at the
estimated speed until the next server update arrives. Movement stops
only after two consecutive intervals with no new packet (entity has
genuinely stopped).
Also replaced the raw packet-delta duration with an exponential moving
average (EMA) per player. A single slow or fast packet no longer spikes
the playback speed; the EMA converges on the actual send rate (~100 ms)
and absorbs jitter without adding a fixed input-latency penalty.
Two bugs caused the client to look like a bot to server GMs:
1. Strafe animation played during forward+strafe (W+A) instead of the
walk/run animation. Added pureStrafe guard so strafe animations only
play when exclusively strafing (no forward key or auto-run active).
2. CMSG_MOVE_SET_FACING was never sent on mouse-look turns. The server
predicts movement from the last known facing; without SET_FACING the
heartbeat position appeared to teleport each time the player changed
direction. Now sent at up to 10 Hz whenever facing changes >3°,
skipped while keyboard-turning (handled server-side by TURN flags).