diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..b8dff462 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,53 @@ +# Changelog + +## [Unreleased] — changes since v1.8.1-preview (2026-03-23) + +### Performance +- Eliminate ~70 unnecessary sqrt ops per frame; constexpr reciprocals and cache optimizations +- Skip bone animation for LOD3 models; frustum-cull water surfaces +- Eliminate per-frame heap allocations in M2 renderer +- Convert entity/skill/DBC/warden maps to unordered_map; fix 3x contacts scan +- Eliminate double map lookups and dynamic_cast in render loops +- Use second GPU queue for parallel texture/buffer uploads +- Time-budget tile finalization to prevent 1+ second main-loop stalls +- Add Vulkan pipeline cache persistence for faster startup + +### Bug Fixes +- Fix spline parsing with expansion context; preload DBC caches at world entry +- Fix NPC/player attack animation to use weapon-appropriate anim ID +- Fix equipment visibility and follow-target run speed +- Fix inspect (packed GUID) and client-side auto-walk for follow +- Fix mail money uint64, other-player cape textures, zone toast dedup, TCP_NODELAY +- Guard spline point loop against unsigned underflow; guard hexDecode/stoi/stof +- Fix infinite recursion in toLowerInPlace and operator precedence bugs +- Fix 3D audio coords for PLAY_OBJECT_SOUND; correct melee swing sound paths +- Prevent Vulkan sampler exhaustion crash; skip pipeline cache on NVIDIA +- Skip FSR3 frame gen on non-AMD GPUs to prevent driver crash +- Fix chest GO interaction (send GAMEOBJ_USE+LOOT together) +- Restore WMO wall collision threshold; fix off-screen bag positions +- Guard texture log dedup sets with mutex for thread safety +- Fix lua_pcall return check in ACTIONBAR_PAGE_CHANGED + +### Features +- Render equipment on other players (helmets, weapons, belts, wrists, shoulders) +- Target frame right-click context menu +- Crafting sounds and Create All button +- Server-synced bag sort +- Log GPU vendor/name at init + +### Security +- Add path traversal rejection and packet length validation + +### Code Quality +- Packet API: add readPackedGuid, writePackedGuid, writeFloat, getRemainingSize, + hasRemaining, hasData, skipAll (replacing 1300+ verbose expressions) +- GameHandler helpers: isInWorld, isPreWotlk, guidToUnitId, lookupName, + getUnitByGuid, fireAddonEvent, withSoundManager +- Dispatch table: registerHandler, registerSkipHandler, registerWorldHandler, + registerErrorHandler (replacing 120+ lambda wrappers) +- Shared ui_colors.hpp with named constants replacing 200+ inline color literals +- Promote 50+ static const arrays to constexpr across audio/core/rendering/UI +- Deduplicate class name/color functions, enchantment cache, item-set DBC keys +- Extract settings tabs, GameHandler::update() phases, loadWeaponM2 into methods +- Remove 12 duplicate dispatch registrations and C-style casts +- Extract toHexString, toLowerInPlace, duration formatting, Lua return helpers diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..6e5aebae --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to Wowee + +## Build Setup + +See [BUILD_INSTRUCTIONS.md](BUILD_INSTRUCTIONS.md) for full platform-specific details. +The short version: CMake + Make on Linux/macOS, MSYS2 on Windows. + +``` +cmake -B build -DCMAKE_BUILD_TYPE=Debug +make -C build -j$(nproc) +``` + +## Code Style + +- **C++17**. Use `#pragma once` for include guards. +- Namespaces: `wowee::game`, `wowee::rendering`, `wowee::ui`, `wowee::core`, `wowee::network`. +- Conventional commit messages in imperative mood: + - `feat:` new feature + - `fix:` bug fix + - `refactor:` code restructuring with no behavior change + - `perf:` performance improvement +- Prefer `constexpr` over `static const` for compile-time data. +- Mark functions whose return value should not be ignored with `[[nodiscard]]`. + +## Pull Request Process + +1. Branch from `master`. +2. Keep commits focused -- one logical change per commit. +3. Describe *what* changed and *why* in the PR description. +4. Ensure the project compiles cleanly before submitting. +5. Manual testing against a WoW 3.3.5a server (e.g. AzerothCore/ChromieCraft) is expected + for gameplay-affecting changes. + +## Architecture Overview + +See [docs/architecture.md](docs/architecture.md) for the full picture. Key namespaces: + +| Namespace | Responsibility | +|---|---| +| `wowee::game` | Game state, packet handling (`GameHandler`), opcode dispatch | +| `wowee::rendering` | Vulkan renderer, M2/WMO/terrain, sky system | +| `wowee::ui` | ImGui windows and HUD (`GameScreen`) | +| `wowee::core` | Coordinates, math, utilities | +| `wowee::network` | Connection, `Packet` read/write API | + +## Packet Handlers + +The standard pattern for adding a new server packet handler: + +1. Define a `struct FooData` holding the parsed fields. +2. Write `void GameHandler::handleFoo(network::Packet& packet)` to parse into `FooData`. +3. Register it in the dispatch table: `registerHandler(LogicalOpcode::SMSG_FOO, &GameHandler::handleFoo)`. + +Helper variants: `registerWorldHandler` (requires `isInWorld()`), `registerSkipHandler` (discard), +`registerErrorHandler` (log warning). + +## Testing + +There is no automated test suite. Changes are verified by manual testing against +WoW 3.3.5a private servers (primarily ChromieCraft/AzerothCore). Classic and TBC +expansion paths are tested against their respective server builds. + +## Key Files for New Contributors + +| File | What it does | +|---|---| +| `include/game/game_handler.hpp` | Central game state and all packet handler declarations | +| `src/game/game_handler.cpp` | Packet dispatch registration and handler implementations | +| `include/network/packet.hpp` | `Packet` class -- the read/write API every handler uses | +| `include/ui/game_screen.hpp` | Main gameplay UI screen (ImGui) | +| `src/rendering/m2_renderer.cpp` | M2 model loading and rendering | +| `docs/architecture.md` | High-level system architecture reference | diff --git a/src/game/world_packets.cpp b/src/game/world_packets.cpp index db3009bb..37e7d139 100644 --- a/src/game/world_packets.cpp +++ b/src/game/world_packets.cpp @@ -5,6 +5,7 @@ #include "auth/crypto.hpp" #include "core/logger.hpp" #include +#include #include #include #include @@ -1478,29 +1479,35 @@ bool MessageChatParser::parse(network::Packet& packet, MessageChatData& data) { return false; } - std::string tmp; - tmp.resize(len); + // Stack buffer for typical messages; heap fallback for oversized ones. + static constexpr uint32_t kStackBufSize = 256; + std::array stackBuf; + std::string heapBuf; + char* buf; + if (len <= kStackBufSize) { + buf = stackBuf.data(); + } else { + heapBuf.resize(len); + buf = heapBuf.data(); + } + for (uint32_t i = 0; i < len; ++i) { - tmp[i] = static_cast(packet.readUInt8()); + buf[i] = static_cast(packet.readUInt8()); } - if (tmp.empty() || tmp.back() != '\0') { + if (buf[len - 1] != '\0') { packet.setReadPos(start); return false; } - tmp.pop_back(); - if (tmp.empty()) { - packet.setReadPos(start); - return false; - } - for (char c : tmp) { - unsigned char uc = static_cast(c); + // len >= 2 guaranteed above, so len-1 >= 1 — string body is non-empty. + for (uint32_t i = 0; i < len - 1; ++i) { + auto uc = static_cast(buf[i]); if (uc < 32 || uc > 126) { packet.setReadPos(start); return false; } } - out = std::move(tmp); + out.assign(buf, len - 1); return true; };