From c2e1b557ccc9234ea15f8bebf948834208a0fedb Mon Sep 17 00:00:00 2001 From: James Huston Date: Tue, 2 Dec 2025 22:55:11 -0500 Subject: [PATCH] fix(waypoints): Display configuration and client refresh - Fix waypoint markers showing as human models in 11.2.5 client - Use Elven Wisp (displayId 1824) which renders correctly - Add runtime display config via Lua shared data (no recompile needed) - Add client refresh (DestroyForNearbyPlayers + UpdateObjectVisibilityOnCreate) - Graceful MySQL error handling (no crash on bad MCP queries) - Fix displayId arg parsing in VisualizeWaypointPath - Add documentation for display IDs and configuration --- .../05_WAYPOINT_DISPLAY_CONFIG.md | 108 ++++++++++++++++++ .../database/Database/MySQLConnection.cpp | 12 +- .../methods/TrinityCore/CreatureMethods.h | 12 +- .../Movement/Waypoints/WaypointManager.cpp | 14 ++- 4 files changed, 130 insertions(+), 16 deletions(-) create mode 100644 araxiaonline/araxia_docs/admin_npcdata/05_WAYPOINT_DISPLAY_CONFIG.md diff --git a/araxiaonline/araxia_docs/admin_npcdata/05_WAYPOINT_DISPLAY_CONFIG.md b/araxiaonline/araxia_docs/admin_npcdata/05_WAYPOINT_DISPLAY_CONFIG.md new file mode 100644 index 0000000000..ff54faa50a --- /dev/null +++ b/araxiaonline/araxia_docs/admin_npcdata/05_WAYPOINT_DISPLAY_CONFIG.md @@ -0,0 +1,108 @@ +# Waypoint Display Configuration + +## Overview + +Waypoint markers are visual indicators spawned at each node of a creature's patrol path. They use the `VISUAL_WAYPOINT` creature entry (ID 1) and are only visible to GMs. + +## Display IDs for 11.2.5 Client + +**Working Display IDs:** +| ID | Name | Description | +|----|------|-------------| +| 1824 | Elven Wisp | Blue floating wisp - **recommended for waypoints** | +| 169 | Rock Elemental | Small rock creature | +| 31366 | Green Circle | Targeting circle - used for spawn markers | + +**Non-Working Display IDs:** +| ID | Name | Issue | +|----|------|-------| +| 17188 | Blue Skull | Shows as human model in 11.2.5 | +| 17519 | Red Orb | Shows as human model in 11.2.5 | +| 10045 | Wisp | Shows as human model in 11.2.5 | + +## Runtime Configuration via Lua Shared Data + +Display IDs are configurable at runtime without recompiling: + +```lua +-- In admin_handlers.lua +local DEFAULT_DISPLAYS = { + waypoint_marker = 1824, -- Elven Wisp + waypoint_highlight = 1824, -- Same, differentiated by scale + spawn_marker = 31366, -- Green targeting circle +} +``` + +### Changing via MCP + +``` +-- Set waypoint marker to rock elemental +mcp0_shared_data_write(key="config_display_waypoint_marker", value="169") + +-- Then reload Lua and hide/show waypoints +.reload eluna +``` + +### Pattern for Configurable Values + +```lua +local function GetDisplayId(key) + local sharedKey = "config_display_" .. key + local value = GetSharedData(sharedKey) + if value and value ~= "" then + local num = tonumber(value) + if num then return num end + end + return DEFAULT_DISPLAYS[key] or 1824 +end +``` + +## C++ Client Refresh Pattern + +**Critical:** `SetDisplayId()` alone does NOT update creatures already visible to the client. You must force a refresh: + +```cpp +summon->SetDisplayId(displayId, true); +summon->SetObjectScale(0.5f); + +// Force client to see the change +summon->DestroyForNearbyPlayers(); +summon->UpdateObjectVisibilityOnCreate(); +``` + +Without these calls, the client shows cached appearance from when the creature spawned. + +## Files Modified + +### C++ Changes +- `src/server/game/Movement/Waypoints/WaypointManager.cpp` + - Added client refresh after SetDisplayId + - Default display: 1824 (Elven Wisp) + +- `src/server/game/LuaEngine/methods/TrinityCore/CreatureMethods.h` + - Fixed displayId arg parsing for `VisualizeWaypointPath(player, displayId)` + +- `src/server/database/Database/MySQLConnection.cpp` + - Graceful handling of `ER_BAD_FIELD_ERROR`, `ER_NO_SUCH_TABLE`, `ER_PARSE_ERROR` + - No longer crashes server on bad MCP queries + +### Lua Changes +- `lua_scripts/admin_handlers.lua` + - Added `DEFAULT_DISPLAYS` table + - Added `GetDisplayId()`, `SetDisplayIdConfig()`, `InitDisplayConfigs()` + - AMS handlers: `SET_DISPLAY_CONFIG`, `GET_DISPLAY_CONFIGS` + +### Database Changes +- `creature_template_model` for `CreatureID = 1` set to `CreatureDisplayID = 1824` + +## Lessons Learned + +1. **Client version matters** - Display IDs from older WoW versions may not render correctly in modern clients + +2. **Test displays in-game** - Use `.npc add ` to spawn creatures and verify their appearance before using displayIds + +3. **Client caches creature appearance** - Must force refresh with `DestroyForNearbyPlayers()` + `UpdateObjectVisibilityOnCreate()` + +4. **Prefer Lua for config values** - Allows testing without 5+ minute recompiles. Use MCP to change values at runtime. + +5. **Shared data clears on restart** - `InitDisplayConfigs()` re-initializes defaults on server start diff --git a/src/server/database/Database/MySQLConnection.cpp b/src/server/database/Database/MySQLConnection.cpp index 832c56834d..60f553df3d 100644 --- a/src/server/database/Database/MySQLConnection.cpp +++ b/src/server/database/Database/MySQLConnection.cpp @@ -592,17 +592,15 @@ bool MySQLConnection::_HandleMySQLErrno(uint32 errNo, uint8 attempts /*= 5*/) case ER_DUP_ENTRY: return false; - // Outdated table or database structure - terminate core + // Outdated table or database structure - log error but don't terminate + // (Changed from ABORT to allow recovery from bad external queries like MCP) case ER_BAD_FIELD_ERROR: case ER_NO_SUCH_TABLE: - TC_LOG_ERROR("sql.sql", "Your database structure is not up to date. Please make sure you've executed all queries in the sql/updates folders."); - std::this_thread::sleep_for(std::chrono::seconds(10)); - ABORT(); + TC_LOG_ERROR("sql.sql", "Database structure error (bad field or missing table). Query failed but server will continue."); + TC_LOG_ERROR("sql.sql", "If this is a schema issue, run sql/updates. If from external query (MCP), fix the query."); return false; case ER_PARSE_ERROR: - TC_LOG_ERROR("sql.sql", "Error while parsing SQL. Core fix required."); - std::this_thread::sleep_for(std::chrono::seconds(10)); - ABORT(); + TC_LOG_ERROR("sql.sql", "Error while parsing SQL. Query failed but server will continue."); return false; default: TC_LOG_ERROR("sql.sql", "Unhandled MySQL errno {}. Unexpected behaviour possible.", errNo); diff --git a/src/server/game/LuaEngine/methods/TrinityCore/CreatureMethods.h b/src/server/game/LuaEngine/methods/TrinityCore/CreatureMethods.h index 77c33d73a9..1023ed76ad 100644 --- a/src/server/game/LuaEngine/methods/TrinityCore/CreatureMethods.h +++ b/src/server/game/LuaEngine/methods/TrinityCore/CreatureMethods.h @@ -1575,12 +1575,16 @@ namespace LuaCreature // Optional player parameter for phase inheritance (arg 2) Player* phaseSource = E->CHECKOBJ(2, false); - // Optional display ID parameter (arg 3, or arg 2 if no player provided) + // Display ID is always arg 3 when player is provided Optional displayId; - int displayIdArg = phaseSource ? 3 : 2; - if (!lua_isnoneornil(E->L, displayIdArg)) + if (!lua_isnoneornil(E->L, 3)) { - displayId = E->CHECKVAL(displayIdArg); + displayId = E->CHECKVAL(3); + } + else if (!phaseSource && !lua_isnoneornil(E->L, 2) && lua_isnumber(E->L, 2)) + { + // If no player but arg 2 is a number, use it as displayId + displayId = E->CHECKVAL(2); } sWaypointMgr->VisualizePath(creature, path, displayId); diff --git a/src/server/game/Movement/Waypoints/WaypointManager.cpp b/src/server/game/Movement/Waypoints/WaypointManager.cpp index be7f0a4b25..facec213f3 100644 --- a/src/server/game/Movement/Waypoints/WaypointManager.cpp +++ b/src/server/game/Movement/Waypoints/WaypointManager.cpp @@ -237,11 +237,15 @@ void WaypointMgr::VisualizePath(Unit* owner, WaypointPath const* path, Optional< if (!summon) continue; - if (displayId) - { - summon->SetDisplayId(*displayId, true); - summon->SetObjectScale(0.5f); - } + // Always set display - use passed displayId or default to Elven Wisp (1824) + constexpr uint32 DEFAULT_WAYPOINT_DISPLAY = 1824; // Elven Wisp - works in 11.2.5 + summon->SetDisplayId(displayId.value_or(DEFAULT_WAYPOINT_DISPLAY), true); + summon->SetObjectScale(0.5f); + + // Force client to refresh the creature's appearance + // (SetDisplayId alone doesn't update already-spawned creatures on client) + summon->DestroyForNearbyPlayers(); + summon->UpdateObjectVisibilityOnCreate(); _nodeToVisualWaypointGUIDsMap[pathNodePair] = summon->GetGUID(); _visualWaypointGUIDToNodeMap[summon->GetGUID()] = std::pair(path, &node);