Parse M2RibbonEmitter data (WotLK format) from M2 files — bone index,
position, color/alpha/height tracks, edgesPerSecond, edgeLifetime,
gravity. Add CPU-side trail simulation per instance (edge birth at bone
world position, lifetime expiry, gravity droop). New m2_ribbon.vert/frag
shaders render a triangle-strip quad per emitter using the existing
particleTexLayout_ descriptor set. Supports both alpha-blend and additive
pipeline variants based on material blend mode. Fixes invisible spell
trail effects (~5-10%% of spell visuals) that were silently skipped.
- Animation stutter: skip playAnimation(Run) for the local player in the
server movement callback — the player renderer state machine already manages
it; resetting animTime on every movement packet caused visible stutter
- Resolution crash: reorder swapchain recreation so old swapchain is only
destroyed after confirming the new build succeeded; add null-swapchain
guard in beginFrame to survive the retry window
- Memory cap: reduce cache budget from 80% uncapped to 50% hard-capped at
16 GB to prevent excessive RAM use on high-memory systems
- Spell tooltip: suppress "Drag to action bar / Double-click to cast" hints
when the tooltip is shown from the action bar (showUsageHints=false)
- M2 collision: add watermelon/melon/squash/gourd to foliage (no-collision);
exclude chair/bench/stool/seat/throne from smallSolidProp so invisible chair
bounding boxes no longer trap the player
- 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
- 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
Read the ambient color from the MOHD chunk (BGRA uint32) and store it
on WMOModel as a normalized RGB vec3. Pass it through ModelData into
the per-batch WMOMaterialUBO (replacing the unused pad[3] bytes, keeping
the struct at 64 bytes). The GLSL interior branch now floors vertex
colors against the WMO ambient instead of a hardcoded 0.5, so dungeon
interiors respect the artist-specified ambient tint from the WMO root
rather than always clamping to grey.
Parse MOPY per-triangle flags in WMO groups and exclude detail/decorative
triangles (flag 0x04) from collision detection. This prevents invisible
walls from objects like gears and railings in WMO interiors.
Add WotLK area trigger IDs 2173/2175 to extended-range tram triggers.
- Remove bogus 2-byte skip after materialId in MLIQ parser that shifted
all vertex heights and tile flags by 2 bytes (garbage data)
- Skip liquid loading for interior WMO groups (flag 0x2000) to prevent
indoor water from rendering as outdoor canal water
- Clear movement inputs on teleport/portal to prevent auto-running after
zone transfer (held keys persist through loading screen)
- Fix WDT chunk magic constants to big-endian ASCII (matching ADTLoader)
- Add minimum effective size for box area triggers (90 units, like sphere 45-unit radius)
- Add areaTriggerSuppressFirst_ flag to prevent portal ping-pong on map transfer
- Add WMORenderer::clearAll() to clear models/textures on map change (prevents GPU crash)
- Increase WMO texture cache default to 8GB
- Fix setMapName called after loadTestTerrain so WMO renderer exists
- Save/restore player position around CMSG_AREATRIGGER to prevent bad DB persistence
- Force binary loading for SkillLine.dbc (CSV exports are garbled)
- Handle quoted numeric values in DBC CSV parser
- Fix bank slot drag-and-drop to use mouse-release detection instead
of click detection, preventing item drops on wrong slots
- Fix action bar item drop to use hoveredOnRelease for consistency
Two bugs in loadPatchArchives():
1. isLetterPatch detection was inverted (rfind != 0 is false for all
"patch-*" entries), making the disable flags non-functional
2. Patch file lookup used exact std::filesystem::exists() which is
case-sensitive on Linux — Patch-A.MPQ wouldn't match patch-a.mpq
Now scans the data directory once and builds a case-insensitive lookup
map, so any case variant (Patch-A.MPQ, patch-a.mpq, PATCH-A.MPQ) is
found correctly.
Warden: copy/skip pair order was reversed — format is [copy][data][skip]
per MaNGOS/TrinityCore, not [skip][copy][data]. All copy sizes read as 0,
causing module load failure and server disconnect.
DBC: when binary DBCs aren't available (no MPQ extraction), fall back to
expansion CSV files even for visual DBCs (CreatureDisplayInfo, CharSections,
ItemDisplayInfo, etc.) instead of failing with "DBC not found".
- Fix HeightMap::getHeight() to use interleaved 17-wide row layout
matching MCVT storage (was using wrong 9-wide contiguous indexing)
- Guard terrain bump mapping normalize against zero-length vectors
to prevent NaN propagation and GPU faults
Two WMO rendering fixes:
1. Glass batch merging: BatchKey didn't include isWindow, so window and
non-window batches sharing the same texture got merged together. If
the window batch was processed first, the entire merged batch (lamp
base, iron frame, etc.) rendered as transparent glass. Add isWindow
to the batch key so glass/non-glass batches stay separate.
2. LOD group culling: WMO groups named with "LOD" are distance-only
impostor geometry (e.g. cathedral tower extension, hill tower). They
should only render beyond 200 units. Store raw MOGN chunk for
offset-based name lookup, detect "lod" in group names during load,
and skip LOD groups in both main and shadow passes when camera is
within range.
- Fix MLIQ vertex stride: each vertex is 8 bytes (4 flow + 4 height), not 4
- Use MLIQ tile flags to mask out tiles with no liquid (bridges, covered areas)
- Disable wave displacement on WMO water to prevent edge slosh artifacts
- Convert screen-space depth to vertical depth for shoreline foam and water
transparency, preventing false shoreline effects on occluding geometry
- Add underwater blue fog overlay and scene fog shift (terrain water only)
- Add getNearestWaterHeightAt to avoid false underwater detection from
elevated WMO water surfaces
- Tint refracted scene toward water color to mask occlusion edge artifacts
- Lower WMO water by 1 unit to match terrain water level
- reduce per-tile ground clutter generation pressure and enforce tighter caps to avoid spikes
- remove expensive detail dedupe scans from the hot render path
- add progressive/lazy clutter updates around player movement to smooth frame pacing
- lower noisy runtime INFO logging to DEBUG/throttled paths
- keep terrain/game screen updates responsive while preserving existing behavior
Hide NPC cloak/object-skin mesh when no cape texture resolves by using a transparent texture fallback, preventing skin-texture bleed on cloaks. Tighten NPC equipment region compositing by slot and add safe humanoid geoset selection to avoid robe-over-pants conflicts and odd pants texturing.
Reduce login/runtime hitching by deferring non-critical world-system initialization across frames, lowering per-frame transport doodad spawn budget, and demoting high-volume transport/MO_TRANSPORT diagnostics to debug. Gate M2 glow diagnostics behind WOWEE_M2_GLOW_DIAG and make zone music prewarm opt-in via WOWEE_PREWARM_ZONE_MUSIC.
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.
Batches whose named texture fails to load now render invisible instead of
white (the swampreeds01a.blp case causing a white shell around aquatic plants).
Also implements proper M2 opacity plumbing:
- Parse texture weight tracks (M2Track<fixed16>) and color animation alpha
tracks (M2Color.alpha) to resolve per-batch opacity at load time
- Skip batches with batchOpacity < 0.01 in the render loop
- Apply M2Texture.flags (bit0=WrapS, bit1=WrapT) to GL sampler wrap mode
- Upload both UV sets (texCoords[0] and texCoords[1]) and select via
textureUnit uniform, so batches referencing UV set 1 render correctly
- Fix SMSG_QUESTGIVER_REQUEST_ITEMS: read emoteDelay(u32)+emoteId(u32)+autoFinish(u8)
instead of 5 uint32s — the 11-byte over-read corrupted requiredMoney, itemCount,
and all item data (itemId/count/displayInfoId)
- Fix garbled CSV fallback in asset_manager: return nullptr instead of silently
returning garbled DBC data when binary fallback is unavailable
- Add NPC spawn diagnostics: log when UNIT blocks have displayId=0 (wrong field index)
and when spawn callback fires with displayId + position
- Improve getModelPathForDisplayId failure logging: distinguish displayDataMap_ miss
vs modelIdToPath_ miss, and include map sizes for context
M2 models like OrgrimmarFloatingEmbers and OrgrimmarSmokeEmitter have a
simple box mesh (24 verts, 36 indices) meant only to define particle
emitter bounds. Their blendMode was 0 (opaque), causing them to render
as large grey boxes. Detect these by checking for box geometry with
particle emitters and large bounds (>5 units), then mark as invisible.
Also add ANTIPORTAL and batch-disable flag checks to WMO group filtering.
- Fix FBlock color keys from 3-byte BGR to 4-byte RGBA (CImVector) to
prevent garbled purple/red colors from byte misalignment
- Add circular soft-edge falloff in particle fragment shader (GL_POINTS
rendered as squares by default)
- Apply default gravity (4.0 spray, 1.5 mist) when M2 gravity is 0 since
bone animation from .anim files isn't loaded yet
- Add drift velocity to speed=0 emitters so particles spread as mist
instead of clustering at static bone positions
- Run particle updates for all nearby instances, not just those in
boneWorkIndices_, to prevent particles freezing when bone culled
- Wrap animation time for particle models to keep emission tracks looping
- Cap particle scale to 1.5 and reduce point size multiplier (800→400)
- Desaturate FBlock colors 70% toward white for natural water appearance
- Reduce additive blend alpha to 5% and volume particles to 2%
Corrected CharGeosets group assignments verified via vertex bounding boxes:
- Group 4 (401+) = gloves/forearms, Group 5 (501+) = boots/shins,
Group 8 (801+) = sleeves (chest-controlled), Group 9 = kneepads,
Group 13 (1301+) = pants/trousers, Group 20 (2002) = bare feet
- Changed bare shin default from 501 to 502 for better width match
with thigh mesh (0.39 vs 0.32, thighs are 0.42)
- Added clearCompositeCache() to prevent stale composite textures
from being reused across equipment changes
- Fixed character preview geoset defaults to match corrected mapping
Implement full mail send/receive: SMSG_SHOW_MAILBOX, CMSG_GET_MAIL_LIST,
SMSG_MAIL_LIST_RESULT, CMSG_SEND_MAIL, SMSG_SEND_MAIL_RESULT, mail take
money/item/delete/mark-as-read, and inbox/compose UI windows.
Fix periodic stuttering in Stormwind caused by synchronous per-line disk
flushes in the logger — remove fileStream.flush() and std::endl, downgrade
high-volume per-packet/per-model/per-texture LOG_INFO to LOG_DEBUG.
Remove HDPackManager, expansion overlay manifests, and BLP size-comparison
logic. Assets now resolve through a single manifest with a simple override
directory (Data/override/) for future HD upgrades.
- Parse MCLQ sub-chunks in vanilla ADTs for water rendering (WotLK uses MH2O)
- Load TaxiPathNode.dbc for MO_TRANSPORT world-coordinate paths (vanilla boats)
- Parse data[] from SMSG_GAMEOBJECT_QUERY_RESPONSE (taxiPathId for transports)
- Support vanilla M2 particle emitters (504-byte struct, different from WotLK 476)
- Add character preview texture diagnostic logging
- Fix disconnect handling on character screen (show error only when no chars)
CharSections fields were mapped incorrectly (Variation/Color at 4-5,
textures at 6-8) — corrected to textures at 4-6, Flags at 7,
Variation at 8, Color at 9. Fixed in both dbc_layout.cpp and all
expansion JSON configs. Also fix Warden HASH_REQUEST to SHA1 over
seed+moduleImage instead of just seed.
Two bugs fixed:
1. SMSG_INSPECT_RESULTS (0x115) was falling through to the inspect
handler, but this opcode is not the real inspect response. Removed
the case so only SMSG_INSPECT_TALENT (0x3F4) triggers the handler.
2. EmotesText DBC layout was missing "ID" field in all 4 expansion
JSON files, causing operator[] to return 0xFFFFFFFF instead of 0.
This broke the dbcId reverse lookup, so other players' emotes
always fell back to generic "performs an emote" text. Added ID
and OthersTargetTextID/OthersNoTargetTextID to all layouts.
Load third-person emote text templates (othersTarget/othersNoTarget)
from EmotesText.dbc fields 3 and 7 alongside existing sender text.
Build reverse lookup map from dbcId to EmoteInfo for incoming
SMSG_TEXT_EMOTE resolution. Other players now show proper emote
descriptions like "Player dances with Target" instead of generic
"Player performs an emote" text.
Expansion overlays allow each expansion to supplement the base asset data
via an assetManifest field in expansion.json, loaded at priority 50 (below
HD packs). The asset extractor gains --reference-manifest for delta-only
extraction. Also fixes CharSections field indices (VariationIndex=4,
ColorIndex=5, Texture1=6) across all DBC layout references.
Vanilla M2 tracks store absolute timestamps in the flat array (e.g.
1000-2000ms) but the renderer plays animTime from 0 to duration.
Normalize timestamps to 0-relative after slicing per-sequence ranges
so findKeyframeIndex matches correctly.
- Parse vanilla M2 animation tracks (flat arrays with M2Range indices)
instead of skipping them, fixing T-pose on all vanilla models
- Use C4Quaternion (float[4]) for vanilla bone rotations instead of
CompressedQuat (int16[4]) which produced garbage transforms
- Fix vanilla M2 attachment struct size (48 bytes, not 40) so weapons
attach to correct bones instead of model origin
- Route movement packets through expansion-specific packet parsers
instead of hardcoded WotLK format, fixing server-side position sync
- Fix Spell.dbc field indices for classic/turtle (Name=120, Rank=129,
IconID=117) - were pointing to Portuguese locale column (+7 offset)
- Change guild roster keybind from J to O (WoW default)
- Add guild opcodes for all expansions
- Route SMSG_UPDATE_OBJECT through polymorphic parsers for correct
vanilla format (uint8 updateFlags, 6 speeds vs WotLK uint16/9)
- Fix SMSG_DESTROY_OBJECT for vanilla (8 bytes, no isDeath field)
- Add MSG_MOVE_* handlers for other player movement relay
- Add ClassicPacketParsers::parseMessageChat with targetGuid read
and monster-type name handling
- Resolve chat sender names from player name cache before display
- Fix CSV DBC field 0 always treated as numeric ID (fixes 16+ garbled
Turtle CSVs including Map, AreaTable, Spell, CreatureDisplayInfo)
- Add CSV DBC validation: reject garbled CSVs (>80% zero IDs) and
fall back to binary DBC files
- Fix ItemDisplayInfo texture component field index (14+ not 15+)
for binary DBC with gender-aware suffix resolution
- Spawn other players as visible M2 models via creature callback
- Map name cache dedup prevents overwrites from duplicate CSV records
- Vanilla M2 bone struct (108 bytes) with 28-byte animation tracks
- Version-aware bone parsing (vanilla vs WotLK format detection)
- Fix CharSections.dbc field layout for vanilla (variation/color at 4-5)
- Remove broken CharSections.csv files (all fields marked as strings)
- Expansion data reload on profile switch (DBC cache clear, layout reload)
- Vanilla packet encryption (VanillaCrypt XOR-based header crypt)
- Extended character preview geoset range (0-99) for vanilla models
- DBC cache clear support in AssetManager
Replace hardcoded WotLK protocol constants with a data-driven architecture
supporting Classic 1.12.1, TBC 2.4.3, and WotLK 3.3.5a. Each expansion
has JSON profiles for opcodes, update fields, and DBC layouts, plus C++
polymorphic packet parsers for binary format differences (movement flags,
speed fields, transport data, spline format, char enum layout).
Key components:
- ExpansionRegistry: scans Data/expansions/*/expansion.json at startup
- OpcodeTable: logical enum <-> wire values loaded from JSON
- UpdateFieldTable: field indices loaded from JSON per expansion
- DBCLayout: schema-driven DBC field lookups replacing magic numbers
- PacketParsers: WotLK/TBC/Classic parsers with correct flag positions
- Multi-manifest AssetManager: layered manifests with priority ordering
- HDPackManager: overlay texture packs with expansion compatibility
- Auth screen expansion picker replacing hardcoded version dropdown