From 7c6249e9ee0f48eadafae77853a17a88ae7cb5ac Mon Sep 17 00:00:00 2001 From: SAS2000 Date: Mon, 25 Aug 2025 00:23:12 +0200 Subject: [PATCH 1/7] fix(Core/Commands): Waypoint show on/off bug Current implementation cannot delete waypoints Closes AzerothCore issue #22707 On branch fix-issue-22726 Changes to be committed: modified: src/server/scripts/Commands/cs_wp.cpp --- src/server/scripts/Commands/cs_wp.cpp | 194 ++++++++++++-------------- 1 file changed, 89 insertions(+), 105 deletions(-) diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index d0d4c1cf1c..8e087a2990 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -49,6 +49,57 @@ public: }; return commandTable; } + + /** + * Remove all waypoint from a query result. + * + * @param result: If the result not empty it should constains the creature database guids in the first column in all rows. + * + */ + static void RemoveWaypointsFromQueryResult(ChatHandler* handler, const PreparedQueryResult& result) + { + if (result) + { + bool hasError = false; + + do + { + const Field* fields = result->Fetch(); + const ObjectGuid::LowType guid = fields[0].Get(); + const auto creatureRange = handler->GetSession()->GetPlayer()->GetMap()->GetCreatureBySpawnIdStore().equal_range(guid); + if (creatureRange.first != creatureRange.second) + { + std::for_each( + creatureRange.first, + creatureRange.second, + [&](auto& guidCreaturePair) { + Creature* creature = guidCreaturePair.second; + creature->CombatStop(); + creature->DeleteFromDB(); + creature->AddObjectToRemoveList(); + } + ); + } + else + { + handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); + hasError = true; + + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); + stmt->SetData(0, guid); + WorldDatabase.Execute(stmt); + } + } while (result->NextRow()); + + if (hasError) + { + handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR1); + handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR2); + handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR3); + } + } + } + /** * Add a waypoint to a creature. * @@ -752,41 +803,45 @@ public: uint32 pathid = 0; Creature* target = handler->getSelectedCreature(); - // Did player provide a PathID? + const std::string show = show_str; - if (!guid_str) + // If command equals "off" we do not need a guid or target, just delete all WPs + if (show != "off") { - // No PathID provided - // -> Player must have selected a creature - - if (!target) + // Did player provide a PathID? + if (!guid_str) { - handler->SendErrorMessage(LANG_SELECT_CREATURE); - return false; + // No PathID provided + // -> Player must have selected a creature + if (!target) + { + handler->SendErrorMessage(LANG_SELECT_CREATURE); + return false; + } + + pathid = target->GetWaypointPath(); } + else + { + // PathID provided + // Warn if player also selected a creature + // -> Creature selection is ignored <- + if (target) + handler->SendSysMessage(LANG_WAYPOINT_CREATSELECTED); - pathid = target->GetWaypointPath(); + pathid = atoi((char*)guid_str); + } } - else - { - // PathID provided - // Warn if player also selected a creature - // -> Creature selection is ignored <- - if (target) - handler->SendSysMessage(LANG_WAYPOINT_CREATSELECTED); - - pathid = atoi((char*)guid_str); - } - - std::string show = show_str; //handler->PSendSysMessage("wpshow - show: {}", show); + constexpr uint32 wpEntry = VISUAL_WAYPOINT; + // Show info for the selected waypoint if (show == "info") { // Check if the user did specify a visual waypoint - if (!target || target->GetEntry() != VISUAL_WAYPOINT) + if (!target || target->GetEntry() != wpEntry) { handler->SendErrorMessage(LANG_WAYPOINT_VP_SELECT); return false; @@ -843,45 +898,9 @@ public: // Delete all visuals for this NPC stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_WPGUID_BY_ID); - stmt->SetData(0, pathid); - PreparedQueryResult result2 = WorldDatabase.Query(stmt); - - if (result2) - { - bool hasError = false; - do - { - Field* fields = result2->Fetch(); - uint32 wpguid = fields[0].Get(); - Creature* creature = handler->GetSession()->GetPlayer()->GetMap()->GetCreature(ObjectGuid::Create(VISUAL_WAYPOINT, wpguid)); - - if (!creature) - { - handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, wpguid); - hasError = true; - - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); - stmt->SetData(0, wpguid); - - WorldDatabase.Execute(stmt); - } - else - { - creature->CombatStop(); - creature->DeleteFromDB(); - creature->AddObjectToRemoveList(); - } - } while (result2->NextRow()); - - if (hasError) - { - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR1); - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR2); - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR3); - } - } + RemoveWaypointsFromQueryResult(handler, WorldDatabase.Query(stmt)); do { @@ -891,16 +910,14 @@ public: float y = fields[2].Get(); float z = fields[3].Get(); - uint32 id = VISUAL_WAYPOINT; - Player* chr = handler->GetSession()->GetPlayer(); Map* map = chr->GetMap(); float o = chr->GetOrientation(); Creature* wpCreature = new Creature; - if (!wpCreature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), id, 0, x, y, z, o)) + if (!wpCreature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), wpEntry, 0, x, y, z, o)) { - handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); + handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); delete wpCreature; return false; } @@ -918,7 +935,7 @@ public: // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map, true, true)) { - handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); + handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); delete wpCreature; return false; } @@ -953,16 +970,15 @@ public: float x = fields[0].Get(); float y = fields[1].Get(); float z = fields[2].Get(); - uint32 id = VISUAL_WAYPOINT; Player* chr = handler->GetSession()->GetPlayer(); float o = chr->GetOrientation(); Map* map = chr->GetMap(); Creature* creature = new Creature; - if (!creature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), id, 0, x, y, z, o)) + if (!creature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), wpEntry, 0, x, y, z, o)) { - handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); + handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); delete creature; return false; } @@ -970,7 +986,7 @@ public: creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map, true, true)) { - handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, id); + handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); delete creature; return false; } @@ -1003,15 +1019,14 @@ public: float y = fields[1].Get(); float z = fields[2].Get(); float o = fields[3].Get(); - uint32 id = VISUAL_WAYPOINT; Player* chr = handler->GetSession()->GetPlayer(); Map* map = chr->GetMap(); Creature* creature = new Creature; - if (!creature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), id, 0, x, y, z, o)) + if (!creature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), wpEntry, 0, x, y, z, o)) { - handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); + handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, wpEntry); delete creature; return false; } @@ -1019,7 +1034,7 @@ public: creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map, true, true)) { - handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, id); + handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, wpEntry); delete creature; return false; } @@ -1036,7 +1051,7 @@ public: if (show == "off") { WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_BY_ID); - stmt->SetArguments(1, 1, 1); + stmt->SetArguments(wpEntry, wpEntry, wpEntry); PreparedQueryResult result = WorldDatabase.Query(stmt); if (!result) @@ -1045,44 +1060,13 @@ public: return false; } - bool hasError = false; - - do - { - Field* fields = result->Fetch(); - ObjectGuid::LowType guid = fields[0].Get(); - Creature* creature = handler->GetSession()->GetPlayer()->GetMap()->GetCreature(ObjectGuid::Create(VISUAL_WAYPOINT, guid)); - if (!creature) - { - handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); - hasError = true; - - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); - - stmt->SetData(0, guid); - - WorldDatabase.Execute(stmt); - } - else - { - creature->CombatStop(); - creature->DeleteFromDB(); - creature->AddObjectToRemoveList(); - } - } while (result->NextRow()); + RemoveWaypointsFromQueryResult(handler, result); // set "wpguid" column to "empty" - no visual waypoint spawned stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_ALL_WPGUID); WorldDatabase.Execute(stmt); - if (hasError) - { - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR1); - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR2); - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR3); - } - handler->SendSysMessage(LANG_WAYPOINT_VP_ALLREMOVED); return true; } From b5369a72d70f0fe2ad7881a00129e011a40fb33e Mon Sep 17 00:00:00 2001 From: SAS2000 Date: Tue, 26 Aug 2025 11:47:33 +0200 Subject: [PATCH 2/7] On branch fix-issue-22726 Changes to be committed: modified: src/server/scripts/Commands/cs_wp.cpp --- src/server/scripts/Commands/cs_wp.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index 8e087a2990..58168c83fc 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -58,7 +58,7 @@ public: */ static void RemoveWaypointsFromQueryResult(ChatHandler* handler, const PreparedQueryResult& result) { - if (result) + if (handler && result) { bool hasError = false; @@ -66,7 +66,7 @@ public: { const Field* fields = result->Fetch(); const ObjectGuid::LowType guid = fields[0].Get(); - const auto creatureRange = handler->GetSession()->GetPlayer()->GetMap()->GetCreatureBySpawnIdStore().equal_range(guid); + auto const creatureRange = handler->GetSession()->GetPlayer()->GetMap()->GetCreatureBySpawnIdStore().equal_range(guid); if (creatureRange.first != creatureRange.second) { std::for_each( @@ -803,7 +803,7 @@ public: uint32 pathid = 0; Creature* target = handler->getSelectedCreature(); - const std::string show = show_str; + std::string_view const show = show_str; // If command equals "off" we do not need a guid or target, just delete all WPs if (show != "off") From f556278c6c96c66506ab7e8475c7512ee849a61d Mon Sep 17 00:00:00 2001 From: SAS2000 Date: Tue, 26 Aug 2025 21:33:54 +0200 Subject: [PATCH 3/7] On branch fix-issue-22726 Changes to be committed: modified: src/server/scripts/Commands/cs_wp.cpp --- src/server/scripts/Commands/cs_wp.cpp | 72 +++++++++++++-------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index 58168c83fc..df5746552c 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -58,45 +58,45 @@ public: */ static void RemoveWaypointsFromQueryResult(ChatHandler* handler, const PreparedQueryResult& result) { - if (handler && result) + if (!handler || !result) + return; + + bool hasError = false; + + do { - bool hasError = false; - - do + const Field* fields = result->Fetch(); + const ObjectGuid::LowType guid = fields[0].Get(); + auto const creatureRange = handler->GetSession()->GetPlayer()->GetMap()->GetCreatureBySpawnIdStore().equal_range(guid); + if (creatureRange.first != creatureRange.second) { - const Field* fields = result->Fetch(); - const ObjectGuid::LowType guid = fields[0].Get(); - auto const creatureRange = handler->GetSession()->GetPlayer()->GetMap()->GetCreatureBySpawnIdStore().equal_range(guid); - if (creatureRange.first != creatureRange.second) - { - std::for_each( - creatureRange.first, - creatureRange.second, - [&](auto& guidCreaturePair) { - Creature* creature = guidCreaturePair.second; - creature->CombatStop(); - creature->DeleteFromDB(); - creature->AddObjectToRemoveList(); - } - ); - } - else - { - handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); - hasError = true; - - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); - stmt->SetData(0, guid); - WorldDatabase.Execute(stmt); - } - } while (result->NextRow()); - - if (hasError) - { - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR1); - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR2); - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR3); + std::for_each( + creatureRange.first, + creatureRange.second, + [&](auto& guidCreaturePair) { + Creature* creature = guidCreaturePair.second; + creature->CombatStop(); + creature->DeleteFromDB(); + creature->AddObjectToRemoveList(); + } + ); } + else + { + handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); + hasError = true; + + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); + stmt->SetData(0, guid); + WorldDatabase.Execute(stmt); + } + } while (result->NextRow()); + + if (hasError) + { + handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR1); + handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR2); + handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR3); } } From 41d734f5902c3b2221b0dd249317f52e78f99141 Mon Sep 17 00:00:00 2001 From: SAS2000 Date: Thu, 28 Aug 2025 18:04:41 +0200 Subject: [PATCH 4/7] fix(Core/Commands): wp show on/off/first/last wp show first/last not works as expected Closes AzerothCore issue #22740 On branch fix-issue-22740 Changes to be committed: new file: data/sql/updates/db_world/2025_08_28_00.sql modified: src/server/database/Database/Implementation/WorldDatabase.cpp modified: src/server/database/Database/Implementation/WorldDatabase.h modified: src/server/game/Miscellaneous/Language.h modified: src/server/scripts/Commands/cs_wp.cpp --- data/sql/updates/db_world/2025_08_28_00.sql | 5 + .../Database/Implementation/WorldDatabase.cpp | 8 +- .../Database/Implementation/WorldDatabase.h | 2 +- src/server/game/Miscellaneous/Language.h | 14 +- src/server/scripts/Commands/cs_wp.cpp | 381 +++++++----------- 5 files changed, 170 insertions(+), 240 deletions(-) create mode 100644 data/sql/updates/db_world/2025_08_28_00.sql diff --git a/data/sql/updates/db_world/2025_08_28_00.sql b/data/sql/updates/db_world/2025_08_28_00.sql new file mode 100644 index 0000000000..6f3dce26a0 --- /dev/null +++ b/data/sql/updates/db_world/2025_08_28_00.sql @@ -0,0 +1,5 @@ +-- DB update 2025_08_25_02 -> 2025_08_28_00 +-- add wp show first/last to command help +UPDATE `command` SET +`help`='Syntax: .wp show $option\nOptions:\non $pathid (or selected creature with loaded path) - Show path\nfirst $pathid (or selected creature with loaded path) - Show first waypoint\nlast $pathid (or selected creature with loaded path) - Show last waypoint\noff - Hide path (all waypoints in current map)\ninfo $selected_waypoint - Show info for selected waypoint.' +WHERE `name`='wp show'; diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index f216c95838..ede44d3c04 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -53,12 +53,12 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_ID, "SELECT MAX(id) FROM waypoint_data", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_MAX_POINT, "SELECT MAX(point) FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, move_type, delay, action, action_chance FROM waypoint_data WHERE id = ? ORDER BY point", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID, "SELECT point, position_x, position_y, position_z FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, "SELECT position_x, position_y, position_z FROM waypoint_data WHERE point = 1 AND id = ?", CONNECTION_SYNCH); - PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID, "SELECT position_x, position_y, position_z, orientation FROM waypoint_data WHERE id = ? ORDER BY point DESC LIMIT 1", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, wpguid FROM waypoint_data WHERE id = ?", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, wpguid FROM waypoint_data WHERE id = ? ORDER BY point ASC LIMIT 1", CONNECTION_SYNCH); + PrepareStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID, "SELECT point, position_x, position_y, position_z, orientation, wpguid FROM waypoint_data WHERE id = ? ORDER BY point DESC LIMIT 1", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_WPGUID, "SELECT id, point FROM waypoint_data WHERE wpguid = ?", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_ALL_BY_WPGUID, "SELECT id, point, delay, move_type, action, action_chance FROM waypoint_data WHERE wpguid = ?", CONNECTION_SYNCH); - PrepareStatement(WORLD_UPD_WAYPOINT_DATA_ALL_WPGUID, "UPDATE waypoint_data SET wpguid = 0", CONNECTION_ASYNC); + PrepareStatement(WORLD_UPD_WAYPOINT_DATA_BY_WPGUID, "UPDATE waypoint_data SET wpguid = 0 WHERE wpguid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_BY_POS, "SELECT id, point FROM waypoint_data WHERE (abs(position_x - ?) <= ?) and (abs(position_y - ?) <= ?) and (abs(position_z - ?) <= ?)", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_WPGUID_BY_ID, "SELECT wpguid FROM waypoint_data WHERE id = ? and wpguid <> 0", CONNECTION_SYNCH); PrepareStatement(WORLD_SEL_WAYPOINT_DATA_ACTION, "SELECT DISTINCT action FROM waypoint_data", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/WorldDatabase.h b/src/server/database/Database/Implementation/WorldDatabase.h index 016791ccd5..46a32a03c9 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.h +++ b/src/server/database/Database/Implementation/WorldDatabase.h @@ -55,7 +55,7 @@ enum WorldDatabaseStatements : uint32 WORLD_UPD_WAYPOINT_DATA_POINT, WORLD_UPD_WAYPOINT_DATA_POSITION, WORLD_UPD_WAYPOINT_DATA_WPGUID, - WORLD_UPD_WAYPOINT_DATA_ALL_WPGUID, + WORLD_UPD_WAYPOINT_DATA_BY_WPGUID, WORLD_SEL_WAYPOINT_DATA_MAX_ID, WORLD_SEL_WAYPOINT_DATA_BY_ID, WORLD_SEL_WAYPOINT_DATA_POS_BY_ID, diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 5658e4147d..15b09b332f 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -264,18 +264,18 @@ enum AcoreStrings LANG_RESETALL_TALENTS = 219, LANG_WAYPOINT_NOTFOUND = 220, - LANG_WAYPOINT_NOTFOUNDLAST = 221, + LANG_WAYPOINT_NOTFOUNDLAST = 221, // Not used LANG_WAYPOINT_NOTFOUNDSEARCH = 222, LANG_WAYPOINT_NOTFOUNDDBPROBLEM = 223, LANG_WAYPOINT_CREATSELECTED = 224, - LANG_WAYPOINT_CREATNOTFOUND = 225, + LANG_WAYPOINT_CREATNOTFOUND = 225, // Not used LANG_WAYPOINT_VP_SELECT = 226, LANG_WAYPOINT_VP_NOTFOUND = 227, LANG_WAYPOINT_VP_NOTCREATED = 228, LANG_WAYPOINT_VP_ALLREMOVED = 229, - LANG_WAYPOINT_NOTCREATED = 230, - LANG_WAYPOINT_NOGUID = 231, - LANG_WAYPOINT_NOWAYPOINTGIVEN = 232, + LANG_WAYPOINT_NOTCREATED = 230, // Not used + LANG_WAYPOINT_NOGUID = 231, // Not used + LANG_WAYPOINT_NOWAYPOINTGIVEN = 232, // Not used LANG_WAYPOINT_ARGUMENTREQ = 233, LANG_WAYPOINT_ADDED = 234, LANG_WAYPOINT_ADDED_NO = 235, @@ -287,8 +287,8 @@ enum AcoreStrings LANG_WAYPOINT_REMOVED = 241, LANG_WAYPOINT_NOTREMOVED = 242, LANG_WAYPOINT_TOOFAR1 = 243, - LANG_WAYPOINT_TOOFAR2 = 244, - LANG_WAYPOINT_TOOFAR3 = 245, + LANG_WAYPOINT_TOOFAR2 = 244, // Not used + LANG_WAYPOINT_TOOFAR3 = 245, // Not used LANG_WAYPOINT_INFO_TITLE = 246, LANG_WAYPOINT_INFO_WAITTIME = 247, LANG_WAYPOINT_INFO_MODEL = 248, diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index df5746552c..2002258bc2 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -26,10 +26,14 @@ using namespace Acore::ChatCommands; +namespace { + constexpr uint32 wpEntry = VISUAL_WAYPOINT; +} + class wp_commandscript : public CommandScript { public: - wp_commandscript() : CommandScript("wp_commandscript") { } + wp_commandscript() : CommandScript("wp_commandscript") {} ChatCommandTable GetCommands() const override { @@ -50,53 +54,117 @@ public: return commandTable; } - /** - * Remove all waypoint from a query result. - * - * @param result: If the result not empty it should constains the creature database guids in the first column in all rows. - * - */ - static void RemoveWaypointsFromQueryResult(ChatHandler* handler, const PreparedQueryResult& result) + static bool AddWaypointsByPreparedStatement(ChatHandler* handler, WorldDatabaseConnection::Statements index, uint32 pathid, Creature* target = nullptr) { - if (!handler || !result) - return; + ASSERT(handler); + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(index); + stmt->SetData(0, pathid); + PreparedQueryResult result = WorldDatabase.Query(stmt); - bool hasError = false; + if (!result) + { + handler->SendErrorMessage(LANG_WAYPOINT_NOTFOUND, pathid); + return false; + } do { - const Field* fields = result->Fetch(); - const ObjectGuid::LowType guid = fields[0].Get(); - auto const creatureRange = handler->GetSession()->GetPlayer()->GetMap()->GetCreatureBySpawnIdStore().equal_range(guid); - if (creatureRange.first != creatureRange.second) - { - std::for_each( - creatureRange.first, - creatureRange.second, - [&](auto& guidCreaturePair) { - Creature* creature = guidCreaturePair.second; - creature->CombatStop(); - creature->DeleteFromDB(); - creature->AddObjectToRemoveList(); - } - ); - } - else - { - handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); - hasError = true; + Field* fields = result->Fetch(); + uint32 point = fields[0].Get(); + float x = fields[1].Get(); + float y = fields[2].Get(); + float z = fields[3].Get(); + float o = fields[4].Get(); + uint32 wpguid = fields[5].Get(); - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_DEL_CREATURE); - stmt->SetData(0, guid); - WorldDatabase.Execute(stmt); + Player* chr = handler->GetSession()->GetPlayer(); + Map* map = chr->GetMap(); + + // Try to create new creature + Creature* wpCreature = new Creature; + if (!wpCreature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), wpEntry, 0, x, y, z, o)) + { + handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); + delete wpCreature; + return false; + } + + // Remove old waypoint for the same path_id and point if exists + if (wpguid != 0) + RemoveWaypointById(handler, wpguid); + + // Save new waypoint to db + wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); + + // Set "wpguid" column to the new visual waypoint + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID); + stmt->SetData(0, wpCreature->GetSpawnId()); + stmt->SetData(1, pathid); + stmt->SetData(2, point); + + WorldDatabase.Execute(stmt); + + // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); + if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map, true, true)) + { + handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); + delete wpCreature; + return false; + } + + if (target) + { + wpCreature->SetDisplayId(target->GetDisplayId()); + wpCreature->SetObjectScale(0.5f); + wpCreature->SetLevel(point > STRONG_MAX_LEVEL ? STRONG_MAX_LEVEL : point); } } while (result->NextRow()); - if (hasError) + return true; + } + + /** + * Remove an existing waypoint by dbGuid. + * + * @param guid: The dbGuid of the waypoint + * @param deleteFromWaypointData: true if the caller wants to delete the wpGuid from the waypoint_data table + * should be false if we immediately recreate the waypoint and set a new value + * + * @return true - command did succeed, false - something went wrong + * + */ + static bool RemoveWaypointById(ChatHandler* handler, const ObjectGuid::LowType guid, bool deleteFromWaypointData = false) + { + ASSERT(handler); + const auto creatureRange = handler->GetSession()->GetPlayer()->GetMap()->GetCreatureBySpawnIdStore().equal_range(guid); + if (creatureRange.first != creatureRange.second) { - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR1); - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR2); - handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR3); + std::for_each( + creatureRange.first, + creatureRange.second, + [&](auto& guidCreaturePair) { + if (deleteFromWaypointData) { + // Set "wpguid" column to "empty" + WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_BY_WPGUID); + stmt->SetData(0, guid); + WorldDatabase.Execute(stmt); + } + + // Remove creature + Creature* creature = guidCreaturePair.second; + creature->CombatStop(); + creature->DeleteFromDB(); + creature->AddObjectToRemoveList(); + } + ); + + return true; + } + else + { + handler->PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, guid); + + return false; } } @@ -478,11 +546,11 @@ public: return true; } - std::string arg_string = arg_2; + std::string arg_string = arg_2; if ((arg_string != "setid") && (arg_string != "delay") && (arg_string != "command") - && (arg_string != "datalong") && (arg_string != "datalong2") && (arg_string != "dataint") && (arg_string != "posx") - && (arg_string != "posy") && (arg_string != "posz") && (arg_string != "orientation")) + && (arg_string != "datalong") && (arg_string != "datalong2") && (arg_string != "dataint") && (arg_string != "posx") + && (arg_string != "posy") && (arg_string != "posz") && (arg_string != "orientation")) { handler->SendSysMessage("|cffff33ffERROR: No valid argument present.|r"); return true; @@ -607,8 +675,8 @@ public: // Check // Remember: "show" must also be the name of a column! if ((show != "delay") && (show != "action") && (show != "action_chance") - && (show != "move_type") && (show != "del") && (show != "move") && (show != "wpadd") - ) + && (show != "move_type") && (show != "del") && (show != "move") && (show != "wpadd") + ) { return false; } @@ -787,29 +855,26 @@ public: return true; } - static bool HandleWpShowCommand(ChatHandler* handler, const char* args) + static bool HandleWpShowCommand(ChatHandler* handler, std::string show, Optional guid) { - if (!*args) - return false; - - // first arg: on, off, first, last - char* show_str = strtok((char*)args, " "); - if (!show_str) - return false; - - // second arg: GUID (optional, if a creature is selected) - char* guid_str = strtok((char*)nullptr, " "); - uint32 pathid = 0; Creature* target = handler->getSelectedCreature(); - std::string_view const show = show_str; - - // If command equals "off" we do not need a guid or target, just delete all WPs + // If command equals "off" we do not need a guid or target, just delete all WPs in the current map if (show != "off") { // Did player provide a PathID? - if (!guid_str) + if (guid) + { + // PathID provided + // Warn if player also selected a creature + // -> Creature selection is ignored <- + if (target) + handler->SendSysMessage(LANG_WAYPOINT_CREATSELECTED); + + pathid = *guid; + } + else { // No PathID provided // -> Player must have selected a creature @@ -820,23 +885,15 @@ public: } pathid = target->GetWaypointPath(); - } - else - { - // PathID provided - // Warn if player also selected a creature - // -> Creature selection is ignored <- - if (target) - handler->SendSysMessage(LANG_WAYPOINT_CREATSELECTED); - pathid = atoi((char*)guid_str); + // If target hasn't got a path -> we should return an error + if (pathid == 0) { + handler->SendErrorMessage(LANG_WAYPOINT_NOTFOUND, target->GetEntry()); + return false; + } } } - //handler->PSendSysMessage("wpshow - show: {}", show); - - constexpr uint32 wpEntry = VISUAL_WAYPOINT; - // Show info for the selected waypoint if (show == "info") { @@ -880,174 +937,31 @@ public: return true; } + // Show all waypoints in #pathid if (show == "on") { - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_BY_ID); + handler->PSendSysMessage("|cff00ff00DEBUG: wp on, PathID: |r|cff00ffff{}|r", pathid); - stmt->SetData(0, pathid); - - PreparedQueryResult result = WorldDatabase.Query(stmt); - - if (!result) - { - handler->SendErrorMessage("|cffff33ffPath no found.|r"); - return false; - } - - handler->PSendSysMessage("|cff00ff00DEBUG: wp on, PathID: |cff00ffff{}|r", pathid); - - // Delete all visuals for this NPC - stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_WPGUID_BY_ID); - stmt->SetData(0, pathid); - - RemoveWaypointsFromQueryResult(handler, WorldDatabase.Query(stmt)); - - do - { - Field* fields = result->Fetch(); - uint32 point = fields[0].Get(); - float x = fields[1].Get(); - float y = fields[2].Get(); - float z = fields[3].Get(); - - Player* chr = handler->GetSession()->GetPlayer(); - Map* map = chr->GetMap(); - float o = chr->GetOrientation(); - - Creature* wpCreature = new Creature; - if (!wpCreature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), wpEntry, 0, x, y, z, o)) - { - handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); - delete wpCreature; - return false; - } - - wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); - - // Set "wpguid" column to the visual waypoint - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_WPGUID); - stmt->SetData(0, wpCreature->GetSpawnId()); - stmt->SetData(1, pathid); - stmt->SetData(2, point); - - WorldDatabase.Execute(stmt); - - // To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells(); - if (!wpCreature->LoadCreatureFromDB(wpCreature->GetSpawnId(), map, true, true)) - { - handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); - delete wpCreature; - return false; - } - - if (target) - { - wpCreature->SetDisplayId(target->GetDisplayId()); - wpCreature->SetObjectScale(0.5f); - wpCreature->SetLevel(point > STRONG_MAX_LEVEL ? STRONG_MAX_LEVEL : point); - } - } while (result->NextRow()); - - handler->SendSysMessage("|cff00ff00Showing the current creature's path.|r"); - return true; + return AddWaypointsByPreparedStatement(handler, WORLD_SEL_WAYPOINT_DATA_POS_BY_ID, pathid, target); } + // Show first waypoint in #pathid if (show == "first") { - handler->PSendSysMessage("|cff00ff00DEBUG: wp first, GUID: {}|r", pathid); + handler->PSendSysMessage("|cff00ff00DEBUG: wp first, PathID: |r|cff00ffff{}|r", pathid); - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID); - stmt->SetData(0, pathid); - - PreparedQueryResult result = WorldDatabase.Query(stmt); - if (!result) - { - handler->SendErrorMessage(LANG_WAYPOINT_NOTFOUND, pathid); - return false; - } - - Field* fields = result->Fetch(); - float x = fields[0].Get(); - float y = fields[1].Get(); - float z = fields[2].Get(); - - Player* chr = handler->GetSession()->GetPlayer(); - float o = chr->GetOrientation(); - Map* map = chr->GetMap(); - - Creature* creature = new Creature; - if (!creature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), wpEntry, 0, x, y, z, o)) - { - handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); - delete creature; - return false; - } - - creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); - if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map, true, true)) - { - handler->PSendSysMessage(LANG_WAYPOINT_VP_NOTCREATED, wpEntry); - delete creature; - return false; - } - - if (target) - { - creature->SetDisplayId(target->GetDisplayId()); - creature->SetObjectScale(0.5f); - } - - return true; + return AddWaypointsByPreparedStatement(handler, WORLD_SEL_WAYPOINT_DATA_POS_FIRST_BY_ID, pathid, target); } + // Show last waypoint in #pathid if (show == "last") { handler->PSendSysMessage("|cff00ff00DEBUG: wp last, PathID: |r|cff00ffff{}|r", pathid); - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID); - stmt->SetData(0, pathid); - - PreparedQueryResult result = WorldDatabase.Query(stmt); - if (!result) - { - handler->SendErrorMessage(LANG_WAYPOINT_NOTFOUNDLAST, pathid); - return false; - } - - Field* fields = result->Fetch(); - float x = fields[0].Get(); - float y = fields[1].Get(); - float z = fields[2].Get(); - float o = fields[3].Get(); - - Player* chr = handler->GetSession()->GetPlayer(); - Map* map = chr->GetMap(); - - Creature* creature = new Creature; - if (!creature->Create(map->GenerateLowGuid(), map, chr->GetPhaseMaskForSpawn(), wpEntry, 0, x, y, z, o)) - { - handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, wpEntry); - delete creature; - return false; - } - - creature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn()); - if (!creature->LoadCreatureFromDB(creature->GetSpawnId(), map, true, true)) - { - handler->PSendSysMessage(LANG_WAYPOINT_NOTCREATED, wpEntry); - delete creature; - return false; - } - - if (target) - { - creature->SetDisplayId(target->GetDisplayId()); - creature->SetObjectScale(0.5f); - } - - return true; + return AddWaypointsByPreparedStatement(handler, WORLD_SEL_WAYPOINT_DATA_POS_LAST_BY_ID, pathid, target); } + // Delete all waypoints if (show == "off") { WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_BY_ID); @@ -1060,12 +974,23 @@ public: return false; } - RemoveWaypointsFromQueryResult(handler, result); + if (result) + { + bool hasError = false; - // set "wpguid" column to "empty" - no visual waypoint spawned - stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_ALL_WPGUID); + do + { + Field* fields = result->Fetch(); + ObjectGuid::LowType guid = fields[0].Get(); + hasError |= !RemoveWaypointById(handler, guid, true); - WorldDatabase.Execute(stmt); + } while (result->NextRow()); + + if (hasError) + { + handler->PSendSysMessage(LANG_WAYPOINT_TOOFAR1); + } + } handler->SendSysMessage(LANG_WAYPOINT_VP_ALLREMOVED); return true; From 973541f39a0774a98651065af99351b208bccad3 Mon Sep 17 00:00:00 2001 From: SAS2000 Date: Thu, 28 Aug 2025 18:17:07 +0200 Subject: [PATCH 5/7] On branch fix-issue-22740 Changes to be committed: modified: src/server/scripts/Commands/cs_wp.cpp --- src/server/scripts/Commands/cs_wp.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index 2002258bc2..397593035b 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -143,7 +143,8 @@ public: creatureRange.first, creatureRange.second, [&](auto& guidCreaturePair) { - if (deleteFromWaypointData) { + if (deleteFromWaypointData) + { // Set "wpguid" column to "empty" WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_UPD_WAYPOINT_DATA_BY_WPGUID); stmt->SetData(0, guid); @@ -887,7 +888,8 @@ public: pathid = target->GetWaypointPath(); // If target hasn't got a path -> we should return an error - if (pathid == 0) { + if (pathid == 0) + { handler->SendErrorMessage(LANG_WAYPOINT_NOTFOUND, target->GetEntry()); return false; } From 1aa23e84ea88447f47bab215522a2d662405bacd Mon Sep 17 00:00:00 2001 From: SAS2000 Date: Fri, 29 Aug 2025 06:59:42 +0200 Subject: [PATCH 6/7] On branch fix-issue-22740 Changes to be committed: modified: src/server/scripts/Commands/cs_wp.cpp --- src/server/scripts/Commands/cs_wp.cpp | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index 397593035b..953a986330 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -861,8 +861,18 @@ public: uint32 pathid = 0; Creature* target = handler->getSelectedCreature(); + // Checks for early return + if (show == "info") + { + // Check if the user is targeting a visual waypoint + if (!target || target->GetEntry() != wpEntry) + { + handler->SendErrorMessage(LANG_WAYPOINT_VP_SELECT); + return false; + } + } // If command equals "off" we do not need a guid or target, just delete all WPs in the current map - if (show != "off") + else if (show != "off") { // Did player provide a PathID? if (guid) @@ -899,13 +909,6 @@ public: // Show info for the selected waypoint if (show == "info") { - // Check if the user did specify a visual waypoint - if (!target || target->GetEntry() != wpEntry) - { - handler->SendErrorMessage(LANG_WAYPOINT_VP_SELECT); - return false; - } - WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_WAYPOINT_DATA_ALL_BY_WPGUID); stmt->SetData(0, target->GetSpawnId()); From 869aad1bc218ad9eddf9185675539e60f1298566 Mon Sep 17 00:00:00 2001 From: SAS2000 Date: Mon, 13 Oct 2025 10:28:50 +0200 Subject: [PATCH 7/7] On branch fix-issue-22740 Changes to be committed: renamed: data/sql/updates/db_world/2025_08_28_01.sql -> data/sql/updates/db_world/2025_10_12_07.sql --- .../updates/db_world/{2025_08_28_01.sql => 2025_10_12_07.sql} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename data/sql/updates/db_world/{2025_08_28_01.sql => 2025_10_12_07.sql} (91%) diff --git a/data/sql/updates/db_world/2025_08_28_01.sql b/data/sql/updates/db_world/2025_10_12_07.sql similarity index 91% rename from data/sql/updates/db_world/2025_08_28_01.sql rename to data/sql/updates/db_world/2025_10_12_07.sql index 00283c3568..3c9589b08f 100644 --- a/data/sql/updates/db_world/2025_08_28_01.sql +++ b/data/sql/updates/db_world/2025_10_12_07.sql @@ -1,4 +1,4 @@ --- DB update 2025_08_28_00 -> 2025_08_28_01 +-- DB update 2025_10_12_06 -> 2025_10_12_07 -- add wp show first/last to command help UPDATE `command` SET `help`='Syntax: .wp show $option\nOptions:\non $pathid (or selected creature with loaded path) - Show path\nfirst $pathid (or selected creature with loaded path) - Show first waypoint\nlast $pathid (or selected creature with loaded path) - Show last waypoint\noff - Hide path (all waypoints in current map)\ninfo $selected_waypoint - Show info for selected waypoint.'