- moved chat panel logic out of `game_screen` into `chat_panel`
- added chat_panel.hpp and chat_panel.cpp
- updated game_screen.hpp and game_screen.cpp to integrate new `ChatPanel` component
- updated build config in CMakeLists.txt to include new UI module sources
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 ✅.
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).
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.
Remove all OpenGL/GLEW code and dependencies. The Vulkan renderer has
been the sole active backend for months; these files were dead code.
Deleted (8 files, 641 lines):
- rendering/mesh.cpp+hpp: OpenGL VAO/VBO/EBO wrapper (never instantiated)
- rendering/shader.cpp+hpp: OpenGL GLSL compiler (replaced by VkShaderModule)
- rendering/scene.cpp+hpp: Scene graph holding Mesh objects (created but
never populated — all rendering uses Vulkan sub-renderers directly)
- rendering/video_player.cpp+hpp: FFmpeg+GL texture uploader (never
included by any other file — login video feature can be re-implemented
with VkTexture when needed)
Cleaned up:
- renderer.hpp: remove Scene forward-decl, getScene() accessor, scene member
- renderer.cpp: remove scene.hpp/shader.hpp includes, Scene create/destroy
- application.cpp: remove stale "GL/glew.h removed" comment
- CMakeLists.txt: remove find_package(OpenGL/GLEW), source/header entries,
and target_link_libraries for OpenGL::GL and GLEW::GLEW
- PKGBUILD: remove glew dependency
- BUILD_INSTRUCTIONS.md: remove glew from all platform install commands
- Delete 4 legacy GLSL 330 shaders (basic.vert/frag, terrain.vert/frag)
left over from OpenGL→Vulkan migration — Vulkan equivalents exist as
*.glsl files compiled to SPIR-V by the build system
- Delete orphaned mpq_manager.hpp/cpp (694 lines) — not in CMakeLists,
not included by any file, unreferenced StormLib integration attempt
- Add comments to water.frag.glsl wave constants explaining the
multi-octave noise design: non-axis-aligned directions prevent tiling,
frequency increases and amplitude decreases per octave for natural
water appearance
Complete rewrite — the previous version extensively referenced OpenGL
(glClearColor, VAO/VBO/EBO, GLSL shaders) throughout all sections.
The project has used Vulkan exclusively for months.
Key changes:
- Replace all OpenGL references with Vulkan equivalents (VkContext,
VMA, descriptor sets, pipeline cache, SPIR-V shaders)
- Update system diagram to show actual sub-renderer hierarchy
(TerrainRenderer, WMORenderer, M2Renderer, CharacterRenderer, etc.)
- Document GameHandler SOLID decomposition (8 domain handlers +
EntityController + GameServices dependency injection)
- Add Warden 4-layer architecture section
- Add audio system section (miniaudio, 5 sound managers)
- Update opcode count from "100+" to 664+
- Update UI section: talent screen and settings are implemented (not TODO)
- Document threading model (async terrain, GPU upload queue, normal maps)
- Fix dependencies list (Vulkan SDK, VMA, vk-bootstrap, Unicorn, FFmpeg)
- Add container builds and CI platforms
- Remove stale "TODO" items for features that are complete
- CONTRIBUTING.md: C++17 → C++20 (matches CMakeLists.txt)
- TROUBLESHOOTING.md: fix log path (~/.wowee/logs/ → logs/wowee.log)
- docs/authentication.md: remove stale "next milestone" (char enum
and world entry have been working for months)
- docs/srp-implementation.md: update session key status (RC4 encryption
is implemented), fix file reference to actual src/auth/srp.cpp
- docs/packet-framing.md: remove stale "next steps" (realm list is
fully implemented), update status with tested servers
- docs/WARDEN_IMPLEMENTATION.md: fix file list — handler is in
warden_handler.cpp not game_handler.cpp, add warden_memory.hpp/cpp
- docs/WARDEN_QUICK_REFERENCE.md: fix header/source paths (include/
not src/), add warden_handler and warden_memory
- docs/quickstart.md: fix clone command (--recurse-submodules, WoWee
not wowee), remove obsolete manual ImGui clone step, fix log path
- docs/server-setup.md: update version to v1.8.9-preview, date to
2026-03-30, add all supported expansions
- assets/textures/README.md: remove broken doc references
(TURTLEHD_IMPORT.md, TEXTURE_MANIFEST.txt), update integration
status to reflect working PNG override pipeline
GETTING_STARTED.md:
- Fix keybinding table: T→N for talents, Q→L for quest log, W→M for
world map, add missing keys (C, I, O, J, Y, K), remove nonexistent
minimap toggle
- Fix extract_assets.ps1 example param (-WowDirectory → positional)
- Fix Data/ directory tree to match actual manifest layout
- Fix log path: ~/.wowee/logs/ → logs/wowee.log (local directory)
EXPANSION_GUIDE.md:
- Add Turtle WoW 1.17 to supported expansions
- Update code examples to use game_utils.hpp helpers
(isActiveExpansion/isClassicLikeExpansion/isPreWotlk) instead of
removed ExpansionProfile::getActive() and GameHandler::getInstance()
- Update packet parser references (WotLK is default in domain handlers,
not a separate packet_parsers_wotlk.cpp file)
- Update references section with game_utils.hpp
- README: update status date to 2026-03-30, version to v1.8.9-preview,
add container builds line, update current focus to code quality
- CHANGELOG: move v1.8.1 entries to their own section, add v1.8.2-v1.8.9
unreleased section covering architecture (GameHandler decomposition,
Docker cross-compilation), bug fixes (7 UB/overflow/safety fixes),
and code quality (30+ constants, 55+ comments, 8 DRY extractions)
- docs/status.md: update last-updated date to 2026-03-30
- Explain icon load deferral strategy: returning null without caching
allows retry next frame when budget resets, rather than permanently
blacklisting icons that were deferred due to rate-limiting
- Explain DBC field fallback logic: hard-coded WotLK indices are a
safety net when dbc_layouts.json is missing; fieldCount >= 200
distinguishes WotLK (234 fields) from Classic (148)
- Explain M2 version 264 threshold (WotLK stores submesh/bone data
in external .skin files; Classic/TBC embed it in the M2)
- Explain M2 texture types 1 and 6 (skin and hair/scalp; empty
filenames resolved via CharSections.dbc at runtime)
- Explain 0x20 anim flag (embedded data; when clear, keyframes live
in external {Model}{SeqID}-{Var}.anim files)
- Explain geoset ID encoding (group × 100 + variant from
ItemDisplayInfo.dbc; e.g. 801 = sleeves variant 1)
- 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
- 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
- inventory_screen: extract renderClassRestriction() and
renderRaceRestriction() from two identical 40-line blocks in quest
info and item info tooltips. Both used identical bitmask logic,
strncat formatting, and player-class/race validation (-49 lines net)
- world_map: add why-comment on AreaTable.dbc fallback field indices —
explains that incorrect indices silently return wrong data and why
the WotLK stock layout (ID=0, Parent=2, ExploreFlag=3) is chosen
as the safest default
- input: fix undefined behavior in SDL mouse button loop — SDL_BUTTON(0)
computes (1 << -1) which is UB. Start loop at 1 since SDL button
indices are 1-based (SDL_BUTTON_LEFT=1, RIGHT=3, MIDDLE=2)
- big_num: guard BN_bn2hex/BN_bn2dec against nullptr return on
OpenSSL allocation failure — previously constructed std::string
from nullptr which is undefined behavior
- 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
- 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)
- Name kHalfMinutesPerDay (2880) replacing 8 bare literals across
time conversion, modulo clamping, and midnight wrap arithmetic.
Add why-comment: Light.dbc stores time-of-day as half-minutes
(24h × 60m × 2 = 2880 ticks per day cycle)
- Replace hardcoded 3.14159f with glm::two_pi<float>() in sun
direction angle calculations (2 occurrences)
- terrain_manager: extract kRand16Max (65535.0f) from 8 duplicated
random normalization expressions — 16-bit mask to [0..1] float
- terrain_manager: add static_assert verifying packed alpha unpacks
to full alpha map size (ALPHA_MAP_PACKED * 2 == ALPHA_MAP_SIZE)
- camera_controller: name kCameraClipEpsilon (0.1f) with why-comment
preventing character model clipping at near-minimum distance
- 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)
- vk_pipeline: extract kColorWriteAll constant from 4 duplicated RGBA
bitmask expressions across blend mode functions, with why-comment
- frustum: name kMinNormalLenSq epsilon (1e-8) with why-comment —
prevents division by zero on degenerate planes
- dbc_loader: add why-comment on DBC field width validation — all
fields are fixed 4-byte uint32 per format spec
- pin_auth: replace 0x30 hex literal with '0' char constant, add
why-comment on ASCII encoding for server HMAC compatibility
- m2_loader: define kM2SeqFlagEmbeddedData (0x20) with why-comment —
when clear, keyframe data lives in external .anim files and M2 offsets
are file-relative (reading them from M2 produces garbage). Replaces
3 bare hex literals across parseAnimTrack and ribbon emitter parsing
- audio_engine: replace empty for-loop iterator advance with
std::advance() for clarity
- 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)
- weather: remove duplicate setZoneWeather(15) for Dustwallow Marsh —
second call silently overwrote the first with different parameters
- weather: replace duplicate static RNG in getRandomPosition() with
shared weatherRng() to avoid redundant generator state
- starfield: extract day/night cycle thresholds into named constants
(kDuskStart/kNightStart/kDawnStart/kDawnEnd/kFadeDuration)
- skybox: replace while-loop time wrapping with std::fmod — avoids
O(n) iterations on large time jumps
- asset_manager: add size guard before fsPath.substr(size-4) in
tryLoadPngOverride — resolveFile could theoretically return a
path shorter than the extension
- wmo_loader: name kDoodadNameIndexMask (0x00FFFFFF) with why-comment
explaining the 24-bit name index / 8-bit flags packing and MODN
string table reference
- window: add why-comment on LOG_WARNING usage during shutdown —
intentionally elevated so teardown progress is visible at default
log levels for crash diagnosis
- Fix move constructor and move assignment: set other.ownsSampler_ to
false after transfer (was incorrectly set to true, leaving moved-from
object claiming ownership of a null sampler)
- Fix destroy(): reset ownsSampler_ to false after clearing sampler
handle (was set to true, inconsistent with null handle state)
- Extract finalizeSampler() from 3 duplicated cache-or-create blocks
in createSampler() overloads and createShadowSampler() (-24 lines)
- Add SPIR-V alignment why-comment in vk_shader.cpp
- 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)
- Name portal spin wrap value as kTwoPi constant
- Name particle animTime wrap as kParticleWrapMs (3333ms) with
why-comment: covers longest known emission cycle (~3s torch/campfire)
while preventing float precision loss over hours of runtime
- Add FBlock interpolation documentation: explain what FBlocks are
(particle lifetime curves) and note that float/vec3 variants share
identical logic and must be updated together
- 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
- 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
- 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
- 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)
- 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
- Dockerfile: fix LLVM apt repo codename (jammy → noble) for ubuntu:24.04
- build-linux.sh: add missing mkdir -p /wowee-build-src before tar extraction
- Dockerfile: remove dead ENV OSXCROSS_VERSION=1.5 and its unset
- CMakeLists: scope -undefined dynamic_lookup to wowee target only
- GameServices: remove redundant game:: qualifier inside namespace game
- application.cpp: zero out gameServices_ after gameHandler reset in shutdown