From 468457a63ef84c2f359bf29c126c9b4b339bdf05 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sun, 10 Aug 2025 16:12:46 +0200 Subject: [PATCH 001/155] fix(DB/Creature): En'kilah Gargoyles fly well again. (#22658) --- data/sql/updates/pending_db_world/gargoyles.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/gargoyles.sql diff --git a/data/sql/updates/pending_db_world/gargoyles.sql b/data/sql/updates/pending_db_world/gargoyles.sql new file mode 100644 index 000000000..0ec1048f5 --- /dev/null +++ b/data/sql/updates/pending_db_world/gargoyles.sql @@ -0,0 +1,3 @@ + +-- Remove Creature_addon tables from some Gargoyles. +DELETE FROM `creature_addon` WHERE (`guid` IN (100016, 100017, 100018, 100032, 100033, 100034, 100035, 100056, 100057, 100058, 100059, 100060, 100061)); From 72d3db08e50f48a00b4571704cf5b706cc035f60 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 10 Aug 2025 14:19:02 +0000 Subject: [PATCH 002/155] chore(DB): import pending files Referenced commit(s): 468457a63ef84c2f359bf29c126c9b4b339bdf05 --- .../gargoyles.sql => db_world/2025_08_10_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/gargoyles.sql => db_world/2025_08_10_00.sql} (82%) diff --git a/data/sql/updates/pending_db_world/gargoyles.sql b/data/sql/updates/db_world/2025_08_10_00.sql similarity index 82% rename from data/sql/updates/pending_db_world/gargoyles.sql rename to data/sql/updates/db_world/2025_08_10_00.sql index 0ec1048f5..cbbdd0811 100644 --- a/data/sql/updates/pending_db_world/gargoyles.sql +++ b/data/sql/updates/db_world/2025_08_10_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_08_01 -> 2025_08_10_00 -- Remove Creature_addon tables from some Gargoyles. DELETE FROM `creature_addon` WHERE (`guid` IN (100016, 100017, 100018, 100032, 100033, 100034, 100035, 100056, 100057, 100058, 100059, 100060, 100061)); From 5b36ba898ecc7454f8932735484d2f399bedcb12 Mon Sep 17 00:00:00 2001 From: Christian M Date: Sun, 10 Aug 2025 15:06:57 -0400 Subject: [PATCH 003/155] fix(DB/creature_loot): Remove wotlk skinning items from creature loot tables (#22645) --- data/sql/updates/pending_db_world/rev_1754674822967930380.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1754674822967930380.sql diff --git a/data/sql/updates/pending_db_world/rev_1754674822967930380.sql b/data/sql/updates/pending_db_world/rev_1754674822967930380.sql new file mode 100644 index 000000000..c564974fa --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1754674822967930380.sql @@ -0,0 +1,4 @@ +-- Delete Nerubian Chitin, Borean Leather, and Arctic Fur from loot table from various creatures in WotLK +DELETE from `creature_loot_template` WHERE `item` = 33568; +DELETE from `creature_loot_template` WHERE `item` = 44128; +DELETE from `creature_loot_template` WHERE `item` = 38558; From ba1fcf242455b05d1f1ccb6ae9ca2809fcf76a45 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 10 Aug 2025 19:08:00 +0000 Subject: [PATCH 004/155] chore(DB): import pending files Referenced commit(s): 5b36ba898ecc7454f8932735484d2f399bedcb12 --- .../rev_1754674822967930380.sql => db_world/2025_08_10_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1754674822967930380.sql => db_world/2025_08_10_01.sql} (86%) diff --git a/data/sql/updates/pending_db_world/rev_1754674822967930380.sql b/data/sql/updates/db_world/2025_08_10_01.sql similarity index 86% rename from data/sql/updates/pending_db_world/rev_1754674822967930380.sql rename to data/sql/updates/db_world/2025_08_10_01.sql index c564974fa..0629204ae 100644 --- a/data/sql/updates/pending_db_world/rev_1754674822967930380.sql +++ b/data/sql/updates/db_world/2025_08_10_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_10_00 -> 2025_08_10_01 -- Delete Nerubian Chitin, Borean Leather, and Arctic Fur from loot table from various creatures in WotLK DELETE from `creature_loot_template` WHERE `item` = 33568; DELETE from `creature_loot_template` WHERE `item` = 44128; From 2485ff7f5fbb1029132d083cbe201a46fa2ada8c Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Sun, 10 Aug 2025 12:15:55 -0700 Subject: [PATCH 005/155] fix(Core/Transports): Improve static transport visibility (#22660) --- .../game/Entities/GameObject/GameObject.h | 4 +- .../Object/ObjectVisibilityContainer.cpp | 4 ++ .../game/Entities/Player/PlayerUpdates.cpp | 5 +- .../game/Entities/Transport/Transport.cpp | 43 +++++++++++++-- .../game/Entities/Transport/Transport.h | 6 ++- src/server/game/Grids/GridObjectLoader.cpp | 25 ++++++--- src/server/game/Maps/Map.cpp | 53 +++++++------------ src/server/game/Maps/Map.h | 3 +- src/server/game/Maps/TransportMgr.cpp | 4 +- 9 files changed, 92 insertions(+), 55 deletions(-) diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 37605de21..1b8ccd047 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -156,8 +156,8 @@ public: void SaveToDB(bool saveAddon = false); void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask, bool saveAddon = false); - bool LoadFromDB(ObjectGuid::LowType guid, Map* map) { return LoadGameObjectFromDB(guid, map, false); } - bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true); + virtual bool LoadFromDB(ObjectGuid::LowType guid, Map* map) { return LoadGameObjectFromDB(guid, map, false); } + virtual bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true); void DeleteFromDB(); void SetOwnerGUID(ObjectGuid owner) diff --git a/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp b/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp index 9831adf5a..358626180 100644 --- a/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp +++ b/src/server/game/Entities/Object/ObjectVisibilityContainer.cpp @@ -66,6 +66,10 @@ void ObjectVisibilityContainer::LinkWorldObjectVisibility(WorldObject* worldObje if (worldObject == _selfObject) return; + // Transports are special and should not be added to our visibility map + if (worldObject->IsGameObject() && worldObject->ToGameObject()->IsTransport()) + return; + // Only players can link visibility if (!_visibleWorldObjectsMap) return; diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 074dc4923..8426e4c8c 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -1645,10 +1645,7 @@ template <> inline void UpdateVisibilityOf_helper(Player* player, GameObject* target, std::vector& /*v*/) { - // @HACK: This is to prevent objects like deeprun tram from disappearing - // when player moves far from its spawn point while riding it - if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT)) - player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); + player->GetObjectVisibilityContainer().LinkWorldObjectVisibility(target); } template <> diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 251cdb936..ee87af41d 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -607,12 +607,12 @@ void MotionTransport::DelayedTeleportTransport() } Map* newMap = sMapMgr->CreateBaseMap(newMapId); - GetMap()->RemoveFromMap(this, false); + GetMap()->RemoveFromMap(this, false); newMap->LoadGrid(x, y); // xinef: load before adding passengers to new map SetMap(newMap); Relocate(x, y, z, o); - GetMap()->AddToMap(this); + GetMap()->AddToMap(this); LoadStaticPassengers(); } @@ -690,6 +690,40 @@ StaticTransport::~StaticTransport() ASSERT(_passengers.empty()); } +bool StaticTransport::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap) +{ + GameObjectData const* data = sObjectMgr->GetGameObjectData(spawnId); + + if (!data) + { + LOG_ERROR("sql.sql", "Gameobject (GUID: {}) not found in table `gameobject`, can't load. ", spawnId); + return false; + } + + uint32 entry = data->id; + //uint32 map_id = data->mapid; // already used before call + uint32 phaseMask = data->phaseMask; + float x = data->posX; + float y = data->posY; + float z = data->posZ; + float ang = data->orientation; + + uint32 animprogress = data->animprogress; + GOState go_state = data->go_state; + uint32 artKit = data->artKit; + + m_goData = data; + m_spawnId = spawnId; + + if (!Create(map->GenerateLowGuid(), entry, map, phaseMask, x, y, z, ang, data->rotation, animprogress, go_state, artKit)) + return false; + + if (addToMap && !GetMap()->AddToMap(this)) + return false; + + return true; +} + bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit) { ASSERT(map); @@ -794,7 +828,6 @@ bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* m LastUsedScriptID = GetGOInfo()->ScriptId; AIM_Initialize(); - this->setActive(true); return true; } @@ -929,7 +962,9 @@ void StaticTransport::UpdatePosition(float x, float y, float z, float o) if (!GetMap()->IsGridLoaded(x, y)) // pussywizard: should not happen, but just in case GetMap()->LoadGrid(x, y); - GetMap()->GameObjectRelocation(this, x, y, z, o); // this also relocates the model + Relocate(x, y, z, o); + UpdateModelPosition(); + UpdatePassengerPositions(); } diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 005e9d789..9b50775af 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -38,6 +38,8 @@ public: virtual void RemovePassenger(WorldObject* passenger, bool withAll = false) = 0; PassengerSet const& GetPassengers() const { return _passengers; } + virtual void DelayedUpdate(uint32 /*diff*/) {} + uint32 GetPathProgress() const { return GetGOValue()->Transport.PathProgress; } void SetPathProgress(uint32 val) { m_goValue.Transport.PathProgress = val; } @@ -57,7 +59,7 @@ public: void BuildUpdate(UpdateDataMapType& data_map) override; void Update(uint32 diff) override; - void DelayedUpdate(uint32 diff); + void DelayedUpdate(uint32 diff) override; void UpdatePosition(float x, float y, float z, float o); void AddPassenger(WorldObject* passenger, bool withAll = false) override; @@ -115,6 +117,8 @@ public: StaticTransport(); ~StaticTransport() override; + bool LoadFromDB(ObjectGuid::LowType guid, Map* map) override { return LoadGameObjectFromDB(guid, map, false); } + bool LoadGameObjectFromDB(ObjectGuid::LowType guid, Map* map, bool addToMap = true) override; bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit = 0) override; void CleanupsBeforeDelete(bool finalCleanup = true) override; void BuildUpdate(UpdateDataMapType& data_map) override; diff --git a/src/server/game/Grids/GridObjectLoader.cpp b/src/server/game/Grids/GridObjectLoader.cpp index 6a5b4b736..d8b39ebd6 100644 --- a/src/server/game/Grids/GridObjectLoader.cpp +++ b/src/server/game/Grids/GridObjectLoader.cpp @@ -64,15 +64,28 @@ void GridObjectLoader::LoadGameObjects(CellGuidSet const& guid_set, Map* map) for (ObjectGuid::LowType const& guid : guid_set) { GameObjectData const* data = sObjectMgr->GetGameObjectData(guid); - GameObject* obj = data && sObjectMgr->IsGameObjectStaticTransport(data->id) ? new StaticTransport() : new GameObject(); - if (!obj->LoadFromDB(guid, map)) + if (data && sObjectMgr->IsGameObjectStaticTransport(data->id)) { - delete obj; - continue; - } + StaticTransport* transport = new StaticTransport(); - AddObjectHelper(map, obj); + // Special case for static transports - we are loaded via grids + // but we do not want to actually be stored in the grid + if (!transport->LoadGameObjectFromDB(guid, map, true)) + delete transport; + } + else + { + GameObject* obj = new GameObject(); + + if (!obj->LoadFromDB(guid, map)) + { + delete obj; + continue; + } + + AddObjectHelper(map, obj); + } } } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index e06a22d64..9ad4cfb4e 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -345,7 +345,7 @@ bool Map::AddToMap(T* obj, bool checkTransport) } template<> -bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/) +bool Map::AddToMap(Transport* obj, bool /*checkTransport*/) { //TODO: Needs clean up. An object should not be added to map twice. if (obj->IsInWorld()) @@ -360,26 +360,22 @@ bool Map::AddToMap(MotionTransport* obj, bool /*checkTransport*/) } Cell cell(cellCoord); - if (obj->isActiveObject()) - EnsureGridLoaded(cell); + EnsureGridLoaded(cell); obj->AddToWorld(); _transports.insert(obj); // Broadcast creation to players - if (!GetPlayers().IsEmpty()) + for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) { - for (Map::PlayerList::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + if (itr->GetSource()->GetTransport() != obj) { - if (itr->GetSource()->GetTransport() != obj) - { - UpdateData data; - obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource()); - WorldPacket packet; - data.BuildPacket(packet); - itr->GetSource()->SendDirectMessage(&packet); - } + UpdateData data; + obj->BuildCreateUpdateBlockForPlayer(&data, itr->GetSource()); + WorldPacket packet; + data.BuildPacket(packet); + itr->GetSource()->SendDirectMessage(&packet); } } @@ -667,7 +663,7 @@ void Map::RemoveFromMap(T* obj, bool remove) } template<> -void Map::RemoveFromMap(MotionTransport* obj, bool remove) +void Map::RemoveFromMap(Transport* obj, bool remove) { obj->RemoveFromWorld(); @@ -697,8 +693,6 @@ void Map::RemoveFromMap(MotionTransport* obj, bool remove) obj->ResetMap(); - // Transports are never actually deleted, but it *should* be safe to clear - // from update list when removing from world RemoveObjectFromMapUpdateList(obj); if (remove) @@ -966,11 +960,10 @@ void Map::UnloadAll() for (TransportsContainer::iterator itr = _transports.begin(); itr != _transports.end();) { - MotionTransport* transport = *itr; + Transport* transport = *itr; ++itr; - transport->RemoveFromWorld(); - delete transport; + RemoveFromMap(transport, true); } _transports.clear(); @@ -1589,6 +1582,9 @@ void Map::SendInitSelf(Player* player) void Map::SendInitTransports(Player* player) { + if (_transports.empty()) + return; + // Hack to send out transports UpdateData transData; for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) @@ -1605,24 +1601,15 @@ void Map::SendInitTransports(Player* player) void Map::SendRemoveTransports(Player* player) { + if (_transports.empty()) + return; + // Hack to send out transports UpdateData transData; for (TransportsContainer::const_iterator itr = _transports.begin(); itr != _transports.end(); ++itr) if (*itr != player->GetTransport()) (*itr)->BuildOutOfRangeUpdateBlock(&transData); - // pussywizard: remove static transports from client - /*for (GuidUnorderedSet::const_iterator it = player->m_clientGUIDs.begin(); it != player->m_clientGUIDs.end(); ) - { - if ((*it).IsTransport()) - { - transData.AddOutOfRangeGUID(*it); - it = player->m_clientGUIDs.erase(it); - } - else - ++it; - }*/ - if (!transData.HasData()) return; @@ -1693,7 +1680,7 @@ void Map::DelayedUpdate(const uint32 t_diff) { for (_transportsUpdateIter = _transports.begin(); _transportsUpdateIter != _transports.end();) { - MotionTransport* transport = *_transportsUpdateIter; + Transport* transport = *_transportsUpdateIter; ++_transportsUpdateIter; if (!transport->IsInWorld()) @@ -1738,7 +1725,7 @@ void Map::RemoveAllObjectsInRemoveList() RemoveFromMap((DynamicObject*)obj, true); break; case TYPEID_GAMEOBJECT: - if (MotionTransport* transport = obj->ToGameObject()->ToMotionTransport()) + if (Transport* transport = obj->ToGameObject()->ToTransport()) RemoveFromMap(transport, true); else RemoveFromMap(obj->ToGameObject(), true); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index fb24b0924..df1f73cfe 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -146,7 +146,7 @@ struct ZoneDynamicInfo typedef std::map CreatureGroupHolderType; typedef std::unordered_map ZoneDynamicInfoMap; -typedef std::set TransportsContainer; +typedef std::unordered_set TransportsContainer; enum EncounterCreditType : uint8 { @@ -535,7 +535,6 @@ protected: MapRefMgr m_mapRefMgr; MapRefMgr::iterator m_mapRefIter; - // Objects that must update even in inactive grids without activating them TransportsContainer _transports; TransportsContainer::iterator _transportsUpdateIter; diff --git a/src/server/game/Maps/TransportMgr.cpp b/src/server/game/Maps/TransportMgr.cpp index ad319e943..f065ccf56 100644 --- a/src/server/game/Maps/TransportMgr.cpp +++ b/src/server/game/Maps/TransportMgr.cpp @@ -412,10 +412,8 @@ MotionTransport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType if (map && map->IsDungeon()) trans->m_zoneScript = map->ToInstanceMap()->GetInstanceScript(); - // xinef: transports are active so passengers can be relocated (grids must be loaded) - trans->setActive(true); HashMapHolder::Insert(trans); - trans->GetMap()->AddToMap(trans); + trans->GetMap()->AddToMap(trans); return trans; } From 2450237b7d11fe5ea60e000f8e9a0d800fc9910e Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Sun, 10 Aug 2025 12:16:04 -0700 Subject: [PATCH 006/155] fix(Core/Maps): Improve large object updater (#22659) --- src/server/game/Entities/Creature/Creature.cpp | 2 +- src/server/game/Entities/GameObject/GameObject.cpp | 2 +- src/server/game/Maps/Map.h | 2 +- src/server/scripts/Commands/cs_debug.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 8c070c6db..8ef6206bb 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -3903,7 +3903,7 @@ bool Creature::IsUpdateNeeded() if (IsInCombat()) return true; - if (IsVisibilityOverridden()) + if (!GetObjectVisibilityContainer().GetVisiblePlayersMap().empty()) return true; if (ToTempSummon()) diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 827dc38dd..1fa5a4177 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -3086,7 +3086,7 @@ bool GameObject::IsUpdateNeeded() if (GetMap()->isCellMarked(GetCurrentCell().GetCellCoord().GetId())) return true; - if (IsVisibilityOverridden()) + if (!GetObjectVisibilityContainer().GetVisiblePlayersMap().empty()) return true; if (IsTransport()) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index df1f73cfe..f43111633 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -479,7 +479,7 @@ public: _updateObjects.erase(obj); } - size_t GetUpdateObjectsCount() const { return _updateObjects.size(); } + size_t GetUpdatableObjectsCount() const { return _updatableObjectList.size(); } virtual std::string GetDebugInfo() const; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 3198936fa..a9fe5d9e0 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -1351,7 +1351,7 @@ public: map->GetId(), map->GetMapName(), map->GetInstanceId(), uint64(map->GetObjectsStore().Size()), uint64(map->GetObjectsStore().Size()), - uint64(map->GetUpdateObjectsCount())); + uint64(map->GetUpdatableObjectsCount())); CreatureCountWorker worker; TypeContainerVisitor visitor(worker); From 1d70c6acc3192684ac1665057cfa9bfd4f7aaffa Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Sun, 10 Aug 2025 17:18:53 -0300 Subject: [PATCH 007/155] =?UTF-8?q?fix(Scripts/Pet):=20Fix=20Risen=20Ghoul?= =?UTF-8?q?=20behavior=20that=20does=20not=20automaticall=E2=80=A6=20(#225?= =?UTF-8?q?46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/scripts/Pet/pet_dk.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/server/scripts/Pet/pet_dk.cpp b/src/server/scripts/Pet/pet_dk.cpp index c2eedba77..d1df1ef83 100644 --- a/src/server/scripts/Pet/pet_dk.cpp +++ b/src/server/scripts/Pet/pet_dk.cpp @@ -245,6 +245,20 @@ struct npc_pet_dk_ghoul : public CombatAI { npc_pet_dk_ghoul(Creature* c) : CombatAI(c) { } + void IsSummonedBy(WorldObject* summoner) override + { + if (!summoner || !summoner->IsPlayer()) + return; + + Player* player = summoner->ToPlayer(); + + if (Unit* victim = player->GetVictim()) + { + me->Attack(victim, true); + me->GetMotionMaster()->MoveChase(victim); + } + } + void JustDied(Unit* /*who*/) override { if (me->IsGuardian() || me->IsSummon()) From 12bee97df93cdf1431275722fecf8bf07d6fc2e9 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Mon, 11 Aug 2025 10:07:00 -0700 Subject: [PATCH 008/155] fix(Core/Visibility): Fix initial visibility on player reused sessions (#22673) --- src/server/game/Handlers/CharacterHandler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index f6ee1479d..2169cb531 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -1180,6 +1180,10 @@ void WorldSession::HandlePlayerLoginToCharInWorld(Player* pCurrChar) pCurrChar->GetMap()->SendInitTransports(pCurrChar); pCurrChar->GetMap()->SendInitSelf(pCurrChar); pCurrChar->GetMap()->SendZoneDynamicInfo(pCurrChar); + + // If we are logging into an existing player, simply clear visibility references + // so player will receive a fresh list of new objects on the next vis update. + pCurrChar->GetObjectVisibilityContainer().CleanVisibilityReferences(); pCurrChar->UpdateObjectVisibility(false); pCurrChar->CleanupChannels(); From 2e07a0fe1f6f6f1af9dffd7e6a7f8f19a2082599 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Tue, 12 Aug 2025 14:23:25 +0200 Subject: [PATCH 009/155] fix(DB/SAI): Fezzix Geartwist now flies correctly and the wreckages despawn. (#22674) --- data/sql/updates/pending_db_world/Fezzix.sql | 41 ++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Fezzix.sql diff --git a/data/sql/updates/pending_db_world/Fezzix.sql b/data/sql/updates/pending_db_world/Fezzix.sql new file mode 100644 index 000000000..772136e73 --- /dev/null +++ b/data/sql/updates/pending_db_world/Fezzix.sql @@ -0,0 +1,41 @@ + +-- Set SmartAI (Wreckage A, B, C) +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE (`entry`IN (188087, 188088, 188089)); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 1) AND (`entryorguid` IN (188087, 188088, 188089)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(188087, 1, 0, 0, 38, 0, 100, 0, 0, 1, 0, 0, 0, 0, 41, 0, 60, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wreckage A - On Data Set 0 1 - Despawn Instant'), +(188088, 1, 0, 0, 38, 0, 100, 0, 0, 1, 0, 0, 0, 0, 41, 0, 60, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wreckage B - On Data Set 0 1 - Despawn Instant'), +(188089, 1, 0, 0, 38, 0, 100, 0, 0, 1, 0, 0, 0, 0, 41, 0, 60, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Wreckage C - On Data Set 0 1 - Despawn Instant'); + +-- Set SmartAI (Fezzix Geartwist) +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 25849; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 25849); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(25849, 0, 0, 0, 25, 0, 100, 512, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Reset - Set Event Phase 1'), +(25849, 0, 1, 2, 20, 1, 100, 0, 11894, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Quest \'Patching Up\' Finished - Say Line 0 (Phase 1)'), +(25849, 0, 2, 0, 61, 1, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2584900, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Quest \'Patching Up\' Finished - Run Script (Phase 1)'), +(25849, 0, 3, 0, 40, 2, 100, 512, 11, 25849, 0, 0, 0, 0, 80, 2584901, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Point 11 of Path 25849 Reached - Run Script (Phase 2)'), +(25849, 0, 4, 5, 40, 2, 100, 512, 12, 25849, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - On Point 12 of Path 25849 Reached - Set Event Phase 1 (Phase 2)'), +(25849, 0, 5, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4.06662, 'Fezzix Geartwist - On Point 12 of Path 25849 Reached - Set Orientation 4.06662 (Phase 2)'); + +-- Set Timed Actionlist (Fezzix Geartwist) +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND (`entryorguid` IN (2584900, 2584901)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(2584900, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Event Phase 2'), +(2584900, 9, 1, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 12, 26040, 1, 13000, 0, 0, 0, 8, 0, 0, 0, 0, 3481.33, 4099.85, 17.839, 3.35103, 'Fezzix Geartwist - Actionlist - Summon Creature \'Fezzix\'s Flying Machine\''), +(2584900, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 1, 0, 0, 0, 0, 14, 60069, 188087, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Data 0 1'), +(2584900, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 1, 0, 0, 0, 0, 14, 60080, 188088, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Data 0 1'), +(2584900, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 0, 1, 0, 0, 0, 0, 14, 60095, 188089, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Data 0 1'), +(2584900, 9, 5, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Say Line 1'), +(2584900, 9, 6, 0, 0, 0, 100, 0, 9000, 9000, 0, 0, 0, 0, 43, 0, 22719, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Mount To Model 22719'), +(2584900, 9, 7, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Say Line 2'), +(2584900, 9, 8, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 60, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Fly On'), +(2584900, 9, 9, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 53, 0, 25849, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Start Waypoint Path 25849'), +(2584901, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 46419, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Cast \'Cosmetic - Explosion\''), +(2584901, 9, 1, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Dismount'), +(2584901, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Fly Off'), +(2584901, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 42963, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Cast \'Cosmetic - Combat Knockdown Self\''), +(2584901, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Say Line 3'), +(2584901, 9, 5, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Say Line 4'); From 9efdd9798f9789590f7533afac0e4ac857d01d42 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 12 Aug 2025 12:24:27 +0000 Subject: [PATCH 010/155] chore(DB): import pending files Referenced commit(s): 2e07a0fe1f6f6f1af9dffd7e6a7f8f19a2082599 --- .../{pending_db_world/Fezzix.sql => db_world/2025_08_12_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Fezzix.sql => db_world/2025_08_12_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/Fezzix.sql b/data/sql/updates/db_world/2025_08_12_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/Fezzix.sql rename to data/sql/updates/db_world/2025_08_12_00.sql index 772136e73..430efa851 100644 --- a/data/sql/updates/pending_db_world/Fezzix.sql +++ b/data/sql/updates/db_world/2025_08_12_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_10_01 -> 2025_08_12_00 -- Set SmartAI (Wreckage A, B, C) UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE (`entry`IN (188087, 188088, 188089)); From 743b6e7cd971808bf57efd94ba61f9db98b15e34 Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Tue, 12 Aug 2025 13:10:20 -0300 Subject: [PATCH 011/155] =?UTF-8?q?fix(Scripts/Pet):=20Fixes=20behavior=20?= =?UTF-8?q?of=20Army=20of=20the=20Dead=20Ghouls=20to=20attack=E2=80=A6=20(?= =?UTF-8?q?#22665)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/scripts/Pet/pet_dk.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/server/scripts/Pet/pet_dk.cpp b/src/server/scripts/Pet/pet_dk.cpp index d1df1ef83..1a8a81540 100644 --- a/src/server/scripts/Pet/pet_dk.cpp +++ b/src/server/scripts/Pet/pet_dk.cpp @@ -292,6 +292,28 @@ struct npc_pet_dk_army_of_the_dead : public CombatAI CombatAI::InitializeAI(); ((Minion*)me)->SetFollowAngle(rand_norm() * 2 * M_PI); } + + void IsSummonedBy(WorldObject* summoner) override + { + if (Unit* owner = summoner->ToUnit()) + { + Unit* victim = owner->GetVictim(); + + if (victim && me->IsValidAttackTarget(victim)) + { + AttackStart(victim); + } + else + { + // If there is no valid target, attack the nearest enemy within 30m + if (Unit* nearest = me->SelectNearbyTarget(nullptr, 30.0f)) + { + if (me->IsValidAttackTarget(nearest)) + AttackStart(nearest); + } + } + } + } }; struct npc_pet_dk_dancing_rune_weapon : public NullCreatureAI From 5369aec3c9df2e2888d3312f5e087b43e6614abf Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 12 Aug 2025 14:31:08 -0300 Subject: [PATCH 012/155] =?UTF-8?q?fix(Core/SAI):=20=20Force=20SMC=20creat?= =?UTF-8?q?ures=20to=20resume=20chasing=20victims=20once=20in=E2=80=A6=20(?= =?UTF-8?q?#22581)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/AI/SmartScripts/SmartAI.cpp | 2 ++ src/server/game/AI/SmartScripts/SmartAI.h | 5 +++++ src/server/game/AI/SmartScripts/SmartScript.cpp | 2 ++ src/server/game/Entities/Unit/Unit.cpp | 14 ++++++++++++-- 4 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index d48cf2681..4c2b252e2 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -72,6 +72,8 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c) mcanSpawn = true; + _chaseOnInterrupt = false; + // Xinef: Vehicle conditions m_ConditionsTimer = 0; if (me->GetVehicleKit()) diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index ede658a47..1cc940df4 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -212,6 +212,9 @@ public: // Xinef void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; } + void SetChaseOnInterrupt(bool apply) { _chaseOnInterrupt = apply; } + [[nodiscard]] bool CanChaseOnInterrupt() const { return _chaseOnInterrupt; } + private: bool mIsCharmed; uint32 mFollowCreditType; @@ -257,6 +260,8 @@ private: void CheckConditions(const uint32 diff); ConditionList conditions; uint32 m_ConditionsTimer; + + bool _chaseOnInterrupt; }; class SmartGameObjectAI : public GameObjectAI diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 24a3d6901..2cc4a246b 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -742,6 +742,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) { + CAST_AI(SmartAI, me->AI())->SetChaseOnInterrupt(true); + if (!me->isMoving()) // Don't try to reposition while we are moving { // If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS. diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index f65cdbd97..a2a33e89d 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -53,6 +53,7 @@ #include "Player.h" #include "ReputationMgr.h" #include "ScriptMgr.h" +#include "SmartAI.h" #include "Spell.h" #include "SpellAuraEffects.h" #include "SpellAuras.h" @@ -4109,8 +4110,8 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi //LOG_DEBUG("entities.unit", "Interrupt spell for unit {}.", GetEntry()); Spell* spell = m_currentSpells[spellType]; if (spell - && (withDelayed || spell->getState() != SPELL_STATE_DELAYED) - && (withInstant || spell->GetCastTime() > 0 || spell->getState() == SPELL_STATE_CASTING)) // xinef: or spell is in casting state (channeled spells only) + && (withDelayed || spell->getState() != SPELL_STATE_DELAYED) + && (withInstant || spell->GetCastTime() > 0 || spell->getState() == SPELL_STATE_CASTING)) // xinef: or spell is in casting state (channeled spells only) { // for example, do not let self-stun aura interrupt itself if (!spell->IsInterruptable()) @@ -4128,6 +4129,15 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi m_currentSpells[spellType] = nullptr; spell->SetReferencedFromCurrent(false); } + + // SAI creatures only + // Start chasing victim if they are spell casters (at least one SMC spell) if interrupted/silenced. + if (IsCreature()) + { + if (SmartAI* ai = dynamic_cast(ToCreature()->AI())) + if (ai->CanChaseOnInterrupt()) + ai->SetCombatMove(true); + } } } From 58504b8a3624bece1bc82c62d334b7b072b09fe2 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Tue, 12 Aug 2025 20:54:09 +0200 Subject: [PATCH 013/155] Fix Error (#22681) --- data/sql/updates/db_world/2025_08_12_00.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/sql/updates/db_world/2025_08_12_00.sql b/data/sql/updates/db_world/2025_08_12_00.sql index 430efa851..c25388c09 100644 --- a/data/sql/updates/db_world/2025_08_12_00.sql +++ b/data/sql/updates/db_world/2025_08_12_00.sql @@ -22,7 +22,7 @@ INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_ (25849, 0, 5, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 4.06662, 'Fezzix Geartwist - On Point 12 of Path 25849 Reached - Set Orientation 4.06662 (Phase 2)'); -- Set Timed Actionlist (Fezzix Geartwist) -DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND (`entryorguid` IN (2584900, 2584901)); +DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (2584900, 2584901)); INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES (2584900, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fezzix Geartwist - Actionlist - Set Event Phase 2'), (2584900, 9, 1, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 12, 26040, 1, 13000, 0, 0, 0, 8, 0, 0, 0, 0, 3481.33, 4099.85, 17.839, 3.35103, 'Fezzix Geartwist - Actionlist - Summon Creature \'Fezzix\'s Flying Machine\''), From d917eec2614fdeb5225145de8e136b0d7a578369 Mon Sep 17 00:00:00 2001 From: v-mstrs <104088833+v-mstrs@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:09:37 +0200 Subject: [PATCH 014/155] fix(DB/Spell): Allow Sunder Armor to stack (#22669) --- .../sql/updates/pending_db_world/rev_1754911204417603800.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1754911204417603800.sql diff --git a/data/sql/updates/pending_db_world/rev_1754911204417603800.sql b/data/sql/updates/pending_db_world/rev_1754911204417603800.sql new file mode 100644 index 000000000..f07b70f79 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1754911204417603800.sql @@ -0,0 +1,5 @@ +-- Anub'ar Guardian - Sunder Armor, Sunder Armor(H) +DELETE FROM `spell_custom_attr` WHERE `spell_id` IN (53618, 59350); +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES +(53618, 4194304), +(59350, 4194304); From 55a8e8514503231c2d06ef42b229f91526dd89a3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 12 Aug 2025 22:10:39 +0000 Subject: [PATCH 015/155] chore(DB): import pending files Referenced commit(s): d917eec2614fdeb5225145de8e136b0d7a578369 --- .../rev_1754911204417603800.sql => db_world/2025_08_12_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1754911204417603800.sql => db_world/2025_08_12_01.sql} (83%) diff --git a/data/sql/updates/pending_db_world/rev_1754911204417603800.sql b/data/sql/updates/db_world/2025_08_12_01.sql similarity index 83% rename from data/sql/updates/pending_db_world/rev_1754911204417603800.sql rename to data/sql/updates/db_world/2025_08_12_01.sql index f07b70f79..0f712fbf3 100644 --- a/data/sql/updates/pending_db_world/rev_1754911204417603800.sql +++ b/data/sql/updates/db_world/2025_08_12_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_12_00 -> 2025_08_12_01 -- Anub'ar Guardian - Sunder Armor, Sunder Armor(H) DELETE FROM `spell_custom_attr` WHERE `spell_id` IN (53618, 59350); INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES From 5ad345a873f5d3e0923c022ed0f12891471989cb Mon Sep 17 00:00:00 2001 From: sudlud Date: Wed, 13 Aug 2025 08:48:22 +0200 Subject: [PATCH 016/155] fix(Conf/Logs): fix Error appender log level, enable sql.updates error logging (#22682) --- src/server/apps/worldserver/worldserver.conf.dist | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 9034da67a..9b36fe680 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -656,7 +656,7 @@ Allow.IP.Based.Action.Logging = 0 Appender.Console=1,4,0,"1 9 3 6 5 8" Appender.Server=2,5,0,Server.log,w # Appender.GM=2,5,15,gm_%s.log -Appender.Errors=2,5,0,Errors.log,w +Appender.Errors=2,2,0,Errors.log,w # Appender.DB=3,5,0 # Logger config values: Given a logger "name" @@ -685,6 +685,7 @@ Logger.mmaps=4,Server Logger.scripts.hotswap=4,Console Server Logger.server=4,Console Server Logger.sql.sql=2,Console Errors +Logger.sql.updates=4,Console Server Errors Logger.sql=4,Console Server Logger.time.update=4,Console Server Logger.module=4,Console Server @@ -783,7 +784,6 @@ Logger.spells.scripts=2,Console Errors #Logger.spells=4,Console Server #Logger.sql.dev=4,Console Server #Logger.sql.driver=4,Console Server -#Logger.sql.updates=4,Console Server #Logger.vehicles=4,Console Server #Logger.warden=4,Console Server #Logger.weather=4,Console Server From 2877502fb337b3ee1fd1469906d0caf2c35506d6 Mon Sep 17 00:00:00 2001 From: v-mstrs <104088833+v-mstrs@users.noreply.github.com> Date: Wed, 13 Aug 2025 13:35:27 +0200 Subject: [PATCH 017/155] fix(DB/Spell): Allow Poison Bolt to stack (#22668) --- .../updates/pending_db_world/rev_1754910707047949800.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1754910707047949800.sql diff --git a/data/sql/updates/pending_db_world/rev_1754910707047949800.sql b/data/sql/updates/pending_db_world/rev_1754910707047949800.sql new file mode 100644 index 000000000..d9231f97d --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1754910707047949800.sql @@ -0,0 +1,7 @@ +-- Anub'ar Venomancer - Poison Bolt +DELETE FROM `spell_custom_attr` WHERE `spell_id` = 53617; +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES (53617, 4194304); + +-- Anub'ar Venomancer - Poison Bolt(H) +DELETE FROM `spell_custom_attr` WHERE `spell_id` = 59359; +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES (59359, 4194304); From 8bffcab5cfe98bd1253fa70eb625d18704099b00 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 13 Aug 2025 11:36:33 +0000 Subject: [PATCH 018/155] chore(DB): import pending files Referenced commit(s): 2877502fb337b3ee1fd1469906d0caf2c35506d6 --- .../rev_1754910707047949800.sql => db_world/2025_08_13_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1754910707047949800.sql => db_world/2025_08_13_00.sql} (89%) diff --git a/data/sql/updates/pending_db_world/rev_1754910707047949800.sql b/data/sql/updates/db_world/2025_08_13_00.sql similarity index 89% rename from data/sql/updates/pending_db_world/rev_1754910707047949800.sql rename to data/sql/updates/db_world/2025_08_13_00.sql index d9231f97d..719b7b844 100644 --- a/data/sql/updates/pending_db_world/rev_1754910707047949800.sql +++ b/data/sql/updates/db_world/2025_08_13_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_12_01 -> 2025_08_13_00 -- Anub'ar Venomancer - Poison Bolt DELETE FROM `spell_custom_attr` WHERE `spell_id` = 53617; INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES (53617, 4194304); From 3e36b35a158ad37a71ecbbccdf7b250cccfdc6b8 Mon Sep 17 00:00:00 2001 From: Christian M Date: Thu, 14 Aug 2025 07:58:36 -0400 Subject: [PATCH 019/155] fix(DB/SAI): Some Azjob-Nerub spell fixes (#22603) --- .../updates/pending_db_world/rev_1753816572154118506.sql | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1753816572154118506.sql diff --git a/data/sql/updates/pending_db_world/rev_1753816572154118506.sql b/data/sql/updates/pending_db_world/rev_1753816572154118506.sql new file mode 100644 index 000000000..09f9b5bf6 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1753816572154118506.sql @@ -0,0 +1,9 @@ +-- Allow Anub'ar warrior to use Strike ability. (Set the event_type 0 to "incombat update" instead of "out of combat update") +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28732; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28732) AND (`source_type` = 0) AND (`id` IN (0)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28732, 0, 0, 0, 0, 0, 100, 0, 2000, 5000, 6000, 8000, 0, 0, 11, 52532, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, "Anub'ar Warrior - In Combat - Cast Strike"); + +-- Fix misplaced spell IDs in spelldifficulty for Skittering Infector's Acid Splash +UPDATE `spelldifficulty_dbc` SET `DifficultySpellID_1` = 52446 WHERE `ID` = 59363; +UPDATE `spelldifficulty_dbc` SET `DifficultySpellID_2` = 59363 WHERE `ID` = 59363; From 1878efda1eb469868a5164db30416a34f0ef3246 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 14 Aug 2025 11:59:36 +0000 Subject: [PATCH 020/155] chore(DB): import pending files Referenced commit(s): 3e36b35a158ad37a71ecbbccdf7b250cccfdc6b8 --- .../rev_1753816572154118506.sql => db_world/2025_08_14_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1753816572154118506.sql => db_world/2025_08_14_00.sql} (96%) diff --git a/data/sql/updates/pending_db_world/rev_1753816572154118506.sql b/data/sql/updates/db_world/2025_08_14_00.sql similarity index 96% rename from data/sql/updates/pending_db_world/rev_1753816572154118506.sql rename to data/sql/updates/db_world/2025_08_14_00.sql index 09f9b5bf6..0323cc323 100644 --- a/data/sql/updates/pending_db_world/rev_1753816572154118506.sql +++ b/data/sql/updates/db_world/2025_08_14_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_13_00 -> 2025_08_14_00 -- Allow Anub'ar warrior to use Strike ability. (Set the event_type 0 to "incombat update" instead of "out of combat update") UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28732; DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28732) AND (`source_type` = 0) AND (`id` IN (0)); From 1a019f967d910a5eeabaf2f2fe68bdeb697e5834 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Mon, 18 Aug 2025 11:32:43 -0400 Subject: [PATCH 021/155] fix(DB/Creature): Add more spawns for Isle of Quel'Danas harbor quest credit creatures. (#22698) --- .../keep-the-enemy-at-bay-spawns.sql | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 data/sql/updates/pending_db_world/keep-the-enemy-at-bay-spawns.sql diff --git a/data/sql/updates/pending_db_world/keep-the-enemy-at-bay-spawns.sql b/data/sql/updates/pending_db_world/keep-the-enemy-at-bay-spawns.sql new file mode 100644 index 000000000..fe4d6cb4c --- /dev/null +++ b/data/sql/updates/pending_db_world/keep-the-enemy-at-bay-spawns.sql @@ -0,0 +1,65 @@ +SET @CGUID := 82950; + +DELETE FROM `creature` WHERE `id1` IN (25090, 25091, 25092); +DELETE FROM `creature` WHERE `id1` IN (25090, 25091, 25092) AND `guid` BETWEEN @CGUID+0 AND @CGUID+57; +INSERT INTO `creature` (`guid`, `id1`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `VerifiedBuild`, `CreateObject`) VALUES +(@CGUID+0 , 25090, 530, 0, 0, 1, 1, 0, 13196.1201171875, -7049.33642578125, 16.22812080383300781, 0.855211317539215087, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+1 , 25090, 530, 0, 0, 1, 1, 0, 13210.173828125 , -7052.376953125 , 16.07102394104003906, 4.572762489318847656, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+2 , 25090, 530, 0, 0, 1, 1, 0, 13202.560546875 , -7051.39697265625, 16.39847373962402343, 4.188790321350097656, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+3 , 25090, 530, 0, 0, 1, 1, 0, 13201.0830078125, -7048.72509765625, 13.21125602722167968, 0.191986218094825744, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+4 , 25090, 530, 0, 0, 1, 1, 0, 13199.25 , -7050.6953125 , 14.45721721649169921, 3.700098037719726562, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+5 , 25090, 530, 0, 0, 1, 1, 0, 13207.0126953125, -7053.20068359375, 15.47437477111816406, 2.740166902542114257, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+6 , 25090, 530, 0, 0, 1, 1, 0, 13246.345703125 , -7053.97412109375, 20.62376213073730468, 4.904375076293945312, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+7 , 25090, 530, 0, 0, 1, 1, 0, 13242.1611328125, -7054.7880859375 , 17.20347023010253906, 4.764749050140380859, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+8 , 25090, 530, 0, 0, 1, 1, 0, 13240.6982421875, -7053.22998046875, 14.11119270324707031, 4.97418832778930664 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+9 , 25090, 530, 0, 0, 1, 1, 0, 13235.609375 , -7053.92626953125, 15.19749736785888671, 5.934119224548339843, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+10, 25090, 530, 0, 0, 1, 1, 0, 13247.880859375 , -7055.54150390625, 18.45570755004882812, 0.575958669185638427, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+11, 25090, 530, 0, 0, 1, 1, 0, 13212.2255859375, -7054.658203125 , 17.02962112426757812, 6.108652114868164062, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+12, 25090, 530, 0, 0, 1, 1, 0, 13237.314453125 , -7053.35498046875, 18.92669677734375 , 3.351032257080078125, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+13, 25090, 530, 0, 0, 1, 1, 0, 13253.2548828125, -7054.8837890625 , 16.24456024169921875, 0.069813169538974761, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+14, 25090, 530, 0, 0, 1, 1, 0, 13274.5966796875, -7057.69384765625, 24.88401985168457031, 0.244346097111701965, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+15, 25090, 530, 0, 0, 1, 1, 0, 13264.31640625 , -7057.705078125 , 24.02816200256347656, 1.570796370506286621, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+16, 25090, 530, 0, 0, 1, 1, 0, 13261.1611328125, -7055.92529296875, 26.55978202819824218, 3.298672199249267578, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+17, 25090, 530, 0, 0, 1, 1, 0, 13255.951171875 , -7056.603515625 , 19.514129638671875 , 1.134464025497436523, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+18, 25090, 530, 0, 0, 1, 1, 0, 13262.6103515625, -7056.1162109375 , 22.68890190124511718, 4.520402908325195312, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+19, 25090, 530, 0, 0, 1, 1, 0, 13260.7216796875, -7056.51025390625, 24.51448440551757812, 5.899212837219238281, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), + +(@CGUID+20, 25091, 530, 0, 0, 1, 1, 0, 13330.6298828125, -6993.73974609375, 18.55262374877929687, 0.453785598278045654, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+21, 25091, 530, 0, 0, 1, 1, 0, 13329.9267578125, -6994.26416015625, 15.69489192962646484, 0.279252678155899047, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+22, 25091, 530, 0, 0, 1, 1, 0, 13317.7392578125, -6990.34716796875, 17.51109886169433593, 0.03490658476948738 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+23, 25091, 530, 0, 0, 1, 1, 0, 13317.330078125 , -6988.69384765625, 15.31146907806396484, 1.413716673851013183, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+24, 25091, 530, 0, 0, 1, 1, 0, 13325.728515625 , -6992.54296875 , 17.86301040649414062, 0.314159274101257324, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+25, 25091, 530, 0, 0, 1, 1, 0, 13321.5322265625, -6991.05859375 , 18.0410003662109375 , 3.473205089569091796, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+26, 25091, 530, 0, 0, 1, 1, 0, 13312.7392578125, -6989.04150390625, 16.80069160461425781, 3.03687286376953125 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+27, 25091, 530, 0, 0, 1, 1, 0, 13326.6171875 , -6991.60400390625, 15.73497295379638671, 3.874630928039550781, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+28, 25091, 530, 0, 0, 1, 1, 0, 13321.43359375 , -6992.02294921875, 15.16357707977294921, 1.850049018859863281, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+29, 25091, 530, 0, 0, 1, 1, 0, 13315.4501953125, -6990.5986328125 , 14.17850494384765625, 5.323254108428955078, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+30, 25091, 530, 0, 0, 1, 1, 0, 13351.2919921875, -6989.8095703125 , 14.9304962158203125 , 5.619960308074951171, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+31, 25091, 530, 0, 0, 1, 1, 0, 13348.5439453125, -6990.99853515625, 17.81970596313476562, 3.525565147399902343, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+32, 25091, 530, 0, 0, 1, 1, 0, 13359.8720703125, -6990.33447265625, 11.86795330047607421, 3.892084121704101562, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+33, 25091, 530, 0, 0, 1, 1, 0, 13363.31640625 , -6990.54541015625, 17.51730155944824218, 5.044001579284667968, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+34, 25091, 530, 0, 0, 1, 1, 0, 13357.7421875 , -6991.56103515625, 18.46755599975585937, 4.729842185974121093, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+35, 25091, 530, 0, 0, 1, 1, 0, 13361.9384765625, -6990.98291015625, 21.16696739196777343, 1.308996915817260742, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+36, 25091, 530, 0, 0, 1, 1, 0, 13357.88671875 , -6991.5693359375 , 15.036224365234375 , 1.850049018859863281, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+37, 25091, 530, 0, 0, 1, 1, 0, 13364.1953125 , -6991.95556640625, 18.6686553955078125 , 4.293509960174560546, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+38, 25091, 530, 0, 0, 1, 1, 0, 13374.556640625 , -6992.58837890625, 20.41219139099121093, 0.872664630413055419, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+39, 25091, 530, 0, 0, 1, 1, 0, 13372.1640625 , -6991.0869140625 , 22.58947563171386718, 4.904375076293945312, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+40, 25091, 530, 0, 0, 1, 1, 0, 13367.83203125 , -6992.177734375 , 11.62636184692382812, 2.530727386474609375, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+41, 25091, 530, 0, 0, 1, 1, 0, 13374.2861328125, -6991.216796875 , 18.20113945007324218, 5.026548385620117187, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+42, 25091, 530, 0, 0, 1, 1, 0, 13367.4873046875, -6992.15625 , 15.75841045379638671, 4.328416347503662109, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), + +(@CGUID+43, 25092, 530, 0, 0, 1, 1, 0, 13276.2861328125, -7148.3115234375 , 18.78717231750488281, 5.25344085693359375 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+44, 25092, 530, 0, 0, 1, 1, 0, 13267.578125 , -7146.2333984375 , 17.49614906311035156, 3.089232683181762695, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+45, 25092, 530, 0, 0, 1, 1, 0, 13273.84375 , -7146.33349609375, 11.37590885162353515, 0.314159274101257324, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+46, 25092, 530, 0, 0, 1, 1, 0, 13332.458984375 , -7149.9892578125 , 25.62369537353515625, 3.455751895904541015, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+47, 25092, 530, 0, 0, 1, 1, 0, 13324.3798828125, -7148.763671875 , 12.40258979797363281, 5.393067359924316406, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+48, 25092, 530, 0, 0, 1, 1, 0, 13283.3359375 , -7150.99072265625, 16.36432838439941406, 2.49582076072692871 , 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+49, 25092, 530, 0, 0, 1, 1, 0, 13306.396484375 , -7148.45556640625, 19.448272705078125 , 6.126105785369873046, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+50, 25092, 530, 0, 0, 1, 1, 0, 13323.8916015625, -7149.33056640625, 23.59075736999511718, 0.331612557172775268, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+51, 25092, 530, 0, 0, 1, 1, 0, 13314.80859375 , -7148.80078125 , 21.43866920471191406, 4.834561824798583984, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+52, 25092, 530, 0, 0, 1, 1, 0, 13308.7783203125, -7147.53515625 , 14.74446582794189453, 2.652900457382202148, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+53, 25092, 530, 0, 0, 1, 1, 0, 13336.470703125 , -7149.71533203125, 24.01339530944824218, 0.942477762699127197, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+54, 25092, 530, 0, 0, 1, 1, 0, 13279.6572265625, -7149.91162109375, 16.28713226318359375, 4.747295379638671875, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+55, 25092, 530, 0, 0, 1, 1, 0, 13285.5986328125, -7150.7265625 , 20.10992622375488281, 5.113814830780029296, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+56, 25092, 530, 0, 0, 1, 1, 0, 13315.0751953125, -7149.388671875 , 15.76729774475097656, 4.066617012023925781, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1), +(@CGUID+57, 25092, 530, 0, 0, 1, 1, 0, 13323.4755859375, -7150.04931640625, 19.51647567749023437, 2.251474618911743164, 180, 0, 0, 5468, 0, 0, 0, 0, 0, 49936, 1); From 36f04acdd5326d9901b5c7a4321065d6c4372076 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Aug 2025 15:33:47 +0000 Subject: [PATCH 022/155] chore(DB): import pending files Referenced commit(s): 1a019f967d910a5eeabaf2f2fe68bdeb697e5834 --- .../2025_08_18_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/keep-the-enemy-at-bay-spawns.sql => db_world/2025_08_18_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/keep-the-enemy-at-bay-spawns.sql b/data/sql/updates/db_world/2025_08_18_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/keep-the-enemy-at-bay-spawns.sql rename to data/sql/updates/db_world/2025_08_18_00.sql index fe4d6cb4c..02662cf65 100644 --- a/data/sql/updates/pending_db_world/keep-the-enemy-at-bay-spawns.sql +++ b/data/sql/updates/db_world/2025_08_18_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_14_00 -> 2025_08_18_00 SET @CGUID := 82950; DELETE FROM `creature` WHERE `id1` IN (25090, 25091, 25092); From 7a9f430935a3ad4895304d7c33b4f88417aee56d Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:04:06 -0400 Subject: [PATCH 023/155] fix(Scripts/SunwellPlateau): Remove Kil'jaeden's debuffs from Shield of the Blue effects. (#22687) --- .../pending_db_world/shield-of-the-buffs.sql | 3 +++ .../SunwellPlateau/boss_kiljaeden.cpp | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 data/sql/updates/pending_db_world/shield-of-the-buffs.sql diff --git a/data/sql/updates/pending_db_world/shield-of-the-buffs.sql b/data/sql/updates/pending_db_world/shield-of-the-buffs.sql new file mode 100644 index 000000000..30d7f6123 --- /dev/null +++ b/data/sql/updates/pending_db_world/shield-of-the-buffs.sql @@ -0,0 +1,3 @@ +DELETE FROM `spell_script_names` WHERE `spell_id` = 45848; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(45848, 'spell_kiljaeden_shield_of_the_blue'); diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp index 2858a5cf6..ddd75f48a 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp @@ -1181,6 +1181,26 @@ class spell_kiljaeden_dragon_breath : public SpellScript } }; +// 45848 - Shield of the Blue +class spell_kiljaeden_shield_of_the_blue : public AuraScript +{ + PrepareAuraScript(spell_kiljaeden_shield_of_the_blue); + + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* target = GetTarget()) + { + target->RemoveAurasDueToSpell(SPELL_FIRE_BLOOM); + target->RemoveMovementImpairingAuras(false); + } + } + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_kiljaeden_shield_of_the_blue::HandleEffectApply, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL); + } +}; + void AddSC_boss_kiljaeden() { RegisterSunwellPlateauCreatureAI(npc_kiljaeden_controller); @@ -1196,4 +1216,5 @@ void AddSC_boss_kiljaeden() RegisterSpellScript(spell_kiljaeden_armageddon_periodic_aura); RegisterSpellScript(spell_kiljaeden_armageddon_missile); RegisterSpellScript(spell_kiljaeden_dragon_breath); + RegisterSpellScript(spell_kiljaeden_shield_of_the_blue); } From f41fb5a1693e2f5a30b003f84f6e3726fa925b01 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 18 Aug 2025 19:05:07 +0000 Subject: [PATCH 024/155] chore(DB): import pending files Referenced commit(s): 7a9f430935a3ad4895304d7c33b4f88417aee56d --- .../shield-of-the-buffs.sql => db_world/2025_08_18_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/shield-of-the-buffs.sql => db_world/2025_08_18_01.sql} (79%) diff --git a/data/sql/updates/pending_db_world/shield-of-the-buffs.sql b/data/sql/updates/db_world/2025_08_18_01.sql similarity index 79% rename from data/sql/updates/pending_db_world/shield-of-the-buffs.sql rename to data/sql/updates/db_world/2025_08_18_01.sql index 30d7f6123..2a0dbeedb 100644 --- a/data/sql/updates/pending_db_world/shield-of-the-buffs.sql +++ b/data/sql/updates/db_world/2025_08_18_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_18_00 -> 2025_08_18_01 DELETE FROM `spell_script_names` WHERE `spell_id` = 45848; INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (45848, 'spell_kiljaeden_shield_of_the_blue'); From 2ae1dbeab25cd27ba1de702d1a06370448439ebe Mon Sep 17 00:00:00 2001 From: Yehonal Date: Tue, 19 Aug 2025 03:29:34 +0200 Subject: [PATCH 025/155] feat(core): add standalone functions for player settings (#22703) --- .../game/Entities/Player/PlayerSettings.cpp | 111 ++++++++++++++---- .../game/Entities/Player/PlayerSettings.h | 15 +++ 2 files changed, 101 insertions(+), 25 deletions(-) diff --git a/src/server/game/Entities/Player/PlayerSettings.cpp b/src/server/game/Entities/Player/PlayerSettings.cpp index dba4afaa5..89b719d71 100644 --- a/src/server/game/Entities/Player/PlayerSettings.cpp +++ b/src/server/game/Entities/Player/PlayerSettings.cpp @@ -18,11 +18,95 @@ #include "Player.h" #include "StringConvert.h" #include "Tokenize.h" +#include "CharacterDatabase.h" /*********************************************************/ /*** PLAYER SETTINGS SYSTEM ***/ /*********************************************************/ +namespace PlayerSettingsStore +{ + // Common helper: parse space-separated data string into PlayerSettingVector + PlayerSettingVector ParseSettingsData(std::string const& data) + { + PlayerSettingVector result; + std::vector tokens = Acore::Tokenize(data, ' ', false); + result.reserve(tokens.size()); + for (auto const& token : tokens) + { + if (token.empty()) + continue; + if (auto parsed = Acore::StringTo(token)) + result.emplace_back(*parsed); + } + return result; + } + + // Common helper: serialize PlayerSettingVector to space-separated string + std::string SerializeSettingsData(PlayerSettingVector const& settings) + { + if (settings.empty()) + return ""; + + std::ostringstream data; + data << settings[0].value; + for (size_t i = 1; i < settings.size(); ++i) + data << ' ' << settings[i].value; + return data.str(); + } + + // helper: load a single source row for a player and parse to vector + static PlayerSettingVector LoadPlayerSettings(uint32 playerLowGuid, std::string const& source) + { + PlayerSettingVector result; + + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SETTINGS); + stmt->SetData(0, playerLowGuid); + PreparedQueryResult dbRes = CharacterDatabase.Query(stmt); + if (!dbRes) + return result; + + do + { + Field* fields = dbRes->Fetch(); + std::string rowSource = fields[0].Get(); + if (rowSource != source) + continue; + + std::string data = fields[1].Get(); + return ParseSettingsData(data); + } while (dbRes->NextRow()); + + return result; + } + + void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint8 index, uint32 value) + { + if (!sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED)) + return; + + PlayerSettingVector settings = LoadPlayerSettings(playerLowGuid, source); + size_t const requiredSize = static_cast(index) + 1; + if (settings.size() < requiredSize) + settings.resize(requiredSize); // zero-initialized PlayerSetting::value + + settings[index].value = value; + + CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(playerLowGuid, source, settings); + CharacterDatabase.Execute(stmt); + } +} + +// Implementation of PrepareReplaceStatement +CharacterDatabasePreparedStatement* PlayerSettingsStore::PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings) +{ + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS); + stmt->SetData(0, playerLowGuid); + stmt->SetData(1, source); + stmt->SetData(2, SerializeSettingsData(settings)); + return stmt; +} + void Player::_LoadCharacterSettings(PreparedQueryResult result) { m_charSettingsMap.clear(); @@ -39,21 +123,7 @@ void Player::_LoadCharacterSettings(PreparedQueryResult result) std::string source = fields[0].Get(); std::string data = fields[1].Get(); - std::vector tokens = Acore::Tokenize(data, ' ', false); - - PlayerSettingVector settings; - settings.reserve(tokens.size()); // reserve capacity but don't resize - - for (auto const& token : tokens) - { - if (token.empty()) - continue; - - // Try to parse the value safely - if (auto parsed = Acore::StringTo(token)) - settings.emplace_back(*parsed); - } - + PlayerSettingVector settings = PlayerSettingsStore::ParseSettingsData(data); m_charSettingsMap.emplace(std::move(source), std::move(settings)); } while (result->NextRow()); @@ -81,16 +151,7 @@ void Player::_SavePlayerSettings(CharacterDatabaseTransaction trans) if (settings.empty()) continue; - std::ostringstream data; - data << settings[0].value; - - for (size_t i = 1; i < settings.size(); ++i) - data << ' ' << settings[i].value; - - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_CHAR_SETTINGS); - stmt->SetData(0, GetGUID().GetCounter()); - stmt->SetData(1, source); - stmt->SetData(2, data.str()); + CharacterDatabasePreparedStatement* stmt = PlayerSettingsStore::PrepareReplaceStatement(GetGUID().GetCounter(), source, settings); trans->Append(stmt); } } diff --git a/src/server/game/Entities/Player/PlayerSettings.h b/src/server/game/Entities/Player/PlayerSettings.h index 027062e3c..612225114 100644 --- a/src/server/game/Entities/Player/PlayerSettings.h +++ b/src/server/game/Entities/Player/PlayerSettings.h @@ -17,6 +17,7 @@ #ifndef _PLAYER_SETTINGS_H #define _PLAYER_SETTINGS_H +#include "DatabaseEnvFwd.h" class Player; @@ -51,4 +52,18 @@ struct PlayerSetting typedef std::vector PlayerSettingVector; typedef std::map PlayerSettingMap; +// Standalone API: update a player's setting directly on DB by GUID (low part) without requiring a Player instance +namespace PlayerSettingsStore +{ + // Update a single setting value for any player by GUID (works for online or offline players). + // This reads the existing "source" row from character_settings, adjusts the index, and REPLACE's it back. + void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint8 index, uint32 value); + + // Common helpers for parsing and serializing settings data + PlayerSettingVector ParseSettingsData(std::string const& data); + std::string SerializeSettingsData(PlayerSettingVector const& settings); + // Prepare a REPLACE statement populated with given settings data. Caller may execute or append to a transaction. + CharacterDatabasePreparedStatement* PrepareReplaceStatement(uint32 playerLowGuid, std::string const& source, PlayerSettingVector const& settings); +} + #endif From e3505df99f93e48a3af1ffb72a40dc5f9bd0f890 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Mon, 18 Aug 2025 21:14:08 -0700 Subject: [PATCH 026/155] fix(Core/Visibility): Fix visibility crash (#22704) --- src/server/game/Entities/Object/Object.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index a9fcd7199..3081013be 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1696,7 +1696,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo if (this == obj) return true; - if (obj->IsNeverVisible() || CanNeverSee(obj)) + if (CanNeverSee(obj)) return false; if (obj->IsAlwaysVisibleFor(this) || CanAlwaysSee(obj)) @@ -1840,6 +1840,12 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo bool WorldObject::CanNeverSee(WorldObject const* obj) const { + if (!IsInWorld()) + return true; + + if (obj->IsNeverVisible()) + return true; + if (IsCreature() && obj->IsCreature()) return GetMap() != obj->GetMap() || (!InSamePhase(obj) && ToUnit()->GetVehicleBase() != obj && this != obj->ToUnit()->GetVehicleBase()); return GetMap() != obj->GetMap() || !InSamePhase(obj); From 0f08b6e8ab058e7c42a73c4bfc2346d772ae3a46 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Tue, 19 Aug 2025 14:05:57 +0200 Subject: [PATCH 027/155] fix(DB/Creature) Resolve Return of the High Chief quest issues. (#22695) --- data/sql/updates/pending_db_world/Roanauk.sql | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Roanauk.sql diff --git a/data/sql/updates/pending_db_world/Roanauk.sql b/data/sql/updates/pending_db_world/Roanauk.sql new file mode 100644 index 000000000..63d6dd95b --- /dev/null +++ b/data/sql/updates/pending_db_world/Roanauk.sql @@ -0,0 +1,65 @@ + +-- Remove Unit Flags from Roanauk Icemist (IMMUNE_TO_PC, IMMUNE_TO_NPC) +UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~(256|512) WHERE (`entry` = 26654); + +-- Remove Unit Flags from Icemist Warriors (IMMUNE_TO_PC, IMMUNE_TO_NPC, STUNNED) +UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~(256|512|262144) WHERE (`entry` = 26772); + +-- Update SmartAI (Roanauk Icemist) +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (26654, 26772)); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 26654); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(26654, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 768, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Respawn - Set Flags Immune To Players & Immune To NPC\'s'), +(26654, 0, 1, 2, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 45, 1, 1, 0, 0, 0, 0, 11, 26656, 10, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Respawn - Set Data 1 1'), +(26654, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Respawn - Set Event Phase 1'), +(26654, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 47273, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Respawn - Cast \'Icemist`s Prison\''), +(26654, 0, 4, 0, 1, 1, 100, 0, 5000, 30000, 120000, 150000, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Out of Combat - Say Line 0 (Phase 1)'), +(26654, 0, 5, 0, 38, 0, 100, 513, 1, 1, 0, 0, 0, 0, 80, 2665400, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Data Set 1 1 - Run Script (No Repeat)'), +(26654, 0, 6, 0, 40, 0, 100, 512, 1, 0, 0, 0, 0, 0, 80, 2665401, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Point 1 of Path Any Reached - Run Script'), +(26654, 0, 7, 0, 38, 0, 100, 512, 2, 2, 0, 0, 0, 0, 80, 2665402, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - On Data Set 2 2 - Run Script'); + +-- Update Action List (Roanauk Icemist) +DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (2665400, 2665401)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(2665400, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 28, 47273, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Remove Aura \'Icemist`s Prison\''), +(2665400, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 2, 2, 0, 0, 0, 0, 9, 26656, 0, 200, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Set Data 2 2'), +(2665400, 9, 2, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Set Event Phase 2'), +(2665400, 9, 3, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 1'), +(2665400, 9, 4, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 53, 0, 26654, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Start Waypoint Path 26654'), +(2665401, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 2'), +(2665401, 9, 1, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 3'), +(2665401, 9, 2, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 19, 26608, 100, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 1'), +(2665401, 9, 3, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 11, 47378, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Cast \'Glory of the Ancestors\''), +(2665401, 9, 4, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 4'), +(2665401, 9, 5, 0, 0, 0, 100, 0, 7000, 7000, 0, 0, 0, 0, 19, 768, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Remove Flags Immune To Players & Immune To NPC\'s'), +(2665401, 9, 6, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 11, 47379, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Cast \'Icemist`s Blessing\''), +(2665401, 9, 7, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 5'), +(2665401, 9, 8, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 6, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 6'), +(2665401, 9, 9, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 19, 26608, 100, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 2'), +(2665401, 9, 10, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Say Line 7'), +(2665401, 9, 11, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 45, 1, 1, 0, 0, 0, 0, 19, 26608, 100, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Set Data 1 1'), +(2665401, 9, 12, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Set Home Position'), +(2665401, 9, 13, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 19, 26608, 0, 0, 0, 0, 0, 0, 0, 'Roanauk Icemist - Actionlist - Start Attacking'); + +-- Update SmartAI (Icemist Warriors) +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 26772); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(26772, 0, 0, 1, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 29266, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Respawn - Cast \'Permanent Feign Death\''), +(26772, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 262912, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Respawn - Set Flags Immune To Players & Immune To NPC\'s & Stunned'), +(26772, 0, 2, 3, 8, 0, 100, 512, 47378, 0, 0, 0, 0, 0, 28, 29266, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Spellhit \'Glory of the Ancestors\' - Remove Aura \'Permanent Feign Death\''), +(26772, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 2677200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Spellhit \'Glory of the Ancestors\' - Run Script'), +(26772, 0, 4, 0, 38, 0, 100, 512, 1, 1, 0, 0, 0, 0, 41, 5000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - On Data Set 1 1 - Despawn In 5000 ms'); + +-- Add Action List (Icemist Warriors) +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2677200); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(2677200, 9, 0, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 12, 26676, 6, 6000, 0, 0, 0, 202, 5, 1, 1, 0, 0, 0, 0, 0, 'Icemist Warrior - Actionlist - Summon Creature \'Anub\'ar Invader\''), +(2677200, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 19, 262912, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Icemist Warrior - Actionlist - Remove Flags Immune To Players & Immune To NPC\'s & Stunned'); + +-- Set Conditions for Icemist's Blessing +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` = 47379; +INSERT INTO `conditions`(`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13,3,47379,0,0,31,0,3,26654,0,0,0,0,'','Icemist\'s Blessing (47379) - target is Roanauk Icemist'), +(13,3,47379,0,1,31,0,3,26772,0,0,0,0,'','Icemist\'s Blessing (47379) - target is Icemist Warrior'), +(13,3,47379,0,2,9,0,12069,0,0,0,0,0,'','Icemist\'s Blessing (47379) - player has quest 12069 active'); From a19dc17cc5164fc3a2fbabbf2d90e878af73cf1b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 19 Aug 2025 12:06:59 +0000 Subject: [PATCH 028/155] chore(DB): import pending files Referenced commit(s): 0f08b6e8ab058e7c42a73c4bfc2346d772ae3a46 --- .../{pending_db_world/Roanauk.sql => db_world/2025_08_19_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Roanauk.sql => db_world/2025_08_19_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/Roanauk.sql b/data/sql/updates/db_world/2025_08_19_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/Roanauk.sql rename to data/sql/updates/db_world/2025_08_19_00.sql index 63d6dd95b..a3589d86d 100644 --- a/data/sql/updates/pending_db_world/Roanauk.sql +++ b/data/sql/updates/db_world/2025_08_19_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_18_01 -> 2025_08_19_00 -- Remove Unit Flags from Roanauk Icemist (IMMUNE_TO_PC, IMMUNE_TO_NPC) UPDATE `creature_template` SET `unit_flags` = `unit_flags` &~(256|512) WHERE (`entry` = 26654); From 2602add0fd09dd281ea8318ab162b1cf63c78bd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A4=A9=E9=B9=AD?= <18535853+PkllonG@users.noreply.github.com> Date: Tue, 19 Aug 2025 21:03:03 +0800 Subject: [PATCH 029/155] refactor(Core): Improve readability (#22691) --- src/server/game/AI/SmartScripts/SmartScript.cpp | 6 +++--- src/server/game/AuctionHouse/AuctionHouseMgr.cpp | 2 +- src/server/game/Chat/HyperlinkTags.cpp | 2 +- src/server/game/Conditions/ConditionMgr.cpp | 2 +- src/server/game/Entities/Creature/CreatureGroups.h | 2 +- src/server/game/Entities/Player/Player.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 2cc4a246b..e0a6f4fb8 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -895,7 +895,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u go->SetLootState(GO_READY); } - go->UseDoorOrButton(0, !!e.action.activateObject.alternative, unit); + go->UseDoorOrButton(0, e.action.activateObject.alternative, unit); LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ACTIVATE_GOBJECT. Gameobject {} activated", go->GetGUID().ToString()); } } @@ -1485,14 +1485,14 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u { for (WorldObject* target : targets) if (IsUnit(target)) - target->ToUnit()->SetVisible(!!e.action.visibility.state); + target->ToUnit()->SetVisible(e.action.visibility.state); break; } case SMART_ACTION_SET_ACTIVE: { for (WorldObject* target : targets) - target->setActive(!!e.action.setActive.state); + target->setActive(e.action.setActive.state); break; } case SMART_ACTION_ATTACK_START: diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 736fd46e1..dcc1b7662 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -488,7 +488,7 @@ void AuctionHouseObject::AddAuction(AuctionEntry* auction) bool AuctionHouseObject::RemoveAuction(AuctionEntry* auction) { - bool wasInMap = !!_auctionsMap.erase(auction->Id); + bool wasInMap = _auctionsMap.erase(auction->Id); sAuctionMgr->GetAuctionHouseSearcher()->RemoveAuction(auction); sScriptMgr->OnAuctionRemove(this, auction); diff --git a/src/server/game/Chat/HyperlinkTags.cpp b/src/server/game/Chat/HyperlinkTags.cpp index 342ba3035..2cb36b698 100644 --- a/src/server/game/Chat/HyperlinkTags.cpp +++ b/src/server/game/Chat/HyperlinkTags.cpp @@ -206,7 +206,7 @@ bool Acore::Hyperlinks::LinkTags::spell::StoreTo(SpellInfo const*& val, std::str if (!(t.TryConsumeTo(spellId) && t.IsEmpty())) return false; - return !!(val = sSpellMgr->GetSpellInfo(spellId)); + return (val = sSpellMgr->GetSpellInfo(spellId)); } bool Acore::Hyperlinks::LinkTags::talent::StoreTo(TalentLinkData& val, std::string_view text) diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 93bdc0c8a..09edfab28 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -68,7 +68,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) { // don't allow 0 items (it's checked during table load) ASSERT(ConditionValue2); - bool checkBank = !!ConditionValue3; + bool checkBank = ConditionValue3; condMeets = player->HasItemCount(ConditionValue1, ConditionValue2, checkBank); } } diff --git a/src/server/game/Entities/Creature/CreatureGroups.h b/src/server/game/Entities/Creature/CreatureGroups.h index 2cb6e9ae8..54b01c08c 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.h +++ b/src/server/game/Entities/Creature/CreatureGroups.h @@ -67,7 +67,7 @@ struct FormationInfo uint32 point_1; uint32 point_2; - bool HasGroupFlag(uint16 flag) const { return !!(groupAI & flag); } + bool HasGroupFlag(uint16 flag) const { return (groupAI & flag); } }; typedef std::unordered_map CreatureGroupInfoType; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index adc4f04c0..16a97177e 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1692,7 +1692,7 @@ public: bool RemoveMItem(ObjectGuid::LowType itemLowGuid) { - return !!mMitems.erase(itemLowGuid); + return mMitems.erase(itemLowGuid); } void PetSpellInitialize(); From e0f2ec41efb7159248b9161825be85fdb8b030c7 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 19 Aug 2025 12:59:57 -0300 Subject: [PATCH 030/155] fix(Scripts/Commands): Allow using debug LFG and BG from console (#22705) --- src/server/scripts/Commands/cs_debug.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index a9fe5d9e0..a18e01e0b 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -73,7 +73,7 @@ public: { "hostile", HandleDebugHostileRefListCommand, SEC_ADMINISTRATOR, Console::No }, { "anim", HandleDebugAnimCommand, SEC_ADMINISTRATOR, Console::No }, { "arena", HandleDebugArenaCommand, SEC_ADMINISTRATOR, Console::No }, - { "bg", HandleDebugBattlegroundCommand, SEC_ADMINISTRATOR, Console::No }, + { "bg", HandleDebugBattlegroundCommand, SEC_ADMINISTRATOR, Console::Yes}, { "cooldown", HandleDebugCooldownCommand, SEC_ADMINISTRATOR, Console::No }, { "getitemstate", HandleDebugGetItemStateCommand, SEC_ADMINISTRATOR, Console::No }, { "lootrecipient", HandleDebugGetLootRecipientCommand, SEC_ADMINISTRATOR, Console::No }, @@ -92,7 +92,7 @@ public: { "update", HandleDebugUpdateCommand, SEC_ADMINISTRATOR, Console::No }, { "itemexpire", HandleDebugItemExpireCommand, SEC_ADMINISTRATOR, Console::No }, { "areatriggers", HandleDebugAreaTriggersCommand, SEC_ADMINISTRATOR, Console::No }, - { "lfg", HandleDebugDungeonFinderCommand, SEC_ADMINISTRATOR, Console::No }, + { "lfg", HandleDebugDungeonFinderCommand, SEC_ADMINISTRATOR, Console::Yes}, { "los", HandleDebugLoSCommand, SEC_ADMINISTRATOR, Console::No }, { "moveflags", HandleDebugMoveflagsCommand, SEC_ADMINISTRATOR, Console::No }, { "unitstate", HandleDebugUnitStateCommand, SEC_ADMINISTRATOR, Console::No }, From d4713356a095f0f1c26f33252ed698b2ee910c80 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Tue, 19 Aug 2025 15:51:06 -0400 Subject: [PATCH 031/155] refactor(Core/Packets): Rewrite `MSG_MINIMAP_PING` to modern packet class. (#22696) --- src/server/game/Groups/Group.cpp | 11 +++++++++ src/server/game/Groups/Group.h | 2 ++ src/server/game/Handlers/GroupHandler.cpp | 20 ++++++---------- .../game/Server/Packets/MiscPackets.cpp | 15 ++++++++++++ src/server/game/Server/Packets/MiscPackets.h | 23 +++++++++++++++++++ src/server/game/Server/WorldSession.h | 3 ++- 6 files changed, 60 insertions(+), 14 deletions(-) diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 3fede6ffc..b4b2331e1 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -28,6 +28,7 @@ #include "LFGMgr.h" #include "Log.h" #include "MapMgr.h" +#include "MiscPackets.h" #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" @@ -2059,6 +2060,16 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* return GroupJoinBattlegroundResult(bgTemplate->GetBgTypeID()); } +void Group::DoMinimapPing(ObjectGuid sourceGuid, float mapX, float mapY) +{ + WorldPackets::Misc::MinimapPing minimapPing; + minimapPing.SourceGuid = sourceGuid; + minimapPing.MapX = mapX; + minimapPing.MapY = mapY; + + BroadcastPacket(minimapPing.Write(), true, -1, sourceGuid); +} + //=================================================== //============== Roll =============================== //=================================================== diff --git a/src/server/game/Groups/Group.h b/src/server/game/Groups/Group.h index f4afabf6f..acf6fc0e1 100644 --- a/src/server/game/Groups/Group.h +++ b/src/server/game/Groups/Group.h @@ -255,6 +255,8 @@ public: void SetBattlefieldGroup(Battlefield* bf); GroupJoinBattlegroundResult CanJoinBattlegroundQueue(Battleground const* bgTemplate, BattlegroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot); + void DoMinimapPing(ObjectGuid sourceGuid, float mapX, float mapY); + void ChangeMembersGroup(ObjectGuid guid, uint8 group); void SetTargetIcon(uint8 id, ObjectGuid whoGuid, ObjectGuid targetGuid); void SetGroupMemberFlag(ObjectGuid guid, bool apply, GroupMemberFlags flag); diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index ee4e0676a..85a9f0888 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -22,6 +22,7 @@ #include "LFGMgr.h" #include "Language.h" #include "Log.h" +#include "MapMgr.h" #include "MiscPackets.h" #include "ObjectMgr.h" #include "Opcodes.h" @@ -522,24 +523,17 @@ void WorldSession::HandleLootRoll(WorldPacket& recvData) } } -void WorldSession::HandleMinimapPingOpcode(WorldPacket& recvData) +void WorldSession::HandleMinimapPingOpcode(WorldPackets::Misc::MinimapPingClient& packet) { - if (!GetPlayer()->GetGroup()) + if (!sMapMgr->IsValidMapCoord(GetPlayer()->GetMap()->GetId(), packet.MapX, packet.MapY)) return; - float x, y; - recvData >> x; - recvData >> y; + Group* group = GetPlayer()->GetGroup(); - /** error handling **/ - /********************/ + if (!group) + return; - // everything's fine, do it - WorldPacket data(MSG_MINIMAP_PING, (8 + 4 + 4)); - data << GetPlayer()->GetGUID(); - data << float(x); - data << float(y); - GetPlayer()->GetGroup()->BroadcastPacket(&data, true, -1, GetPlayer()->GetGUID()); + group->DoMinimapPing(GetPlayer()->GetGUID(), packet.MapX, packet.MapY); } void WorldSession::HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& packet) diff --git a/src/server/game/Server/Packets/MiscPackets.cpp b/src/server/game/Server/Packets/MiscPackets.cpp index a418a517e..109845381 100644 --- a/src/server/game/Server/Packets/MiscPackets.cpp +++ b/src/server/game/Server/Packets/MiscPackets.cpp @@ -67,6 +67,21 @@ WorldPacket const* WorldPackets::Misc::Playsound::Write() return &_worldPacket; } +void WorldPackets::Misc::MinimapPingClient::Read() +{ + _worldPacket >> MapX; + _worldPacket >> MapY; +} + +WorldPacket const* WorldPackets::Misc::MinimapPing::Write() +{ + _worldPacket << SourceGuid; + _worldPacket << float(MapX); + _worldPacket << float(MapY); + + return &_worldPacket; +} + void WorldPackets::Misc::RandomRollClient::Read() { _worldPacket >> Min; diff --git a/src/server/game/Server/Packets/MiscPackets.h b/src/server/game/Server/Packets/MiscPackets.h index 6f991293f..7453ad7fc 100644 --- a/src/server/game/Server/Packets/MiscPackets.h +++ b/src/server/game/Server/Packets/MiscPackets.h @@ -93,6 +93,29 @@ namespace WorldPackets uint32 SoundKitID = 0; }; + class MinimapPingClient final : public ClientPacket + { + public: + MinimapPingClient(WorldPacket&& packet) : ClientPacket(MSG_MINIMAP_PING, std::move(packet)) {} + + void Read() override; + + float MapX = 0.0f; // Raw position coordinates + float MapY = 0.0f; + }; + + class MinimapPing final : public ServerPacket + { + public: + MinimapPing() : ServerPacket(MSG_MINIMAP_PING, 8 + 4 + 4) { } + + WorldPacket const* Write() override; + + ObjectGuid SourceGuid; + float MapX = 0.0f; + float MapY = 0.0f; + }; + class RandomRollClient final : public ClientPacket { public: diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 02948d249..9e570f9b0 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -148,6 +148,7 @@ namespace WorldPackets namespace Misc { + class MinimapPingClient; class RandomRollClient; } @@ -933,7 +934,7 @@ public: // opcodes handlers void HandleWardenDataOpcode(WorldPacket& recvData); void HandleWorldTeleportOpcode(WorldPacket& recvData); - void HandleMinimapPingOpcode(WorldPacket& recvData); + void HandleMinimapPingOpcode(WorldPackets::Misc::MinimapPingClient& packet); void HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& packet); void HandleFarSightOpcode(WorldPacket& recvData); void HandleSetDungeonDifficultyOpcode(WorldPacket& recvData); From 413178662ae43db09f23b6a1c69f75eba36b91d9 Mon Sep 17 00:00:00 2001 From: iThorgrim <125808072+iThorgrim@users.noreply.github.com> Date: Thu, 21 Aug 2025 00:31:00 +0200 Subject: [PATCH 032/155] fix(Core/SAI): GameObject smart_script based on GUID (#22710) --- .../game/AI/SmartScripts/SmartScriptMgr.cpp | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 0040e0bd3..9185f77ac 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -202,10 +202,29 @@ void SmartAIMgr::LoadSmartAIFromDB() } else { - if (!sObjectMgr->GetCreatureData(uint32(std::abs(temp.entryOrGuid)))) + switch (source_type) { - LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Creature guid ({}) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid))); - continue; + case SMART_SCRIPT_TYPE_CREATURE: + { + if (!sObjectMgr->GetCreatureData(uint32(std::abs(temp.entryOrGuid)))) + { + LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: Creature guid ({}) does not exist, skipped loading.", uint32(std::abs(temp.entryOrGuid))); + continue; + } + break; + } + case SMART_SCRIPT_TYPE_GAMEOBJECT: + { + if (!sObjectMgr->GetGameObjectData(uint32(std::abs(temp.entryOrGuid)))) + { + LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: GameObject guid ({}) does not exist, skipped loading.", uint32(temp.entryOrGuid)); + continue; + } + break; + } + default: + LOG_ERROR("sql.sql", "SmartAIMgr::LoadSmartAIFromDB: not yet implemented source_type {}", (uint32)source_type); + continue; } } From 9d82ae97ae5cf5730b6ed71a5e1470285e75474c Mon Sep 17 00:00:00 2001 From: SAS2000 Date: Thu, 21 Aug 2025 19:13:04 +0200 Subject: [PATCH 033/155] fix(SERVER/Main): Implementation for version param (#22707) --- src/server/apps/authserver/Main.cpp | 9 ++++----- src/server/apps/worldserver/Main.cpp | 8 +++----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/server/apps/authserver/Main.cpp b/src/server/apps/authserver/Main.cpp index e9ed0119f..b5fbb319a 100644 --- a/src/server/apps/authserver/Main.cpp +++ b/src/server/apps/authserver/Main.cpp @@ -29,6 +29,7 @@ #include "Config.h" #include "DatabaseEnv.h" #include "DatabaseLoader.h" +#include "GitRevision.h" #include "IPLocation.h" #include "IoContext.h" #include "Log.h" @@ -75,7 +76,7 @@ int main(int argc, char** argv) auto vm = GetConsoleArguments(argc, argv, configFile); // exit if help or version is enabled - if (vm.count("help")) + if (vm.count("help") || vm.count("version")) return 0; // Add file and args in config @@ -292,13 +293,11 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile) } if (variablesMap.count("help")) - { std::cout << all << "\n"; - } + else if (variablesMap.count("version")) + std::cout << GitRevision::GetFullVersion() << "\n"; else if (variablesMap.count("dry-run")) - { sConfigMgr->setDryRun(true); - } return variablesMap; } diff --git a/src/server/apps/worldserver/Main.cpp b/src/server/apps/worldserver/Main.cpp index 26a13985f..bdc2f860f 100644 --- a/src/server/apps/worldserver/Main.cpp +++ b/src/server/apps/worldserver/Main.cpp @@ -127,7 +127,7 @@ int main(int argc, char** argv) auto vm = GetConsoleArguments(argc, argv, configFile, configService); // exit if help or version is enabled - if (vm.count("help")) + if (vm.count("help") || vm.count("version")) return 0; #if AC_PLATFORM == AC_PLATFORM_WINDOWS @@ -733,13 +733,11 @@ variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, [ } if (vm.count("help")) - { std::cout << all << "\n"; - } + else if (vm.count("version")) + std::cout << GitRevision::GetFullVersion() << "\n"; else if (vm.count("dry-run")) - { sConfigMgr->setDryRun(true); - } return vm; } From c67d4efa4a19625a3d0bbe902dd3ba9f6980d2a2 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 22 Aug 2025 05:26:35 -0300 Subject: [PATCH 034/155] fix(Core/DB): Update connection type for character settings select (#22712) --- .../database/Database/Implementation/CharacterDatabase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index b397b0142..ec080b784 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -606,7 +606,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_PROFANITY_PLAYER_NAME, "INSERT IGNORE INTO profanity_name (name) VALUES (?)", CONNECTION_ASYNC); // Character settings - PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHAR_SETTINGS, "SELECT source, data FROM character_settings WHERE guid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_REP_CHAR_SETTINGS, "REPLACE INTO character_settings (guid, source, data) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_SETTINGS, "DELETE FROM character_settings WHERE guid = ?", CONNECTION_ASYNC); From 82d98f9fb7674f0b2f98170e74ecbd66cbd1e287 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 22 Aug 2025 22:25:15 -0300 Subject: [PATCH 035/155] =?UTF-8?q?chore(Core/Player):=20Change=20player?= =?UTF-8?q?=20setting=20index=20type=20from=20uint8=20to=20ui=E2=80=A6=20(?= =?UTF-8?q?#22716)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/Entities/Player/Player.h | 4 ++-- src/server/game/Entities/Player/PlayerSettings.cpp | 6 +++--- src/server/game/Entities/Player/PlayerSettings.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 16a97177e..ae6b6767c 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2641,8 +2641,8 @@ public: std::string GetPlayerName(); // Settings - [[nodiscard]] PlayerSetting GetPlayerSetting(std::string const& source, uint8 index); - void UpdatePlayerSetting(std::string const& source, uint8 index, uint32 value); + [[nodiscard]] PlayerSetting GetPlayerSetting(std::string const& source, uint32 index); + void UpdatePlayerSetting(std::string const& source, uint32 index, uint32 value); void SendSystemMessage(std::string_view msg, bool escapeCharacters = false); diff --git a/src/server/game/Entities/Player/PlayerSettings.cpp b/src/server/game/Entities/Player/PlayerSettings.cpp index 89b719d71..1fd4aae3a 100644 --- a/src/server/game/Entities/Player/PlayerSettings.cpp +++ b/src/server/game/Entities/Player/PlayerSettings.cpp @@ -80,7 +80,7 @@ namespace PlayerSettingsStore return result; } - void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint8 index, uint32 value) + void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint32 index, uint32 value) { if (!sWorld->getBoolConfig(CONFIG_PLAYER_SETTINGS_ENABLED)) return; @@ -129,7 +129,7 @@ void Player::_LoadCharacterSettings(PreparedQueryResult result) } while (result->NextRow()); } -PlayerSetting Player::GetPlayerSetting(std::string const& source, uint8 index) +PlayerSetting Player::GetPlayerSetting(std::string const& source, uint32 index) { auto it = m_charSettingsMap.find(source); if (it == m_charSettingsMap.end() || static_cast(index) >= it->second.size()) @@ -156,7 +156,7 @@ void Player::_SavePlayerSettings(CharacterDatabaseTransaction trans) } } -void Player::UpdatePlayerSetting(std::string const& source, uint8 index, uint32 value) +void Player::UpdatePlayerSetting(std::string const& source, uint32 index, uint32 value) { auto it = m_charSettingsMap.find(source); size_t const requiredSize = static_cast(index) + 1; diff --git a/src/server/game/Entities/Player/PlayerSettings.h b/src/server/game/Entities/Player/PlayerSettings.h index 612225114..4e0c96135 100644 --- a/src/server/game/Entities/Player/PlayerSettings.h +++ b/src/server/game/Entities/Player/PlayerSettings.h @@ -57,7 +57,7 @@ namespace PlayerSettingsStore { // Update a single setting value for any player by GUID (works for online or offline players). // This reads the existing "source" row from character_settings, adjusts the index, and REPLACE's it back. - void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint8 index, uint32 value); + void UpdateSetting(uint32 playerLowGuid, std::string const& source, uint32 index, uint32 value); // Common helpers for parsing and serializing settings data PlayerSettingVector ParseSettingsData(std::string const& data); From fc90e30e5016d4bf705b4aa49c8be7c4e1e27035 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Sat, 23 Aug 2025 01:11:03 -0400 Subject: [PATCH 036/155] fix(DB): Add spawns/events for Ahn'Qiraj War Effort event. (#22497) --- .../pending_db_world/creature-spawns.sql | 83 ++++ .../updates/pending_db_world/game-events.sql | 464 ++++++++++++++++++ .../pending_db_world/gobject-spawns.sql | 102 ++++ 3 files changed, 649 insertions(+) create mode 100644 data/sql/updates/pending_db_world/creature-spawns.sql create mode 100644 data/sql/updates/pending_db_world/game-events.sql create mode 100644 data/sql/updates/pending_db_world/gobject-spawns.sql diff --git a/data/sql/updates/pending_db_world/creature-spawns.sql b/data/sql/updates/pending_db_world/creature-spawns.sql new file mode 100644 index 000000000..23346778e --- /dev/null +++ b/data/sql/updates/pending_db_world/creature-spawns.sql @@ -0,0 +1,83 @@ +SET @GUID = 83113; + +DELETE FROM `creature` WHERE `id1` IN (3296, 5595, 15383, 15431, 15432, 15434, 15437, 15445, 15446, 15448, 15450, 15451, 15452, 15453, 15455, 15456, 15457, 15458, 15459, 15460, 15469, 15477, 15508, 15512, 15515, 15522, 15525, 15528, 15529, 15532, 15533, 15534, 15535, 15539, 15663, 15696, 15700, 15701, 15702, 15703, 15704, 15707, 15708, 15709, 15731, 15733, 15734, 15735, 15736, 15737, 15738, 15739, 15761, 15762, 15763, 15764, 15765, 15766, 15767, 15768) AND `guid` BETWEEN @GUID AND @GUID+69; +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +-- Ironforge +(@GUID+0, 15731, 0, 0, 0, 0, 0, 1, 1, 0, -4935.1743, -1197.6975, 501.62204, 2.460914134979248046, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Darnassus Commendation Officer +(@GUID+1, 15733, 0, 0, 0, 0, 0, 1, 1, 0, -4952.5264, -1176.9742, 501.63916, 5.393067359924316406, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Gnomeregan Commendation Officer +(@GUID+2, 15734, 0, 0, 0, 0, 0, 1, 1, 0, -4975.3374, -1196.7572, 501.74588, 1.884955525398254394, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Ironforge Commendation Officer +(@GUID+3, 15735, 0, 0, 0, 0, 0, 1, 1, 0, -4934.9854, -1214.3094, 501.7179, 3.333578824996948242, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Stormwind Commendation Officer +(@GUID+4, 5595, 0, 0, 0, 0, 0, 1, 1, 0, -4980.02, -1219.984, 501.75632, 3.822271108627319335, 300, 0, 0, 5228, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Ironforge Guard +(@GUID+5, 15539, 0, 0, 0, 0, 0, 1, 1, 0, -4981.2524, -1218.3779, 501.7562, 3.804817676544189453, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- General Zog +(@GUID+6, 5595, 0, 0, 0, 0, 0, 1, 1, 0, -4982.4688, -1216.806, 501.7562, 3.874630928039550781, 300, 0, 0, 5228, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Ironforge Guard +(@GUID+7, 15383, 0, 0, 0, 0, 0, 1, 1, 0, -4924.3657, -1222.7299, 501.71756, 3.926990747451782226, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sergeant Stonebrow +(@GUID+8, 15431, 0, 0, 0, 0, 0, 1, 1, 0, -4914.172, -1227.4949, 501.73282, 3.59537816047668457, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Corporal Carnes +(@GUID+9, 15432, 0, 0, 0, 0, 0, 1, 1, 0, -4930.287, -1218.7476, 501.71875, 3.752457857131958007, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Dame Twinbraid +(@GUID+10, 15434, 0, 0, 0, 0, 0, 1, 1, 0, -4952.255, -1274.4495, 501.75662, 1.797689080238342285, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Private Draxlegauge +(@GUID+11, 15437, 0, 0, 0, 0, 0, 1, 1, 0, -4945.4204, -1282.0215, 501.75787, 1.029744267463684082, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Master Nightsong +(@GUID+12, 15445, 0, 0, 0, 0, 0, 1, 1, 0, -4948.3345, -1273.7974, 501.75522, 1.064650893211364746, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sergeant Major Germaine +(@GUID+13, 15446, 0, 0, 0, 0, 0, 1, 1, 0, -4972.2017, -1169.0591, 501.72, 3.281219005584716796, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Bonnie Stoneflayer +(@GUID+14, 15448, 0, 0, 0, 0, 0, 1, 1, 0, -4966.0938, -1176.0596, 501.74265, 3.298672199249267578, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Private Porter +(@GUID+15, 15450, 0, 0, 0, 0, 0, 1, 1, 0, -4969.4565, -1180.2417, 501.7428, 3.246312379837036132, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Marta Finespindle +(@GUID+16, 15451, 0, 0, 0, 0, 0, 1, 1, 0, -4971.5747, -1151.5566, 501.73938, 3.560471534729003906, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sentinel Silversky +(@GUID+17, 15452, 0, 0, 0, 0, 0, 1, 1, 0, -4979.116, -1149.51, 501.7331, 3.368485450744628906, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Nurse Stonefield +(@GUID+18, 15453, 0, 0, 0, 0, 0, 1, 1, 0, -4979.9287, -1142.1707, 501.7428, 3.682644605636596679, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Keeper Moonshade +(@GUID+19, 15455, 0, 0, 0, 0, 0, 1, 1, 0, -4938.0024, -1275.1202, 501.75195, 2.460914134979248046, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Slicky Gastronome +(@GUID+20, 15456, 0, 0, 0, 0, 0, 1, 1, 0, -4940.392, -1277.704, 501.7544, 1.989675283432006835, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sarah Sadwhistle +(@GUID+21, 15457, 0, 0, 0, 0, 0, 1, 1, 0, -4933.8027, -1279.1617, 501.74948, 2.426007747650146484, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Huntress Swiftriver +(@GUID+22, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4917.319, -1224.8823, 501.7417, 4.327857494354248046, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+23, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4944.852, -1277.7028, 501.75586, 4.258603572845458984, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+24, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4966.2954, -1173.8835, 501.72675, 3.648972272872924804, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+25, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4975.13, -1153.5369, 501.74008, 3.013503551483154296, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+26, 15663, 0, 0, 0, 0, 0, 1, 1, 0, -4937.548, -1280.2931, 501.7544, 4.558997154235839843, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Volunteer +(@GUID+27, 15701, 0, 0, 0, 0, 0, 1, 1, 0, -4977.4976, -1172.4156, 501.7317, 2.282009840011596679, 300, 0, 0, 30520, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- Field Marshal Snowfall +-- Orgrimmar +(@GUID+28, 15736, 0, 0, 1, 0, 0, 1, 1, 0, 1584.7704, -4112.9443, 33.37767, 5.410520553588867187, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Orgrimmar Commendation Officer +(@GUID+29, 15737, 0, 0, 1, 0, 0, 1, 1, 0, 1618.4412, -4101.7646, 32.95245, 5.235987663269042968, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Darkspear Commendation Officer +(@GUID+30, 15738, 0, 0, 1, 0, 0, 1, 1, 0, 1660.3582, -4107.4517, 34.620274, 2.059488534927368164, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Undercity Commendation Officer +(@GUID+31, 15739, 0, 0, 1, 0, 0, 1, 1, 0, 1603.886, -4142.897, 33.78176, 2.443460941314697265, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Thunder Bluff Commendation Officer +(@GUID+32, 3296, 0, 0, 1, 0, 0, 1, 1, 0, 1628.9564, -4119.1763, 31.244139, 2.094395160675048828, 300, 0, 0, 5228, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Orgrimmar Grunt +(@GUID+33, 15458, 0, 0, 1, 0, 0, 1, 1, 0, 1630.693, -4118.458, 31.265778, 1.972222089767456054, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Commander Stronghammer +(@GUID+34, 3296, 0, 0, 1, 0, 0, 1, 1, 0, 1632.3059, -4117.596, 31.29346, 2.042035102844238281, 300, 0, 0, 5228, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Orgrimmar Grunt +(@GUID+35, 15522, 0, 0, 1, 0, 0, 1, 1, 0, 1593.2671, -4159.4404, 36.90244, 2.94960641860961914, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Sergeant Umala +(@GUID+36, 15533, 0, 0, 1, 0, 0, 1, 1, 0, 1643.4338, -4085.0864, 37.337215, 4.677482128143310546, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Bloodguard Rawtar +(@GUID+37, 15534, 0, 0, 1, 0, 0, 1, 1, 0, 1629.785, -4089.1484, 35.632874, 5.25344085693359375, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Fisherman Lin'do +(@GUID+38, 15535, 0, 0, 1, 0, 0, 1, 1, 0, 1634.1218, -4084.9915, 36.52574, 5.218534469604492187, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Chief Sharpclaw +(@GUID+39, 15459, 0, 0, 1, 0, 0, 1, 1, 0, 1650.3278, -4124.2856, 31.452269, 2.652900457382202148, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Miner Cromwell +(@GUID+40, 15460, 0, 0, 1, 0, 0, 1, 1, 0, 1665.7623, -4117.497, 34.37464, 2.443460941314697265, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Grunt Maug +(@GUID+41, 15469, 0, 0, 1, 0, 0, 1, 1, 0, 1655.7728, -4119.1626, 32.695107, 1.326450228691101074, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Senior Sergeant T'kelah +(@GUID+42, 15477, 0, 0, 1, 0, 0, 1, 1, 0, 1615.0131, -4145.532, 35.131996, 1.378810048103332519, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Herbalist Proudfeather +(@GUID+43, 15508, 0, 0, 1, 0, 0, 1, 1, 0, 1625.951, -4149.423, 36.395786, 1.902408838272094726, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Batrider Pele'keiki +(@GUID+44, 15512, 0, 0, 1, 0, 0, 1, 1, 0, 1633.2582, -4142.117, 34.70991, 2.111848354339599609, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Apothecary Jezel +(@GUID+45, 15515, 0, 0, 1, 0, 0, 1, 1, 0, 1588.1735, -4179.9014, 39.98489, 2.897246599197387695, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Skinner Jamani +(@GUID+46, 15525, 0, 0, 1, 0, 0, 1, 1, 0, 1595.7618, -4174.3965, 39.766666, 2.722713708877563476, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Doctor Serratus +(@GUID+47, 15528, 0, 0, 1, 0, 0, 1, 1, 0, 1580.1736, -4116.1064, 34.41577, 5.602506637573242187, 300, 0, 0, 13495, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Healer Longrunner +(@GUID+48, 15529, 0, 0, 1, 0, 0, 1, 1, 0, 1571.1464, -4118.6587, 36.584232, 5.026548385620117187, 300, 0, 0, 14355, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Lady Callow +(@GUID+49, 15532, 0, 0, 1, 0, 0, 1, 1, 0, 1565.0651, -4123.9863, 37.44075, 0, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Stoneguard Clayhoof +(@GUID+50, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1576.2423, -4118.5137, 35.26423, 4.112950801849365234, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+51, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1591.8158, -4162.673, 37.39431, 4.81041717529296875, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+52, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1639.2985, -4081.972, 37.58056, 4.678342819213867187, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+53, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1629.4264, -4142.979, 34.864754, 3.117774009704589843, 300, 0, 0, 4775, 0, 2, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+54, 15696, 0, 0, 1, 0, 0, 1, 1, 0, 1666.3688, -4109.401, 35.089664, 1.868425965309143066, 300, 0, 0, 4775, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- War Effort Recruit +(@GUID+55, 15700, 0, 0, 1, 0, 0, 1, 1, 0, 1581.5414, -4184.574, 39.5738, 1.563615918159484863, 300, 0, 0, 30520, 0, 0, 0, 0, 0, '', 61582, 2, NULL), -- Warlord Gorchuk + +-- War Effort Recruiters +(@GUID+56, 15702, 0, 0, 1, 0, 0, 1, 1, 0, -1209.5848, 100.22011, 134.661, 3.159045934677124023, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Senior Sergeant Taiga +(@GUID+57, 15703, 0, 0, 0, 0, 0, 1, 1, 0, 1572.5758, 272.70654, -43.01935, 5.026548385620117187, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Senior Sergeant Grimsford +(@GUID+58, 15704, 0, 0, 1, 0, 0, 1, 1, 0, 1653.0684, -4403.811, 18.581886, 4.450589656829833984, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Senior Sergeant Kai'jin +(@GUID+59, 15707, 0, 0, 0, 0, 0, 1, 1, 0, -4956.0864, -931.13306, 503.3468, 5.375614166259765625, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Master Sergeant Fizzlebolt +(@GUID+60, 15708, 0, 0, 0, 0, 0, 1, 1, 0, -8813.751, 654.0678, 96.16028, 4.834561824798583984, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Master Sergeant Maclure +(@GUID+61, 15709, 0, 0, 1, 0, 0, 1, 1, 0, 9945.1455, 2494.2393, 1317.5244, 4.206243515014648437, 300, 0, 0, 15260, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Master Sergeant Moonshadow +-- Commendation Officers +(@GUID+62, 15761, 0, 0, 1, 0, 0, 1, 1, 0, 1945.322, -4330.305, 22.101057, 3.50811171531677246, 300, 0, 0, 2914, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Vu'Shalay +(@GUID+63, 15762, 0, 0, 1, 0, 0, 1, 1, 0, 9965.52, 2533.7234, 1319.0049, 0.471238881349563598, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Lunalight +(@GUID+64, 15763, 0, 0, 0, 0, 0, 1, 1, 0, -4811.9756, -1264.849, 501.95117, 3.054326057434082031, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Porterhouse +(@GUID+65, 15764, 0, 0, 0, 0, 0, 1, 1, 0, -4814.475, -1055.5201, 502.26733, 6.213372230529785156, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Ironbeard +(@GUID+66, 15765, 0, 0, 1, 0, 0, 1, 1, 0, 1911.704, -4276.771, 31.655682, 4.886921882629394531, 300, 0, 0, 2914, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Redblade +(@GUID+67, 15766, 0, 0, 0, 0, 0, 1, 1, 0, -8859.14, 638.28687, 96.34692, 1.815142393112182617, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Maloof +(@GUID+68, 15767, 0, 0, 1, 0, 0, 1, 1, 0, -1246.4849, 74.262695, 128.36818, 5.026548385620117187, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL), -- Officer Thunderstrider +(@GUID+69, 15768, 0, 0, 0, 0, 0, 1, 1, 0, 1587.8914, 279.28018, -43.019344, 4.852015495300292968, 300, 0, 0, 2614, 0, 0, 0, 0, 0, '', 61582, 1, NULL); -- Officer Gothena + +-- Old spawns +DELETE FROM `creature` WHERE `guid` IN (37, 2032, 6519, 6520, 25997, 32076, 46803) AND `id1` IN (14724, 15761, 15762, 15764, 15765, 15767, 15768); +DELETE FROM `creature_addon` WHERE `guid` IN (6519, 6520, 25997, 32076, 46803); diff --git a/data/sql/updates/pending_db_world/game-events.sql b/data/sql/updates/pending_db_world/game-events.sql new file mode 100644 index 000000000..17b1fb939 --- /dev/null +++ b/data/sql/updates/pending_db_world/game-events.sql @@ -0,0 +1,464 @@ +SET @EventID = 131, + @OGUID = 3639, + @CGUID = 83113; + +SET @AQWarAllianceBarsInitial = @EventID+0, + @AQWarAllianceBarsT1 = @EventID+1, + @AQWarAllianceBarsT2 = @EventID+2, + @AQWarAllianceBarsT3 = @EventID+3, + @AQWarAllianceBarsT4 = @EventID+4, + @AQWarAllianceBarsT5 = @EventID+5, + @AQWarAllianceHerbsInitial = @EventID+6, + @AQWarAllianceHerbsT1 = @EventID+7, + @AQWarAllianceHerbsT2 = @EventID+8, + @AQWarAllianceHerbsT3 = @EventID+9, + @AQWarAllianceHerbsT4 = @EventID+10, + @AQWarAllianceHerbsT5 = @EventID+11, + @AQWarAllianceSkinsInitial = @EventID+12, + @AQWarAllianceSkinsT1 = @EventID+13, + @AQWarAllianceSkinsT2 = @EventID+14, + @AQWarAllianceSkinsT3 = @EventID+15, + @AQWarAllianceSkinsT4 = @EventID+16, + @AQWarAllianceSkinsT5 = @EventID+17, + @AQWarAllianceBandagesInitial = @EventID+18, + @AQWarAllianceBandagesT1 = @EventID+19, + @AQWarAllianceBandagesT2 = @EventID+20, + @AQWarAllianceBandagesT3 = @EventID+21, + @AQWarAllianceBandagesT4 = @EventID+22, + @AQWarAllianceBandagesT5 = @EventID+23, + @AQWarAllianceCookedGoodsInitial = @EventID+24, + @AQWarAllianceCookedGoodsT1 = @EventID+25, + @AQWarAllianceCookedGoodsT2 = @EventID+26, + @AQWarAllianceCookedGoodsT3 = @EventID+27, + @AQWarAllianceCookedGoodsT4 = @EventID+28, + @AQWarAllianceCookedGoodsT5 = @EventID+29, + @AQWarHordeBarsInitial = @EventID+30, + @AQWarHordeBarsT1 = @EventID+31, + @AQWarHordeBarsT2 = @EventID+32, + @AQWarHordeBarsT3 = @EventID+33, + @AQWarHordeBarsT4 = @EventID+34, + @AQWarHordeBarsT5 = @EventID+35, + @AQWarHordeHerbsInitial = @EventID+36, + @AQWarHordeHerbsT1 = @EventID+37, + @AQWarHordeHerbsT2 = @EventID+38, + @AQWarHordeHerbsT3 = @EventID+39, + @AQWarHordeHerbsT4 = @EventID+40, + @AQWarHordeHerbsT5 = @EventID+41, + @AQWarHordeSkinsInitial = @EventID+42, + @AQWarHordeSkinsT1 = @EventID+43, + @AQWarHordeSkinsT2 = @EventID+44, + @AQWarHordeSkinsT3 = @EventID+45, + @AQWarHordeSkinsT4 = @EventID+46, + @AQWarHordeSkinsT5 = @EventID+47, + @AQWarHordeBandagesInitial = @EventID+48, + @AQWarHordeBandagesT1 = @EventID+49, + @AQWarHordeBandagesT2 = @EventID+50, + @AQWarHordeBandagesT3 = @EventID+51, + @AQWarHordeBandagesT4 = @EventID+52, + @AQWarHordeBandagesT5 = @EventID+53, + @AQWarHordeCookedGoodsInitial = @EventID+54, + @AQWarHordeCookedGoodsT1 = @EventID+55, + @AQWarHordeCookedGoodsT2 = @EventID+56, + @AQWarHordeCookedGoodsT3 = @EventID+57, + @AQWarHordeCookedGoodsT4 = @EventID+58, + @AQWarHordeCookedGoodsT5 = @EventID+59; + +DELETE FROM `game_event` WHERE `eventEntry` IN (@AQWarAllianceBarsInitial, @AQWarAllianceBarsT1, @AQWarAllianceBarsT2, @AQWarAllianceBarsT3, @AQWarAllianceBarsT4, @AQWarAllianceBarsT5, @AQWarAllianceHerbsInitial, @AQWarAllianceHerbsT1, @AQWarAllianceHerbsT2, @AQWarAllianceHerbsT3, @AQWarAllianceHerbsT4, @AQWarAllianceHerbsT5, @AQWarAllianceSkinsInitial, @AQWarAllianceSkinsT1, @AQWarAllianceSkinsT2, @AQWarAllianceSkinsT3, @AQWarAllianceSkinsT4, @AQWarAllianceSkinsT5, @AQWarAllianceBandagesInitial, @AQWarAllianceBandagesT1, @AQWarAllianceBandagesT2, @AQWarAllianceBandagesT3, @AQWarAllianceBandagesT4, @AQWarAllianceBandagesT5, @AQWarAllianceCookedGoodsInitial, @AQWarAllianceCookedGoodsT1, @AQWarAllianceCookedGoodsT2, @AQWarAllianceCookedGoodsT3, @AQWarAllianceCookedGoodsT4, @AQWarAllianceCookedGoodsT5, @AQWarHordeBarsInitial, @AQWarHordeBarsT1, @AQWarHordeBarsT2, @AQWarHordeBarsT3, @AQWarHordeBarsT4, @AQWarHordeBarsT5, @AQWarHordeHerbsInitial, @AQWarHordeHerbsT1, @AQWarHordeHerbsT2, @AQWarHordeHerbsT3, @AQWarHordeHerbsT4, @AQWarHordeHerbsT5, @AQWarHordeSkinsInitial, @AQWarHordeSkinsT1, @AQWarHordeSkinsT2, @AQWarHordeSkinsT3, @AQWarHordeSkinsT4, @AQWarHordeSkinsT5, @AQWarHordeBandagesInitial, @AQWarHordeBandagesT1, @AQWarHordeBandagesT2, @AQWarHordeBandagesT3, @AQWarHordeBandagesT4, @AQWarHordeBandagesT5, @AQWarHordeCookedGoodsInitial, @AQWarHordeCookedGoodsT1, @AQWarHordeCookedGoodsT2, @AQWarHordeCookedGoodsT3, @AQWarHordeCookedGoodsT4, @AQWarHordeCookedGoodsT5); +INSERT INTO `game_event` (`eventEntry`, `start_time`, `end_time`, `occurence`, `length`, `holiday`, `holidayStage`, `description`, `world_event`, `announce`) VALUES +(@AQWarAllianceBarsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Initial', 5, 2), +(@AQWarAllianceBarsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 1', 5, 2), +(@AQWarAllianceBarsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 2', 5, 2), +(@AQWarAllianceBarsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 3', 5, 2), +(@AQWarAllianceBarsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 4', 5, 2), +(@AQWarAllianceBarsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bars Tier 5', 5, 2), +(@AQWarAllianceHerbsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Initial', 5, 2), +(@AQWarAllianceHerbsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 1', 5, 2), +(@AQWarAllianceHerbsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 2', 5, 2), +(@AQWarAllianceHerbsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 3', 5, 2), +(@AQWarAllianceHerbsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 4', 5, 2), +(@AQWarAllianceHerbsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Herbs Tier 5', 5, 2), +(@AQWarAllianceSkinsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Initial', 5, 2), +(@AQWarAllianceSkinsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 1', 5, 2), +(@AQWarAllianceSkinsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 2', 5, 2), +(@AQWarAllianceSkinsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 3', 5, 2), +(@AQWarAllianceSkinsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 4', 5, 2), +(@AQWarAllianceSkinsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Skins Tier 5', 5, 2), +(@AQWarAllianceBandagesInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Initial', 5, 2), +(@AQWarAllianceBandagesT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 1', 5, 2), +(@AQWarAllianceBandagesT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 2', 5, 2), +(@AQWarAllianceBandagesT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 3', 5, 2), +(@AQWarAllianceBandagesT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 4', 5, 2), +(@AQWarAllianceBandagesT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Bandages Tier 5', 5, 2), +(@AQWarAllianceCookedGoodsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Initial', 5, 2), +(@AQWarAllianceCookedGoodsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 1', 5, 2), +(@AQWarAllianceCookedGoodsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 2', 5, 2), +(@AQWarAllianceCookedGoodsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 3', 5, 2), +(@AQWarAllianceCookedGoodsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 4', 5, 2), +(@AQWarAllianceCookedGoodsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Alliance Cooked Goods Tier 5', 5, 2), +(@AQWarHordeBarsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Initial', 5, 2), +(@AQWarHordeBarsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 1', 5, 2), +(@AQWarHordeBarsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 2', 5, 2), +(@AQWarHordeBarsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 3', 5, 2), +(@AQWarHordeBarsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 4', 5, 2), +(@AQWarHordeBarsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bars Tier 5', 5, 2), +(@AQWarHordeHerbsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Initial', 5, 2), +(@AQWarHordeHerbsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 1', 5, 2), +(@AQWarHordeHerbsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 2', 5, 2), +(@AQWarHordeHerbsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 3', 5, 2), +(@AQWarHordeHerbsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 4', 5, 2), +(@AQWarHordeHerbsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Herbs Tier 5', 5, 2), +(@AQWarHordeSkinsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Initial', 5, 2), +(@AQWarHordeSkinsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 1', 5, 2), +(@AQWarHordeSkinsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 2', 5, 2), +(@AQWarHordeSkinsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 3', 5, 2), +(@AQWarHordeSkinsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 4', 5, 2), +(@AQWarHordeSkinsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Skins Tier 5', 5, 2), +(@AQWarHordeBandagesInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Initial', 5, 2), +(@AQWarHordeBandagesT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 1', 5, 2), +(@AQWarHordeBandagesT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 2', 5, 2), +(@AQWarHordeBandagesT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 3', 5, 2), +(@AQWarHordeBandagesT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 4', 5, 2), +(@AQWarHordeBandagesT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Bandages Tier 5', 5, 2), +(@AQWarHordeCookedGoodsInitial, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Initial', 5, 2), +(@AQWarHordeCookedGoodsT1, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 1', 5, 2), +(@AQWarHordeCookedGoodsT2, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 2', 5, 2), +(@AQWarHordeCookedGoodsT3, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 3', 5, 2), +(@AQWarHordeCookedGoodsT4, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 4', 5, 2), +(@AQWarHordeCookedGoodsT5, '2000-01-01 14:00:00', '2000-01-01 14:00:00', 5184000, 2592000, 0, 0, 'Ahn''Qiraj War Effort - Horde Cooked Goods Tier 5', 5, 2); + +DELETE FROM `game_event_creature` WHERE `eventEntry` = 22 AND `guid` BETWEEN @CGUID AND @CGUID+69; +INSERT INTO `game_event_creature` (`eventEntry`, `guid`) VALUES +(22, @CGUID+0), +(22, @CGUID+1), +(22, @CGUID+2), +(22, @CGUID+3), +(22, @CGUID+4), +(22, @CGUID+5), +(22, @CGUID+6), +(22, @CGUID+7), +(22, @CGUID+8), +(22, @CGUID+9), +(22, @CGUID+10), +(22, @CGUID+11), +(22, @CGUID+12), +(22, @CGUID+13), +(22, @CGUID+14), +(22, @CGUID+15), +(22, @CGUID+16), +(22, @CGUID+17), +(22, @CGUID+18), +(22, @CGUID+19), +(22, @CGUID+20), +(22, @CGUID+21), +(22, @CGUID+22), +(22, @CGUID+23), +(22, @CGUID+24), +(22, @CGUID+25), +(22, @CGUID+26), +(22, @CGUID+27), +(22, @CGUID+28), +(22, @CGUID+29), +(22, @CGUID+30), +(22, @CGUID+31), +(22, @CGUID+32), +(22, @CGUID+33), +(22, @CGUID+34), +(22, @CGUID+35), +(22, @CGUID+36), +(22, @CGUID+37), +(22, @CGUID+38), +(22, @CGUID+39), +(22, @CGUID+40), +(22, @CGUID+41), +(22, @CGUID+42), +(22, @CGUID+43), +(22, @CGUID+44), +(22, @CGUID+45), +(22, @CGUID+46), +(22, @CGUID+47), +(22, @CGUID+48), +(22, @CGUID+49), +(22, @CGUID+50), +(22, @CGUID+51), +(22, @CGUID+52), +(22, @CGUID+53), +(22, @CGUID+54), +(22, @CGUID+55), +(22, @CGUID+56), +(22, @CGUID+57), +(22, @CGUID+58), +(22, @CGUID+59), +(22, @CGUID+60), +(22, @CGUID+61), +(22, @CGUID+62), +(22, @CGUID+63), +(22, @CGUID+64), +(22, @CGUID+65), +(22, @CGUID+66), +(22, @CGUID+67), +(22, @CGUID+68), +(22, @CGUID+69); + +DELETE FROM `game_event_creature` WHERE `eventEntry` = 22 AND `guid` IN (37, 2032, 6519, 6520, 25997, 32076, 46803); + +DELETE FROM `game_event_gameobject` WHERE `eventEntry` IN (@AQWarAllianceBarsInitial, @AQWarAllianceBarsT1, @AQWarAllianceBarsT2, @AQWarAllianceBarsT3, @AQWarAllianceBarsT4, @AQWarAllianceBarsT5, @AQWarAllianceHerbsInitial, @AQWarAllianceHerbsT1, @AQWarAllianceHerbsT2, @AQWarAllianceHerbsT3, @AQWarAllianceHerbsT4, @AQWarAllianceHerbsT5, @AQWarAllianceSkinsInitial, @AQWarAllianceSkinsT1, @AQWarAllianceSkinsT2, @AQWarAllianceSkinsT3, @AQWarAllianceSkinsT4, @AQWarAllianceSkinsT5, @AQWarAllianceBandagesInitial, @AQWarAllianceBandagesT1, @AQWarAllianceBandagesT2, @AQWarAllianceBandagesT3, @AQWarAllianceBandagesT4, @AQWarAllianceBandagesT5, @AQWarAllianceCookedGoodsInitial, @AQWarAllianceCookedGoodsT1, @AQWarAllianceCookedGoodsT2, @AQWarAllianceCookedGoodsT3, @AQWarAllianceCookedGoodsT4, @AQWarAllianceCookedGoodsT5, @AQWarHordeBarsInitial, @AQWarHordeBarsT1, @AQWarHordeBarsT2, @AQWarHordeBarsT3, @AQWarHordeBarsT4, @AQWarHordeBarsT5, @AQWarHordeHerbsInitial, @AQWarHordeHerbsT1, @AQWarHordeHerbsT2, @AQWarHordeHerbsT3, @AQWarHordeHerbsT4, @AQWarHordeHerbsT5, @AQWarHordeSkinsInitial, @AQWarHordeSkinsT1, @AQWarHordeSkinsT2, @AQWarHordeSkinsT3, @AQWarHordeSkinsT4, @AQWarHordeSkinsT5, @AQWarHordeBandagesInitial, @AQWarHordeBandagesT1, @AQWarHordeBandagesT2, @AQWarHordeBandagesT3, @AQWarHordeBandagesT4, @AQWarHordeBandagesT5, @AQWarHordeCookedGoodsInitial, @AQWarHordeCookedGoodsT1, @AQWarHordeCookedGoodsT2, @AQWarHordeCookedGoodsT3, @AQWarHordeCookedGoodsT4, @AQWarHordeCookedGoodsT5); +INSERT INTO `game_event_gameobject` (`eventEntry`, `guid`) VALUES +(@AQWarAllianceBarsInitial, @OGUID+19), +(@AQWarAllianceBarsT1, @OGUID+19), +(@AQWarAllianceBarsT1, @OGUID+20), +(@AQWarAllianceBarsT2, @OGUID+19), +(@AQWarAllianceBarsT2, @OGUID+20), +(@AQWarAllianceBarsT2, @OGUID+21), +(@AQWarAllianceBarsT3, @OGUID+19), +(@AQWarAllianceBarsT3, @OGUID+20), +(@AQWarAllianceBarsT3, @OGUID+21), +(@AQWarAllianceBarsT3, @OGUID+22), +(@AQWarAllianceBarsT4, @OGUID+19), +(@AQWarAllianceBarsT4, @OGUID+20), +(@AQWarAllianceBarsT4, @OGUID+21), +(@AQWarAllianceBarsT4, @OGUID+22), +(@AQWarAllianceBarsT4, @OGUID+23), +(@AQWarAllianceBarsT5, @OGUID+19), +(@AQWarAllianceBarsT5, @OGUID+20), +(@AQWarAllianceBarsT5, @OGUID+21), +(@AQWarAllianceBarsT5, @OGUID+22), +(@AQWarAllianceBarsT5, @OGUID+23), +(@AQWarAllianceBarsT5, @OGUID+24), +(@AQWarAllianceHerbsInitial, @OGUID+25), +(@AQWarAllianceHerbsT1, @OGUID+25), +(@AQWarAllianceHerbsT1, @OGUID+31), +(@AQWarAllianceHerbsT2, @OGUID+25), +(@AQWarAllianceHerbsT2, @OGUID+31), +(@AQWarAllianceHerbsT2, @OGUID+32), +(@AQWarAllianceHerbsT3, @OGUID+25), +(@AQWarAllianceHerbsT3, @OGUID+31), +(@AQWarAllianceHerbsT3, @OGUID+32), +(@AQWarAllianceHerbsT3, @OGUID+33), +(@AQWarAllianceHerbsT4, @OGUID+25), +(@AQWarAllianceHerbsT4, @OGUID+31), +(@AQWarAllianceHerbsT4, @OGUID+32), +(@AQWarAllianceHerbsT4, @OGUID+33), +(@AQWarAllianceHerbsT4, @OGUID+34), +(@AQWarAllianceHerbsT5, @OGUID+25), +(@AQWarAllianceHerbsT5, @OGUID+31), +(@AQWarAllianceHerbsT5, @OGUID+32), +(@AQWarAllianceHerbsT5, @OGUID+33), +(@AQWarAllianceHerbsT5, @OGUID+34), +(@AQWarAllianceHerbsT5, @OGUID+35), +(@AQWarAllianceSkinsInitial, @OGUID+36), +(@AQWarAllianceSkinsT1, @OGUID+36), +(@AQWarAllianceSkinsT1, @OGUID+37), +(@AQWarAllianceSkinsT2, @OGUID+36), +(@AQWarAllianceSkinsT2, @OGUID+37), +(@AQWarAllianceSkinsT2, @OGUID+38), +(@AQWarAllianceSkinsT3, @OGUID+36), +(@AQWarAllianceSkinsT3, @OGUID+37), +(@AQWarAllianceSkinsT3, @OGUID+38), +(@AQWarAllianceSkinsT3, @OGUID+39), +(@AQWarAllianceSkinsT4, @OGUID+36), +(@AQWarAllianceSkinsT4, @OGUID+37), +(@AQWarAllianceSkinsT4, @OGUID+38), +(@AQWarAllianceSkinsT4, @OGUID+39), +(@AQWarAllianceSkinsT4, @OGUID+40), +(@AQWarAllianceSkinsT5, @OGUID+36), +(@AQWarAllianceSkinsT5, @OGUID+37), +(@AQWarAllianceSkinsT5, @OGUID+38), +(@AQWarAllianceSkinsT5, @OGUID+39), +(@AQWarAllianceSkinsT5, @OGUID+40), +(@AQWarAllianceSkinsT5, @OGUID+41), +(@AQWarAllianceBandagesInitial, @OGUID+0), +(@AQWarAllianceBandagesT1, @OGUID+0), +(@AQWarAllianceBandagesT1, @OGUID+1), +(@AQWarAllianceBandagesT1, @OGUID+6), +(@AQWarAllianceBandagesT2, @OGUID+0), +(@AQWarAllianceBandagesT2, @OGUID+1), +(@AQWarAllianceBandagesT2, @OGUID+2), +(@AQWarAllianceBandagesT2, @OGUID+6), +(@AQWarAllianceBandagesT2, @OGUID+7), +(@AQWarAllianceBandagesT2, @OGUID+8), +(@AQWarAllianceBandagesT2, @OGUID+9), +(@AQWarAllianceBandagesT3, @OGUID+0), +(@AQWarAllianceBandagesT3, @OGUID+1), +(@AQWarAllianceBandagesT3, @OGUID+2), +(@AQWarAllianceBandagesT3, @OGUID+3), +(@AQWarAllianceBandagesT3, @OGUID+6), +(@AQWarAllianceBandagesT3, @OGUID+7), +(@AQWarAllianceBandagesT3, @OGUID+8), +(@AQWarAllianceBandagesT3, @OGUID+9), +(@AQWarAllianceBandagesT3, @OGUID+10), +(@AQWarAllianceBandagesT3, @OGUID+11), +(@AQWarAllianceBandagesT3, @OGUID+12), +(@AQWarAllianceBandagesT3, @OGUID+13), +(@AQWarAllianceBandagesT4, @OGUID+0), +(@AQWarAllianceBandagesT4, @OGUID+1), +(@AQWarAllianceBandagesT4, @OGUID+2), +(@AQWarAllianceBandagesT4, @OGUID+3), +(@AQWarAllianceBandagesT4, @OGUID+4), +(@AQWarAllianceBandagesT4, @OGUID+6), +(@AQWarAllianceBandagesT4, @OGUID+7), +(@AQWarAllianceBandagesT4, @OGUID+8), +(@AQWarAllianceBandagesT4, @OGUID+9), +(@AQWarAllianceBandagesT4, @OGUID+10), +(@AQWarAllianceBandagesT4, @OGUID+11), +(@AQWarAllianceBandagesT4, @OGUID+12), +(@AQWarAllianceBandagesT4, @OGUID+13), +(@AQWarAllianceBandagesT4, @OGUID+14), +(@AQWarAllianceBandagesT4, @OGUID+15), +(@AQWarAllianceBandagesT4, @OGUID+16), +(@AQWarAllianceBandagesT4, @OGUID+17), +(@AQWarAllianceBandagesT4, @OGUID+18), +(@AQWarAllianceBandagesT5, @OGUID+0), +(@AQWarAllianceBandagesT5, @OGUID+1), +(@AQWarAllianceBandagesT5, @OGUID+2), +(@AQWarAllianceBandagesT5, @OGUID+3), +(@AQWarAllianceBandagesT5, @OGUID+4), +(@AQWarAllianceBandagesT5, @OGUID+5), +(@AQWarAllianceBandagesT5, @OGUID+6), +(@AQWarAllianceBandagesT5, @OGUID+7), +(@AQWarAllianceBandagesT5, @OGUID+8), +(@AQWarAllianceBandagesT5, @OGUID+9), +(@AQWarAllianceBandagesT5, @OGUID+10), +(@AQWarAllianceBandagesT5, @OGUID+11), +(@AQWarAllianceBandagesT5, @OGUID+12), +(@AQWarAllianceBandagesT5, @OGUID+13), +(@AQWarAllianceBandagesT5, @OGUID+14), +(@AQWarAllianceBandagesT5, @OGUID+15), +(@AQWarAllianceBandagesT5, @OGUID+16), +(@AQWarAllianceBandagesT5, @OGUID+17), +(@AQWarAllianceBandagesT5, @OGUID+18), +(@AQWarAllianceBandagesT5, @OGUID+72), +(@AQWarAllianceBandagesT5, @OGUID+73), +(@AQWarAllianceBandagesT5, @OGUID+74), +(@AQWarAllianceBandagesT5, @OGUID+75), +(@AQWarAllianceBandagesT5, @OGUID+76), +(@AQWarAllianceBandagesT5, @OGUID+77), +(@AQWarAllianceBandagesT5, @OGUID+78), +(@AQWarAllianceBandagesT5, @OGUID+79), +(@AQWarAllianceBandagesT5, @OGUID+80), +(@AQWarAllianceBandagesT5, @OGUID+81), +(@AQWarAllianceCookedGoodsInitial, @OGUID+25), +(@AQWarAllianceCookedGoodsT1, @OGUID+25), +(@AQWarAllianceCookedGoodsT1, @OGUID+26), +(@AQWarAllianceCookedGoodsT2, @OGUID+25), +(@AQWarAllianceCookedGoodsT2, @OGUID+26), +(@AQWarAllianceCookedGoodsT2, @OGUID+27), +(@AQWarAllianceCookedGoodsT3, @OGUID+25), +(@AQWarAllianceCookedGoodsT3, @OGUID+26), +(@AQWarAllianceCookedGoodsT3, @OGUID+27), +(@AQWarAllianceCookedGoodsT3, @OGUID+28), +(@AQWarAllianceCookedGoodsT4, @OGUID+25), +(@AQWarAllianceCookedGoodsT4, @OGUID+26), +(@AQWarAllianceCookedGoodsT4, @OGUID+27), +(@AQWarAllianceCookedGoodsT4, @OGUID+28), +(@AQWarAllianceCookedGoodsT4, @OGUID+29), +(@AQWarAllianceCookedGoodsT5, @OGUID+25), +(@AQWarAllianceCookedGoodsT5, @OGUID+26), +(@AQWarAllianceCookedGoodsT5, @OGUID+27), +(@AQWarAllianceCookedGoodsT5, @OGUID+28), +(@AQWarAllianceCookedGoodsT5, @OGUID+29), +(@AQWarAllianceCookedGoodsT5, @OGUID+30), +(@AQWarHordeBarsInitial, @OGUID+48), +(@AQWarHordeBarsT1, @OGUID+48), +(@AQWarHordeBarsT1, @OGUID+49), +(@AQWarHordeBarsT2, @OGUID+48), +(@AQWarHordeBarsT2, @OGUID+49), +(@AQWarHordeBarsT2, @OGUID+50), +(@AQWarHordeBarsT3, @OGUID+48), +(@AQWarHordeBarsT3, @OGUID+49), +(@AQWarHordeBarsT3, @OGUID+50), +(@AQWarHordeBarsT3, @OGUID+51), +(@AQWarHordeBarsT4, @OGUID+48), +(@AQWarHordeBarsT4, @OGUID+49), +(@AQWarHordeBarsT4, @OGUID+50), +(@AQWarHordeBarsT4, @OGUID+51), +(@AQWarHordeBarsT4, @OGUID+52), +(@AQWarHordeBarsT5, @OGUID+48), +(@AQWarHordeBarsT5, @OGUID+49), +(@AQWarHordeBarsT5, @OGUID+50), +(@AQWarHordeBarsT5, @OGUID+51), +(@AQWarHordeBarsT5, @OGUID+52), +(@AQWarHordeBarsT5, @OGUID+53), +(@AQWarHordeHerbsInitial, @OGUID+60), +(@AQWarHordeHerbsT1, @OGUID+60), +(@AQWarHordeHerbsT1, @OGUID+61), +(@AQWarHordeHerbsT2, @OGUID+60), +(@AQWarHordeHerbsT2, @OGUID+61), +(@AQWarHordeHerbsT2, @OGUID+62), +(@AQWarHordeHerbsT3, @OGUID+60), +(@AQWarHordeHerbsT3, @OGUID+61), +(@AQWarHordeHerbsT3, @OGUID+62), +(@AQWarHordeHerbsT3, @OGUID+63), +(@AQWarHordeHerbsT4, @OGUID+60), +(@AQWarHordeHerbsT4, @OGUID+61), +(@AQWarHordeHerbsT4, @OGUID+62), +(@AQWarHordeHerbsT4, @OGUID+63), +(@AQWarHordeHerbsT4, @OGUID+64), +(@AQWarHordeHerbsT5, @OGUID+60), +(@AQWarHordeHerbsT5, @OGUID+61), +(@AQWarHordeHerbsT5, @OGUID+62), +(@AQWarHordeHerbsT5, @OGUID+63), +(@AQWarHordeHerbsT5, @OGUID+64), +(@AQWarHordeHerbsT5, @OGUID+65), +(@AQWarHordeSkinsInitial, @OGUID+66), +(@AQWarHordeSkinsT1, @OGUID+66), +(@AQWarHordeSkinsT1, @OGUID+67), +(@AQWarHordeSkinsT2, @OGUID+66), +(@AQWarHordeSkinsT2, @OGUID+67), +(@AQWarHordeSkinsT2, @OGUID+68), +(@AQWarHordeSkinsT3, @OGUID+66), +(@AQWarHordeSkinsT3, @OGUID+67), +(@AQWarHordeSkinsT3, @OGUID+68), +(@AQWarHordeSkinsT3, @OGUID+69), +(@AQWarHordeSkinsT4, @OGUID+66), +(@AQWarHordeSkinsT4, @OGUID+67), +(@AQWarHordeSkinsT4, @OGUID+68), +(@AQWarHordeSkinsT4, @OGUID+69), +(@AQWarHordeSkinsT4, @OGUID+70), +(@AQWarHordeSkinsT5, @OGUID+66), +(@AQWarHordeSkinsT5, @OGUID+67), +(@AQWarHordeSkinsT5, @OGUID+68), +(@AQWarHordeSkinsT5, @OGUID+69), +(@AQWarHordeSkinsT5, @OGUID+70), +(@AQWarHordeSkinsT5, @OGUID+71), +(@AQWarHordeBandagesInitial, @OGUID+42), +(@AQWarHordeBandagesT1, @OGUID+42), +(@AQWarHordeBandagesT1, @OGUID+43), +(@AQWarHordeBandagesT2, @OGUID+42), +(@AQWarHordeBandagesT2, @OGUID+43), +(@AQWarHordeBandagesT2, @OGUID+44), +(@AQWarHordeBandagesT3, @OGUID+42), +(@AQWarHordeBandagesT3, @OGUID+43), +(@AQWarHordeBandagesT3, @OGUID+44), +(@AQWarHordeBandagesT3, @OGUID+45), +(@AQWarHordeBandagesT4, @OGUID+42), +(@AQWarHordeBandagesT4, @OGUID+43), +(@AQWarHordeBandagesT4, @OGUID+44), +(@AQWarHordeBandagesT4, @OGUID+45), +(@AQWarHordeBandagesT4, @OGUID+46), +(@AQWarHordeBandagesT5, @OGUID+42), +(@AQWarHordeBandagesT5, @OGUID+43), +(@AQWarHordeBandagesT5, @OGUID+44), +(@AQWarHordeBandagesT5, @OGUID+45), +(@AQWarHordeBandagesT5, @OGUID+46), +(@AQWarHordeBandagesT5, @OGUID+47), +(@AQWarHordeCookedGoodsInitial, @OGUID+54), +(@AQWarHordeCookedGoodsT1, @OGUID+54), +(@AQWarHordeCookedGoodsT1, @OGUID+55), +(@AQWarHordeCookedGoodsT2, @OGUID+54), +(@AQWarHordeCookedGoodsT2, @OGUID+55), +(@AQWarHordeCookedGoodsT2, @OGUID+56), +(@AQWarHordeCookedGoodsT3, @OGUID+54), +(@AQWarHordeCookedGoodsT3, @OGUID+55), +(@AQWarHordeCookedGoodsT3, @OGUID+56), +(@AQWarHordeCookedGoodsT3, @OGUID+57), +(@AQWarHordeCookedGoodsT4, @OGUID+54), +(@AQWarHordeCookedGoodsT4, @OGUID+55), +(@AQWarHordeCookedGoodsT4, @OGUID+56), +(@AQWarHordeCookedGoodsT4, @OGUID+57), +(@AQWarHordeCookedGoodsT4, @OGUID+58), +(@AQWarHordeCookedGoodsT5, @OGUID+54), +(@AQWarHordeCookedGoodsT5, @OGUID+55), +(@AQWarHordeCookedGoodsT5, @OGUID+56), +(@AQWarHordeCookedGoodsT5, @OGUID+57), +(@AQWarHordeCookedGoodsT5, @OGUID+58), +(@AQWarHordeCookedGoodsT5, @OGUID+59); diff --git a/data/sql/updates/pending_db_world/gobject-spawns.sql b/data/sql/updates/pending_db_world/gobject-spawns.sql new file mode 100644 index 000000000..8b2082ea4 --- /dev/null +++ b/data/sql/updates/pending_db_world/gobject-spawns.sql @@ -0,0 +1,102 @@ +SET @GUID = 3639; +-- Note: Some of these are CO2s, especially since there's some weird destroy/recreate thing going on on official, but I just noted them as CO1s for the time being. +DELETE FROM `gameobject` WHERE `id` IN (180598, 180674, 180675, 180676, 180677, 180678, 180679, 180680, 180681, 180692, 180693, 180694, 180695, 180696, 180714, 180780, 180781, 180782, 180783, 180784, 180800, 180801, 180802, 180803, 180804, 180805, 180806, 180807, 180808, 180809, 180812, 180813, 180814, 180815, 180816, 180817, 180818, 180819, 180820, 180821, 180822, 180823, 180826, 180827, 180828, 180829, 180830, 180831, 180832, 180833, 180834, 180835, 180836, 180837, 180838, 180839, 180840, 180841, 180842, 180843) AND `guid` BETWEEN @GUID AND @GUID+81; +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +-- Ironforge +-- Bandages +(@GUID+0, 180598, 0, 0, 0, 1, 1, -4971.5483, -1148.5706, 501.648, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+1, 180674, 0, 0, 0, 1, 1, -4968.3267, -1152.889, 501.9254, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+2, 180675, 0, 0, 0, 1, 1, -4969.2095, -1143.8433, 509.2506, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+3, 180676, 0, 0, 0, 1, 1, -4983.004, -1136.2194, 501.6594, 2.3038306, 0, 0, 0.91354465, 0.40673843, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+4, 180677, 0, 0, 0, 1, 1, -4975.6016, -1147.3348, 509.2504, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+5, 180678, 0, 0, 0, 1, 1, -4974.111, -1148.3993, 510.8475, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T5 +-- CrateAllianceFirstAid01 +(@GUID+6, 180714, 0, 0, 0, 1, 1, -4972.807, -1145.8307, 501.64996, 3.6128378, 0, 0, -0.9723692, 0.23344836, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+7, 180714, 0, 0, 0, 1, 1, -4976.2925, -1158.7479, 501.64252, 0.85521054, 0, 0, 0.41469288, 0.90996146, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+8, 180714, 0, 0, 0, 1, 1, -4979.9673, -1146.8904, 501.65506, 4.2062464, 0, 0, -0.86162853, 0.5075394, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+9, 180714, 0, 0, 0, 1, 1, -4972.857, -1145.8458, 502.8929, 2.1991146, 0, 0, 0.89100647, 0.45399064, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+10, 180714, 0, 0, 0, 1, 1, -4977.4546, -1157.944, 501.64926, 3.9269955, 0, 0, -0.92387867, 0.3826855, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+11, 180714, 0, 0, 0, 1, 1, -4985.743, -1137.5488, 501.6594, 3.5430236, 0, 0, -0.9799242, 0.19937038, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+12, 180714, 0, 0, 0, 1, 1, -4979.9263, -1139.9106, 501.65945, 1.3613561, 0, 0, 0.62932014, 0.77714616, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+13, 180714, 0, 0, 0, 1, 1, -4972.919, -1145.8206, 504.1289, 1.7627825, 0, 0, 0.77162457, 0.63607824, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+14, 180714, 0, 0, 0, 1, 1, -4976.9517, -1158.2965, 502.88937, 4.153885, 0, 0, -0.8746195, 0.48481005, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+15, 180714, 0, 0, 0, 1, 1, -4972.6714, -1152.6599, 509.25027, 0.3316107, 0, 0, 0.16504669, 0.98628575, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+16, 180714, 0, 0, 0, 1, 1, -4968.471, -1149.9625, 501.9254, 5.0265493, 0, 0, -0.58778477, 0.80901736, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+17, 180714, 0, 0, 0, 1, 1, -4979.914, -1139.9069, 502.9025, 1.4835281, 0, 0, 0.67558956, 0.7372779, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+18, 180714, 0, 0, 0, 1, 1, -4985.936, -1135.9319, 501.6594, 3.1590624, 0, 0, -0.99996185, 0.008734641, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+72, 180714, 0, 0, 0, 1, 1, -4972.6826, -1136.9497, 509.77707, 4.3807764, 0, 0, -0.8141155, 0.58070296, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+73, 180714, 0, 0, 0, 1, 1, -4979.8037, -1146.875, 502.89078, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+74, 180714, 0, 0, 0, 1, 1, -4971.91, -1153.9872, 509.25027, 5.2359877, 0, 0, -0.5, 0.8660254, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+75, 180714, 0, 0, 0, 1, 1, -4978.415, -1159.3356, 501.64444, 3.0368383, 0, 0, 0.9986286, 0.052353222, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+76, 180714, 0, 0, 0, 1, 1, -4974.837, -1156.8785, 503.5236, 6.003934, 0, 0, -0.13917255, 0.9902682, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+77, 180714, 0, 0, 0, 1, 1, -4986.0234, -1136.7771, 502.90247, 3.1940022, 0, 0, -0.9996567, 0.026201647, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+78, 180714, 0, 0, 0, 1, 1, -4979.7725, -1139.9098, 504.1386, 5.98648, 0, 0, -0.14780903, 0.98901594, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+79, 180714, 0, 0, 0, 1, 1, -4968.3887, -1150.027, 503.16846, 0.36651757, 0, 0, 0.18223476, 0.983255, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+80, 180714, 0, 0, 0, 1, 1, -4972.4443, -1153.132, 510.49332, 5.2534423, 0, 0, -0.49242306, 0.87035596, 300, 0, 1, '', 61582, NULL), -- T5 +(@GUID+81, 180714, 0, 0, 0, 1, 1, -4984.3228, -1135.7563, 504.87466, 3.6302915, 0, 0, -0.97029495, 0.241925, 300, 0, 1, '', 61582, NULL), -- T5 +-- Bars +(@GUID+19, 180680, 0, 0, 0, 1, 1, -4913.854, -1225.9967, 501.6508, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+20, 180780, 0, 0, 0, 1, 1, -4913.729, -1225.9507, 501.6506, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+21, 180781, 0, 0, 0, 1, 1, -4913.737, -1225.9277, 501.65067, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+22, 180782, 0, 0, 0, 1, 1, -4913.723, -1225.9167, 501.65067, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+23, 180783, 0, 0, 0, 1, 1, -4913.71, -1225.9053, 501.65067, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+24, 180784, 0, 0, 0, 1, 1, -4913.7773, -1225.8594, 501.65082, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- T5 +-- Cooking +(@GUID+25, 180679, 0, 0, 0, 1, 1, -4937.2886, -1282.7358, 501.67215, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- Cooking/Herbs Initial +(@GUID+26, 180800, 0, 0, 0, 1, 1, -4937.282, -1282.8739, 501.67227, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+27, 180806, 0, 0, 0, 1, 1, -4937.136, -1282.895, 501.6721, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+28, 180807, 0, 0, 0, 1, 1, -4937.2856, -1282.869, 501.67227, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+29, 180808, 0, 0, 0, 1, 1, -4937.336, -1282.7858, 501.67227, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+30, 180809, 0, 0, 0, 1, 1, -4937.2197, -1282.8075, 501.67215, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- T5 +-- Herbs +(@GUID+31, 180801, 0, 0, 0, 1, 1, -4935.5796, -1284.82, 501.67105, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+32, 180802, 0, 0, 0, 1, 1, -4935.6045, -1284.8381, 501.6711, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+33, 180803, 0, 0, 0, 1, 1, -4935.594, -1284.833, 501.6711, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+34, 180804, 0, 0, 0, 1, 1, -4935.621, -1284.8282, 501.6711, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+35, 180805, 0, 0, 0, 1, 1, -4935.5576, -1284.8864, 501.67105, 2.2340178, 0, 0, 0.8987932, 0.43837282, 300, 0, 1, '', 61582, NULL), -- T5 +-- Skins +(@GUID+36, 180681, 0, 0, 0, 1, 1, -4958.5093, -1179.3196, 501.65945, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+37, 180692, 0, 0, 0, 1, 1, -4958.517, -1179.3344, 501.65945, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+38, 180693, 0, 0, 0, 1, 1, -4958.528, -1179.335, 501.65945, 2.2514734, 0, 0, 0.902585, 0.43051165, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+39, 180694, 0, 0, 0, 1, 1, -4958.526, -1179.3273, 501.65945, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+40, 180695, 0, 0, 0, 1, 1, -4958.5156, -1179.3286, 501.65945, 2.2689254, 0, 0, 0.9063072, 0.4226195, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+41, 180696, 0, 0, 0, 1, 1, -4958.5293, -1179.3357, 501.65945, 2.2863789, 0, 0, 0.90996075, 0.4146944, 300, 0, 1, '', 61582, NULL), -- T5 +-- Orgrimmar +-- Bandages +(@GUID+42, 180826, 1, 0, 0, 1, 1, 1579.3531, -4109.2544, 34.541737, 3.7524624, 0, 0, -0.9537163, 0.3007079, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+43, 180827, 1, 0, 0, 1, 1, 1579.3342, -4109.2476, 34.54871, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+44, 180828, 1, 0, 0, 1, 1, 1579.321, -4109.278, 34.551514, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+45, 180829, 1, 0, 0, 1, 1, 1579.333, -4109.2837, 34.547028, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+46, 180830, 1, 0, 0, 1, 1, 1579.3325, -4109.2783, 34.547504, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+47, 180831, 1, 0, 0, 1, 1, 1579.331, -4109.2827, 34.54776, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T5 +-- Bars +(@GUID+48, 180838, 1, 0, 0, 1, 1, 1683.1149, -4134.354, 39.541912, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+49, 180839, 1, 0, 0, 1, 1, 1683.0984, -4134.31, 39.539, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+50, 180840, 1, 0, 0, 1, 1, 1683.097, -4134.3, 39.538742, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+51, 180841, 1, 0, 0, 1, 1, 1683.1057, -4134.3135, 39.54028, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+52, 180842, 1, 0, 0, 1, 1, 1683.0347, -4134.3135, 39.52796, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+53, 180843, 1, 0, 0, 1, 1, 1683.1418, -4134.3403, 39.54657, 3.7524624, 0, 0, -0.9537163, 0.3007079, 300, 0, 1, '', 61582, NULL), -- T5 +-- Cooking +(@GUID+54, 180832, 1, 0, 0, 1, 1, 1619.8307, -4092.4302, 34.51068, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+55, 180833, 1, 0, 0, 1, 1, 1619.8004, -4092.5334, 34.488815, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+56, 180834, 1, 0, 0, 1, 1, 1619.8002, -4092.5317, 34.489204, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+57, 180835, 1, 0, 0, 1, 1, 1619.8073, -4092.5269, 34.490135, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+58, 180836, 1, 0, 0, 1, 1, 1619.8062, -4092.5305, 34.48937, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+59, 180837, 1, 0, 0, 1, 1, 1619.8094, -4092.5217, 34.491196, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T5 +-- Herbs +(@GUID+60, 180818, 1, 0, 0, 1, 1, 1637.1053, -4147.2134, 36.04144, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+61, 180819, 1, 0, 0, 1, 1, 1637.1001, -4147.2534, 36.053123, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+62, 180820, 1, 0, 0, 1, 1, 1637.111, -4147.2534, 36.05357, 3.735006, 0, 0, -0.95630455, 0.29237235, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+63, 180821, 1, 0, 0, 1, 1, 1637.1099, -4147.2573, 36.054718, 3.7524624, 0, 0, -0.9537163, 0.3007079, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+64, 180822, 1, 0, 0, 1, 1, 1637.079, -4147.228, 36.04483, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+65, 180823, 1, 0, 0, 1, 1, 1637.1013, -4147.226, 36.045063, 3.7175536, 0, 0, -0.9588194, 0.28401646, 300, 0, 1, '', 61582, NULL), -- T5 +-- Skins +(@GUID+66, 180812, 1, 0, 0, 1, 1, 1590.8248, -4155.328, 36.292576, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- Initial +(@GUID+67, 180813, 1, 0, 0, 1, 1, 1590.8755, -4155.335, 36.298023, 3.6826503, 0, 0, -0.9636297, 0.267241, 300, 0, 1, '', 61582, NULL), -- T1 +(@GUID+68, 180814, 1, 0, 0, 1, 1, 1590.8511, -4155.342, 36.299625, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T2 +(@GUID+69, 180815, 1, 0, 0, 1, 1, 1590.8533, -4155.339, 36.299156, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T3 +(@GUID+70, 180816, 1, 0, 0, 1, 1, 1590.8516, -4155.341, 36.299488, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL), -- T4 +(@GUID+71, 180817, 1, 0, 0, 1, 1, 1590.8517, -4155.3438, 36.299927, 3.7001047, 0, 0, -0.9612608, 0.2756405, 300, 0, 1, '', 61582, NULL); -- T5 + +-- Old spawns +DELETE FROM `gameobject` WHERE `guid` IN (29294, 29299, 29300, 29301) AND `id` IN (180598, 180679, 180680, 180681); From efd7a006306ba8da1da822022708fd08926bc1c9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 23 Aug 2025 05:12:05 +0000 Subject: [PATCH 037/155] chore(DB): import pending files Referenced commit(s): fc90e30e5016d4bf705b4aa49c8be7c4e1e27035 --- .../creature-spawns.sql => db_world/2025_08_23_00.sql} | 1 + .../game-events.sql => db_world/2025_08_23_01.sql} | 1 + .../gobject-spawns.sql => db_world/2025_08_23_02.sql} | 1 + 3 files changed, 3 insertions(+) rename data/sql/updates/{pending_db_world/creature-spawns.sql => db_world/2025_08_23_00.sql} (99%) rename data/sql/updates/{pending_db_world/game-events.sql => db_world/2025_08_23_01.sql} (99%) rename data/sql/updates/{pending_db_world/gobject-spawns.sql => db_world/2025_08_23_02.sql} (99%) diff --git a/data/sql/updates/pending_db_world/creature-spawns.sql b/data/sql/updates/db_world/2025_08_23_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/creature-spawns.sql rename to data/sql/updates/db_world/2025_08_23_00.sql index 23346778e..064b7cb64 100644 --- a/data/sql/updates/pending_db_world/creature-spawns.sql +++ b/data/sql/updates/db_world/2025_08_23_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_19_00 -> 2025_08_23_00 SET @GUID = 83113; DELETE FROM `creature` WHERE `id1` IN (3296, 5595, 15383, 15431, 15432, 15434, 15437, 15445, 15446, 15448, 15450, 15451, 15452, 15453, 15455, 15456, 15457, 15458, 15459, 15460, 15469, 15477, 15508, 15512, 15515, 15522, 15525, 15528, 15529, 15532, 15533, 15534, 15535, 15539, 15663, 15696, 15700, 15701, 15702, 15703, 15704, 15707, 15708, 15709, 15731, 15733, 15734, 15735, 15736, 15737, 15738, 15739, 15761, 15762, 15763, 15764, 15765, 15766, 15767, 15768) AND `guid` BETWEEN @GUID AND @GUID+69; diff --git a/data/sql/updates/pending_db_world/game-events.sql b/data/sql/updates/db_world/2025_08_23_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/game-events.sql rename to data/sql/updates/db_world/2025_08_23_01.sql index 17b1fb939..d63bf23ff 100644 --- a/data/sql/updates/pending_db_world/game-events.sql +++ b/data/sql/updates/db_world/2025_08_23_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_23_00 -> 2025_08_23_01 SET @EventID = 131, @OGUID = 3639, @CGUID = 83113; diff --git a/data/sql/updates/pending_db_world/gobject-spawns.sql b/data/sql/updates/db_world/2025_08_23_02.sql similarity index 99% rename from data/sql/updates/pending_db_world/gobject-spawns.sql rename to data/sql/updates/db_world/2025_08_23_02.sql index 8b2082ea4..acc01795c 100644 --- a/data/sql/updates/pending_db_world/gobject-spawns.sql +++ b/data/sql/updates/db_world/2025_08_23_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_23_01 -> 2025_08_23_02 SET @GUID = 3639; -- Note: Some of these are CO2s, especially since there's some weird destroy/recreate thing going on on official, but I just noted them as CO1s for the time being. DELETE FROM `gameobject` WHERE `id` IN (180598, 180674, 180675, 180676, 180677, 180678, 180679, 180680, 180681, 180692, 180693, 180694, 180695, 180696, 180714, 180780, 180781, 180782, 180783, 180784, 180800, 180801, 180802, 180803, 180804, 180805, 180806, 180807, 180808, 180809, 180812, 180813, 180814, 180815, 180816, 180817, 180818, 180819, 180820, 180821, 180822, 180823, 180826, 180827, 180828, 180829, 180830, 180831, 180832, 180833, 180834, 180835, 180836, 180837, 180838, 180839, 180840, 180841, 180842, 180843) AND `guid` BETWEEN @GUID AND @GUID+81; From 3bdcd83f4a2e4d9c1db34b7d9497e1c9120fc28b Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 23 Aug 2025 04:39:32 -0300 Subject: [PATCH 038/155] fix(Scripts/Spells): Fix Death and Decay interrupting spellcasting (#22720) --- src/server/scripts/Spells/spell_dk.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index d1d371710..b2d312a5a 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -345,7 +345,7 @@ class spell_dk_death_and_decay_aura : public AuraScript if (GetCaster() && GetTarget()) { int32 basePoints0 = aurEff->GetAmount(); - GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, false, 0, aurEff); + GetCaster()->CastCustomSpell(GetTarget(), SPELL_DK_DEATH_AND_DECAY_TRIGGER, &basePoints0, nullptr, nullptr, true, 0, aurEff); } } From 2a73b89f6a2446b1225c79c29d8834621e161977 Mon Sep 17 00:00:00 2001 From: sudlud Date: Sat, 23 Aug 2025 21:05:17 +0200 Subject: [PATCH 039/155] fix(CI/modules-build): disable archived / outdated module mod-war-effort (#22722) --- apps/ci/ci-install-modules.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/ci/ci-install-modules.sh b/apps/ci/ci-install-modules.sh index d584682ab..5987eaf7a 100755 --- a/apps/ci/ci-install-modules.sh +++ b/apps/ci/ci-install-modules.sh @@ -100,7 +100,8 @@ git clone --depth=1 --branch=main https://github.com/azerothcore/mod-system-vi git clone --depth=1 --branch=master https://github.com/azerothcore/mod-tic-tac-toe modules/mod-tic-tac-toe git clone --depth=1 --branch=master https://github.com/azerothcore/mod-top-arena modules/mod-top-arena git clone --depth=1 --branch=master https://github.com/azerothcore/mod-transmog modules/mod-transmog -git clone --depth=1 --branch=master https://github.com/azerothcore/mod-war-effort modules/mod-war-effort +# archived / outdated +#git clone --depth=1 --branch=master https://github.com/azerothcore/mod-war-effort modules/mod-war-effort git clone --depth=1 --branch=master https://github.com/azerothcore/mod-weekend-xp modules/mod-weekend-xp git clone --depth=1 --branch=master https://github.com/azerothcore/mod-who-logged modules/mod-who-logged git clone --depth=1 --branch=master https://github.com/azerothcore/mod-zone-difficulty modules/mod-zone-difficulty From a1eb3e5cec4a52811979411b63179946089b36ad Mon Sep 17 00:00:00 2001 From: sudlud Date: Sun, 24 Aug 2025 07:40:37 +0200 Subject: [PATCH 040/155] fix(DB/SAI): fix event_type of Pit Commander SAI entry (#22723) --- data/sql/updates/pending_db_world/rev_1755975503363547000.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1755975503363547000.sql diff --git a/data/sql/updates/pending_db_world/rev_1755975503363547000.sql b/data/sql/updates/pending_db_world/rev_1755975503363547000.sql new file mode 100644 index 000000000..061c27534 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1755975503363547000.sql @@ -0,0 +1,2 @@ +-- +UPDATE `smart_scripts` SET `event_type` = 61 WHERE `entryorguid` = 18945 AND `source_type` = 0 AND `id` = 4; From 915b39202a7c602546947b4b8ab70f5d42ce34b5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 24 Aug 2025 05:41:39 +0000 Subject: [PATCH 041/155] chore(DB): import pending files Referenced commit(s): a1eb3e5cec4a52811979411b63179946089b36ad --- .../rev_1755975503363547000.sql => db_world/2025_08_24_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1755975503363547000.sql => db_world/2025_08_24_00.sql} (71%) diff --git a/data/sql/updates/pending_db_world/rev_1755975503363547000.sql b/data/sql/updates/db_world/2025_08_24_00.sql similarity index 71% rename from data/sql/updates/pending_db_world/rev_1755975503363547000.sql rename to data/sql/updates/db_world/2025_08_24_00.sql index 061c27534..0f3d678d9 100644 --- a/data/sql/updates/pending_db_world/rev_1755975503363547000.sql +++ b/data/sql/updates/db_world/2025_08_24_00.sql @@ -1,2 +1,3 @@ +-- DB update 2025_08_23_02 -> 2025_08_24_00 -- UPDATE `smart_scripts` SET `event_type` = 61 WHERE `entryorguid` = 18945 AND `source_type` = 0 AND `id` = 4; From 9ed31bd63e56cbc6a4d82a491bd08576d108a73c Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Sun, 24 Aug 2025 08:50:16 -0400 Subject: [PATCH 042/155] refactor(Core/Packets): Rewrite various query packets to modern class. (#22719) --- src/server/game/Handlers/QueryHandler.cpp | 59 ++++++------- src/server/game/Handlers/TicketHandler.cpp | 2 +- src/server/game/Server/Packets/AllPackets.h | 1 + .../game/Server/Packets/QueryPackets.cpp | 58 +++++++++++++ src/server/game/Server/Packets/QueryPackets.h | 87 +++++++++++++++++++ src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Server/WorldSession.h | 15 +++- 7 files changed, 184 insertions(+), 40 deletions(-) create mode 100644 src/server/game/Server/Packets/QueryPackets.cpp create mode 100644 src/server/game/Server/Packets/QueryPackets.h diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index 0666af71c..604977301 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -24,6 +24,7 @@ #include "Opcodes.h" #include "Pet.h" #include "Player.h" +#include "QueryPackets.h" #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" @@ -32,62 +33,55 @@ void WorldSession::SendNameQueryOpcode(ObjectGuid guid) { CharacterCacheEntry const* playerData = sCharacterCache->GetCharacterCacheByGuid(guid); - WorldPacket data(SMSG_NAME_QUERY_RESPONSE, (8 + 1 + 1 + 1 + 1 + 1 + 10)); - data << guid.WriteAsPacked(); + WorldPackets::Query::NameQueryResponse nameQueryResponse; + nameQueryResponse.Guid = guid.WriteAsPacked(); if (!playerData) { - data << uint8(1); // name unknown - SendPacket(&data); + nameQueryResponse.NameUnknown = true; + SendPacket(nameQueryResponse.Write()); return; } Player* player = ObjectAccessor::FindConnectedPlayer(guid); - data << uint8(0); // name known - data << playerData->Name; // played name - data << uint8(0); // realm name - only set for cross realm interaction (such as Battlegrounds) - data << uint8(player ? player->getRace() : playerData->Race); - data << uint8(playerData->Sex); - data << uint8(playerData->Class); + nameQueryResponse.NameUnknown = false; + nameQueryResponse.Name = playerData->Name; + nameQueryResponse.Race = player ? player->getRace() : playerData->Race; + nameQueryResponse.Sex = player ? player->getGender() : playerData->Sex; + nameQueryResponse.Class = player ? player->getClass() : playerData->Class; - // pussywizard: optimization - /*Player* player = ObjectAccessor::FindConnectedPlayer(guid); if (DeclinedName const* names = (player ? player->GetDeclinedNames() : nullptr)) { - data << uint8(1); // Name is declined - for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) - data << names->name[i]; + nameQueryResponse.Declined = true; + nameQueryResponse.DeclinedNames = *names; } - else*/ - data << uint8(0); // Name is not declined + else + nameQueryResponse.Declined = false; - SendPacket(&data); + SendPacket(nameQueryResponse.Write()); } -void WorldSession::HandleNameQueryOpcode(WorldPacket& recvData) +void WorldSession::HandleNameQueryOpcode(WorldPackets::Query::NameQuery& packet) { - ObjectGuid guid; - recvData >> guid; - // This is disable by default to prevent lots of console spam // LOG_INFO("network.opcode", "HandleNameQueryOpcode {}", guid); - SendNameQueryOpcode(guid); + SendNameQueryOpcode(packet.Guid); } -void WorldSession::HandleQueryTimeOpcode(WorldPacket& /*recvData*/) +void WorldSession::HandleTimeQueryOpcode(WorldPackets::Query::TimeQuery& /*packet*/) { - SendQueryTimeResponse(); + SendTimeQueryResponse(); } -void WorldSession::SendQueryTimeResponse() +void WorldSession::SendTimeQueryResponse() { auto timeResponse = sWorld->GetNextDailyQuestsResetTime() - GameTime::GetGameTime(); - WorldPacket data(SMSG_QUERY_TIME_RESPONSE, 4 + 4); - data << uint32(GameTime::GetGameTime().count()); - data << uint32(timeResponse.count()); - SendPacket(&data); + WorldPackets::Query::TimeQueryResponse timeQueryResponse; + timeQueryResponse.ServerTime = GameTime::GetGameTime().count(); + timeQueryResponse.TimeResponse = timeResponse.count(); + SendPacket(timeQueryResponse.Write()); } /// Only _static_ data is sent in this packet !!! @@ -402,13 +396,10 @@ void WorldSession::HandlePageTextQueryOpcode(WorldPacket& recvData) } } -void WorldSession::HandleCorpseMapPositionQuery(WorldPacket& recvData) +void WorldSession::HandleCorpseMapPositionQuery(WorldPackets::Query::CorpseMapPositionQuery& /*packet*/) { LOG_DEBUG("network", "WORLD: Recv CMSG_CORPSE_MAP_POSITION_QUERY"); - uint32 unk; - recvData >> unk; - WorldPacket data(SMSG_CORPSE_MAP_POSITION_QUERY_RESPONSE, 4 + 4 + 4 + 4); data << float(0); data << float(0); diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp index eb6243b21..089546aa5 100644 --- a/src/server/game/Handlers/TicketHandler.cpp +++ b/src/server/game/Handlers/TicketHandler.cpp @@ -172,7 +172,7 @@ void WorldSession::HandleGMTicketDeleteOpcode(WorldPacket& /*recv_data*/) void WorldSession::HandleGMTicketGetTicketOpcode(WorldPacket& /*recv_data*/) { - SendQueryTimeResponse(); + SendTimeQueryResponse(); if (GmTicket* ticket = sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID())) { diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 1e3457e1d..30aecbb6f 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -27,6 +27,7 @@ #include "LFGPackets.h" #include "MiscPackets.h" #include "PetPackets.h" +#include "QueryPackets.h" #include "TotemPackets.h" #include "WorldStatePackets.h" diff --git a/src/server/game/Server/Packets/QueryPackets.cpp b/src/server/game/Server/Packets/QueryPackets.cpp new file mode 100644 index 000000000..6efa96275 --- /dev/null +++ b/src/server/game/Server/Packets/QueryPackets.cpp @@ -0,0 +1,58 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "QueryPackets.h" + +void WorldPackets::Query::NameQuery::Read() +{ + _worldPacket >> Guid; +} + +WorldPacket const* WorldPackets::Query::NameQueryResponse::Write() +{ + _worldPacket << Guid; + _worldPacket << NameUnknown; + if (NameUnknown) + return &_worldPacket; + + _worldPacket << Name; + _worldPacket << RealmName; + _worldPacket << Race; + _worldPacket << Sex; + _worldPacket << Class; + _worldPacket << Declined; + if (Declined) + { + for (uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i) + _worldPacket << DeclinedNames.name[i]; + } + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Query::TimeQueryResponse::Write() +{ + _worldPacket << ServerTime; + _worldPacket << TimeResponse; + + return &_worldPacket; +} + +void WorldPackets::Query::CorpseMapPositionQuery::Read() +{ + _worldPacket >> unk; +} diff --git a/src/server/game/Server/Packets/QueryPackets.h b/src/server/game/Server/Packets/QueryPackets.h new file mode 100644 index 000000000..85d7a76ce --- /dev/null +++ b/src/server/game/Server/Packets/QueryPackets.h @@ -0,0 +1,87 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef QueryPackets_h__ +#define QueryPackets_h__ + +#include "Packet.h" +#include "Unit.h" + +namespace WorldPackets +{ + namespace Query + { + class NameQuery final : public ClientPacket + { + public: + NameQuery(WorldPacket&& packet) : ClientPacket(CMSG_NAME_QUERY, std::move(packet)) {} + + void Read() override; + + ObjectGuid Guid; + }; + + class NameQueryResponse final : public ServerPacket + { + public: + NameQueryResponse() : ServerPacket(SMSG_NAME_QUERY_RESPONSE, 8 + 1 + 1 + 1 + 1 + 1 + 10) {} + + WorldPacket const* Write() override; + + PackedGuid Guid; + uint8 NameUnknown = false; + std::string_view Name; + std::string_view RealmName = ""; // Only set for cross realm interaction (such as Battlegrounds) + uint8 Race = RACE_NONE; + uint8 Sex = GENDER_MALE; + uint8 Class = CLASS_NONE; + uint8 Declined = false; + DeclinedName DeclinedNames; + }; + + class TimeQuery final : public ClientPacket + { + public: + TimeQuery(WorldPacket&& packet) : ClientPacket(CMSG_QUERY_TIME, std::move(packet)) {} + + void Read() override {}; + }; + + class TimeQueryResponse final : public ServerPacket + { + public: + TimeQueryResponse() : ServerPacket(SMSG_QUERY_TIME_RESPONSE, 4 + 4) {} + + WorldPacket const* Write() override; + + uint32 ServerTime; + uint32 TimeResponse; + }; + + class CorpseMapPositionQuery final : public ClientPacket + { + public: + CorpseMapPositionQuery(WorldPacket&& packet) : ClientPacket(CMSG_CORPSE_MAP_POSITION_QUERY, std::move(packet)) {} + + void Read() override; + + uint32 unk; + }; + } +} + +#endif // QueryPackets_h__ diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 85bcb4186..bc12f7764 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -590,7 +590,7 @@ void OpcodeTable::Initialize() /*0x1CB*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_NOTIFICATION, STATUS_NEVER); /*0x1CC*/ DEFINE_HANDLER(CMSG_PLAYED_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandlePlayedTime ); /*0x1CD*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_PLAYED_TIME, STATUS_NEVER); - /*0x1CE*/ DEFINE_HANDLER(CMSG_QUERY_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleQueryTimeOpcode ); + /*0x1CE*/ DEFINE_HANDLER(CMSG_QUERY_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleTimeQueryOpcode ); /*0x1CF*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_TIME_RESPONSE, STATUS_NEVER); /*0x1D0*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_LOG_XPGAIN, STATUS_NEVER); /*0x1D1*/ DEFINE_SERVER_OPCODE_HANDLER(SMSG_AURACASTLOG, STATUS_NEVER); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 9e570f9b0..032e18abf 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -160,6 +160,13 @@ namespace WorldPackets class PetSpellAutocast; class RequestPetInfo; } + + namespace Query + { + class NameQuery; + class TimeQuery; + class CorpseMapPositionQuery; + } } enum AccountDataType @@ -371,7 +378,7 @@ public: } void SendSetPhaseShift(uint32 phaseShift); - void SendQueryTimeResponse(); + void SendTimeQueryResponse(); void SendAuthResponse(uint8 code, bool shortForm, uint32 queuePos = 0); void SendClientCacheVersion(uint32 version); @@ -661,9 +668,9 @@ public: // opcodes handlers void HandleGameObjectUseOpcode(WorldPacket& recPacket); void HandleGameobjectReportUse(WorldPacket& recvPacket); - void HandleNameQueryOpcode(WorldPacket& recvPacket); + void HandleNameQueryOpcode(WorldPackets::Query::NameQuery& packet); - void HandleQueryTimeOpcode(WorldPacket& recvPacket); + void HandleTimeQueryOpcode(WorldPackets::Query::TimeQuery& packet); void HandleCreatureQueryOpcode(WorldPacket& recvPacket); @@ -863,7 +870,7 @@ public: // opcodes handlers void HandleReclaimCorpseOpcode(WorldPacket& recvPacket); void HandleCorpseQueryOpcode(WorldPacket& recvPacket); - void HandleCorpseMapPositionQuery(WorldPacket& recvPacket); + void HandleCorpseMapPositionQuery(WorldPackets::Query::CorpseMapPositionQuery& packet); void HandleResurrectResponseOpcode(WorldPacket& recvPacket); void HandleSummonResponseOpcode(WorldPacket& recvData); From cd9bdf7a665b170463b08492ca2fe1cb97fa2574 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Mon, 25 Aug 2025 11:52:07 -0300 Subject: [PATCH 043/155] fix(DB/Conditions): Everfrost Chip should require atleast friendly Sons of Hodir standing (#22718) --- data/sql/updates/pending_db_world/rev_1755916063283631200.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1755916063283631200.sql diff --git a/data/sql/updates/pending_db_world/rev_1755916063283631200.sql b/data/sql/updates/pending_db_world/rev_1755916063283631200.sql new file mode 100644 index 000000000..3ab80be7b --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1755916063283631200.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 4) AND (`SourceGroup` = 193997) AND (`SourceEntry` = 44725) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 5) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 1119) AND (`ConditionValue2` = 16) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(4, 26782, 44725, 0, 0, 5, 0, 1119, 240, 0, 0, 0, 0, '', 'Everfrost Chip requires Sons of Hodir friendly'); From 8a340b1efb8bb078e05adb398703b19900c6688c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 25 Aug 2025 14:53:13 +0000 Subject: [PATCH 044/155] chore(DB): import pending files Referenced commit(s): cd9bdf7a665b170463b08492ca2fe1cb97fa2574 --- .../rev_1755916063283631200.sql => db_world/2025_08_25_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1755916063283631200.sql => db_world/2025_08_25_00.sql} (94%) diff --git a/data/sql/updates/pending_db_world/rev_1755916063283631200.sql b/data/sql/updates/db_world/2025_08_25_00.sql similarity index 94% rename from data/sql/updates/pending_db_world/rev_1755916063283631200.sql rename to data/sql/updates/db_world/2025_08_25_00.sql index 3ab80be7b..c5793e675 100644 --- a/data/sql/updates/pending_db_world/rev_1755916063283631200.sql +++ b/data/sql/updates/db_world/2025_08_25_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_24_00 -> 2025_08_25_00 -- DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 4) AND (`SourceGroup` = 193997) AND (`SourceEntry` = 44725) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 5) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 1119) AND (`ConditionValue2` = 16) AND (`ConditionValue3` = 0); INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES From 2e614069a53c3ea46856ad391e083c860a33b383 Mon Sep 17 00:00:00 2001 From: sudlud Date: Mon, 25 Aug 2025 17:32:35 +0200 Subject: [PATCH 045/155] =?UTF-8?q?fix(Entities/Creature):=20do=20not=20to?= =?UTF-8?q?uch=20setActive()=20state=20in=20setDeathSta=E2=80=A6=20(#22729?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Takenbacon --- src/server/game/Entities/Creature/Creature.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 8ef6206bb..996b5ae05 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1986,8 +1986,6 @@ void Creature::setDeathState(DeathState state, bool despawn) Dismount(); // if creature is mounted on a virtual mount, remove it at death - setActive(false); - if (HasSearchedAssistance()) { SetNoSearchAssistance(false); From ddf0997c558edcb43637d4592c54c046e4542859 Mon Sep 17 00:00:00 2001 From: sudlud Date: Mon, 25 Aug 2025 17:33:43 +0200 Subject: [PATCH 046/155] fix(DB/SAI): set dark portal event creatures active (#22724) --- .../rev_1756036806680249000.sql | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756036806680249000.sql diff --git a/data/sql/updates/pending_db_world/rev_1756036806680249000.sql b/data/sql/updates/pending_db_world/rev_1756036806680249000.sql new file mode 100644 index 000000000..f9ff17b47 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756036806680249000.sql @@ -0,0 +1,59 @@ +-- set dark portal creatures active +DELETE FROM `smart_scripts` WHERE (`entryorguid` IN (18944, 18946, 18948, 18949, 18950, 18965, 18966, 18969, 18970, 18971, 18972, 18986, -68744, -68745, -74081, -74082)) AND (`source_type` = 0) AND (`event_type` IN (11, 36)) AND (`id` IN (42, 43)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +-- 18944 Fel Soldier +(18944, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fel Soldier - On Respawn - Set Active On'), +(18944, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Fel Soldier - On Corpse Removed - Set Active On'), +-- 18945 Pit Commander, already set active +-- 18946 Infernal Siegebreaker +(18946, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Siegebreaker - On Respawn - Set Active On'), +(18946, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Siegebreaker - On Corpse Removed - Set Active On'), +-- 18948 Stormwind Soldier +(18948, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormwind Soldier - On Respawn - Set Active On'), +(18948, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormwind Soldier - On Corpse Removed - Set Active On'), +-- 18949 Stormwind Mage +(18949, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormwind Mage - On Respawn - Set Active On'), +(18949, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Stormwind Mage - On Corpse Removed - Set Active On'), +-- 18950 Orgrimmar Grunt +(18950, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Orgrimmar Grunt - On Respawn - Set Active On'), +(18950, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Orgrimmar Grunt - On Corpse Removed - Set Active On'), +-- 18965 Darnassian Archer +(18965, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Archer - On Respawn - Set Active On'), +(18965, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darnassian Archer - On Corpse Removed - Set Active On'), +-- 18966 Justinius the Harbinger +(18966, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Justinius the Harbinger - On Respawn - Set Active On'), +(18966, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Justinius the Harbinger - On Corpse Removed - Set Active On'), +-- 18969 Melgromm Highmountain +(18969, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Melgromm Highmountain - On Respawn - Set Active On'), +(18969, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Melgromm Highmountain - On Corpse Removed - Set Active On'), +-- 18970 Darkspear Axe Thrower +(18970, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darkspear Axe Thrower - On Respawn - Set Active On'), +(18970, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Darkspear Axe Thrower - On Corpse Removed - Set Active On'), +-- 18971 Undercity Mage +(18971, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Undercity Mage - On Respawn - Set Active On'), +(18971, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Undercity Mage - On Corpse Removed - Set Active On'), +-- 18972 Orgrimmar Shaman +(18972, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Orgrimmar Shaman - On Respawn - Set Active On'), +(18972, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Orgrimmar Shaman - On Corpse Removed - Set Active On'), +-- 18986 Ironforge Paladin +(18986, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ironforge Paladin - On Respawn - Set Active On'), +(18986, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ironforge Paladin - On Corpse Removed - Set Active On'), +-- 19005 Wrath Master, GUID SAI -68311, -68312, -68313, -68314, already set active +-- 19215 Infernal Relay (Hellfire) +(-68744, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Relay (Hellfire) - On Respawn - Set Active On'), +(-68744, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Relay (Hellfire) - On Corpse Removed - Set Active On'), +(-68745, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Relay (Hellfire) - On Respawn - Set Active On'), +(-68745, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Relay (Hellfire) - On Corpse Removed - Set Active On'), +-- 21075 Infernal Target (Hyjal) +(-74081, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Target (Hyjal) - On Respawn - Set Active On'), +(-74081, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Target (Hyjal)- On Corpse Removed - Set Active On'), +(-74082, 0, 42, 0, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Target (Hyjal) - On Respawn - Set Active On'), +(-74082, 0, 43, 0, 36, 0, 100, 512, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Infernal Target (Hyjal) - On Corpse Removed - Set Active On'); + +-- cleanup +-- Infernal Relay (Hellfire) used to set nearby 19005 Wrath Master active +DELETE FROM `smart_scripts` WHERE (`entryorguid` = -68744) AND (`source_type` = 0) AND (`id` IN (10)); +UPDATE `smart_scripts` SET `link` = 0 WHERE (`entryorguid` = -68744) AND (`source_type` = 0) AND (`id` IN (9)); + +-- update spawn comment for GUID SAI +UPDATE `creature` SET `Comment` = 'GUID SAI, SAI Target' WHERE (`id1` = 19215) AND (`guid` = 68745); From d22d5ae40ca48525d238807242452b7f08d73a57 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 25 Aug 2025 15:33:46 +0000 Subject: [PATCH 047/155] chore(DB): import pending files Referenced commit(s): 2e614069a53c3ea46856ad391e083c860a33b383 --- .../rev_1756036806680249000.sql => db_world/2025_08_25_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756036806680249000.sql => db_world/2025_08_25_01.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1756036806680249000.sql b/data/sql/updates/db_world/2025_08_25_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1756036806680249000.sql rename to data/sql/updates/db_world/2025_08_25_01.sql index f9ff17b47..196057350 100644 --- a/data/sql/updates/pending_db_world/rev_1756036806680249000.sql +++ b/data/sql/updates/db_world/2025_08_25_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_25_00 -> 2025_08_25_01 -- set dark portal creatures active DELETE FROM `smart_scripts` WHERE (`entryorguid` IN (18944, 18946, 18948, 18949, 18950, 18965, 18966, 18969, 18970, 18971, 18972, 18986, -68744, -68745, -74081, -74082)) AND (`source_type` = 0) AND (`event_type` IN (11, 36)) AND (`id` IN (42, 43)); INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES From f02af61358e1376823ddd15a821ec2d03815e811 Mon Sep 17 00:00:00 2001 From: sudlud Date: Mon, 25 Aug 2025 19:21:33 +0200 Subject: [PATCH 048/155] fix(DB/SAI): fix dark portal creatures calling for help on aggro (#22730) --- .../sql/updates/pending_db_world/rev_1756036806680249000.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756036806680249000.sql diff --git a/data/sql/updates/pending_db_world/rev_1756036806680249000.sql b/data/sql/updates/pending_db_world/rev_1756036806680249000.sql new file mode 100644 index 000000000..e49637c15 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756036806680249000.sql @@ -0,0 +1,5 @@ +-- fix dark portal creatures calling for help on aggro +-- 18948 Stormwind Soldier +UPDATE `smart_scripts` SET `event_type` = 4 WHERE (`entryorguid` = 18948) AND (`source_type` = 0) AND (`id` IN (7)); +-- 18950 Orgrimmar Grunt +UPDATE `smart_scripts` SET `event_type` = 4 WHERE (`entryorguid` = 18950) AND (`source_type` = 0) AND (`id` IN (7)); From 846efc3b6407b83da1b09eee78d1d94a654b219a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 25 Aug 2025 17:22:38 +0000 Subject: [PATCH 049/155] chore(DB): import pending files Referenced commit(s): f02af61358e1376823ddd15a821ec2d03815e811 --- .../rev_1756036806680249000.sql => db_world/2025_08_25_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756036806680249000.sql => db_world/2025_08_25_02.sql} (88%) diff --git a/data/sql/updates/pending_db_world/rev_1756036806680249000.sql b/data/sql/updates/db_world/2025_08_25_02.sql similarity index 88% rename from data/sql/updates/pending_db_world/rev_1756036806680249000.sql rename to data/sql/updates/db_world/2025_08_25_02.sql index e49637c15..47a79017c 100644 --- a/data/sql/updates/pending_db_world/rev_1756036806680249000.sql +++ b/data/sql/updates/db_world/2025_08_25_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_25_01 -> 2025_08_25_02 -- fix dark portal creatures calling for help on aggro -- 18948 Stormwind Soldier UPDATE `smart_scripts` SET `event_type` = 4 WHERE (`entryorguid` = 18948) AND (`source_type` = 0) AND (`id` IN (7)); From de98f424116e77b9ec802db59d4c515cc6be144f Mon Sep 17 00:00:00 2001 From: Yehonal Date: Mon, 25 Aug 2025 20:25:17 +0200 Subject: [PATCH 050/155] feat(Service Manager): add service registry custom dir and restore functionality (#22589) This pull request introduces significant enhancements to the service management system by adding a service registry with features like automatic tracking, reboot persistence, and restoration of missing services. The goal of this PR is to allow the user to store the service configuration files into an arbitrary directory, in this way they can be easily tracked, versioned, and replicated across different environments It also includes a migration script to transition from the legacy service configuration format to the new registry-based system. Below is a summary of the most important changes: ### Service Registry and Management Enhancements: 1. **Service Registry Integration**: - Added a comprehensive service registry system to track all created services, enabling features like cross-reboot persistence and restoration of missing services (`apps/startup-scripts/src/service-manager.sh`). [[1]](diffhunk://#diff-31edfed7f73d0647a5fc96ce74c249e025e884cd1fe06621cb78eb4a381464f9R41-R229) [[2]](diffhunk://#diff-31edfed7f73d0647a5fc96ce74c249e025e884cd1fe06621cb78eb4a381464f9R273) - Introduced commands for managing the registry, such as `restore` for recreating missing services and `list` for viewing registered services. [[1]](diffhunk://#diff-31edfed7f73d0647a5fc96ce74c249e025e884cd1fe06621cb78eb4a381464f9R273) [[2]](diffhunk://#diff-31edfed7f73d0647a5fc96ce74c249e025e884cd1fe06621cb78eb4a381464f9R332-R334) [[3]](diffhunk://#diff-31edfed7f73d0647a5fc96ce74c249e025e884cd1fe06621cb78eb4a381464f9R346-L172) 2. **PM2 Persistence**: - Enhanced PM2 integration to automatically configure startup persistence across reboots using `pm2 startup` and `pm2 save` after service creation. ### Migration and Compatibility: 3. **Migration Script**: - Added a `migrate-registry.sh` script to convert legacy service configurations into the new registry format. It ensures compatibility while preserving existing service information (`apps/startup-scripts/src/migrate-registry.sh`). ### Documentation Updates: 4. **Updated README**: - Expanded documentation in `README.md` to explain the new service registry features, including usage examples, custom configuration directories, and migration instructions. [[1]](diffhunk://#diff-0917b2888cc9b16539173f318b77773d08f7bf360579b68b9710a96ca2bcbb64L387-R468) [[2]](diffhunk://#diff-0917b2888cc9b16539173f318b77773d08f7bf360579b68b9710a96ca2bcbb64R613-R626) ### Configuration Improvements: 5. **Custom Configuration Directories**: - Added support for overriding the default configuration directory for service registry and files using the `AC_SERVICE_CONFIG_DIR` environment variable. [[1]](diffhunk://#diff-31edfed7f73d0647a5fc96ce74c249e025e884cd1fe06621cb78eb4a381464f9L14-R15) [[2]](diffhunk://#diff-31edfed7f73d0647a5fc96ce74c249e025e884cd1fe06621cb78eb4a381464f9R346-L172) These changes significantly improve the usability, reliability, and maintainability of the service management system, especially for setups requiring persistence and multi-project configurations. --- apps/startup-scripts/README.md | 95 ++++- apps/startup-scripts/src/migrate-registry.sh | 144 +++++++ apps/startup-scripts/src/service-manager.sh | 407 +++++++++++++++---- 3 files changed, 561 insertions(+), 85 deletions(-) create mode 100755 apps/startup-scripts/src/migrate-registry.sh diff --git a/apps/startup-scripts/README.md b/apps/startup-scripts/README.md index 7b057581d..176ec3ed6 100644 --- a/apps/startup-scripts/README.md +++ b/apps/startup-scripts/README.md @@ -312,6 +312,9 @@ Services support two restart policies: # Edit configuration ./service-manager.sh edit world + +# Restore missing services from registry +./service-manager.sh restore ``` ## 🌍 Multiple Realms Setup @@ -384,22 +387,72 @@ cp examples/restarter-world.sh restarter-realm2.sh ## 🛠️ Service Management +### Service Registry and Persistence + +The service manager includes a comprehensive registry system that tracks all created services and enables automatic restoration: + +#### Service Registry Features + +- **Automatic Tracking**: All services are automatically registered when created +- **Cross-Reboot Persistence**: PM2 services are configured with startup persistence +- **Service Restoration**: Missing services can be detected and restored from registry +- **Migration Support**: Legacy service configurations can be migrated to the new format + +#### Using the Registry + +```bash +# Check for missing services and restore them +./service-manager.sh restore + +# List all registered services (includes status) +./service-manager.sh list + +# Services are automatically added to registry on creation +./service-manager.sh create auth authserver --bin-path /path/to/bin +``` + +#### Custom Configuration Directories + +You can customize where service configurations and PM2/systemd files are stored: + +```bash +# Set custom directories +export AC_SERVICE_CONFIG_DIR="/path/to/your/project/services" + +# Now all service operations will use these custom directories +./service-manager.sh create auth authserver --bin-path /path/to/bin +``` + +This is particularly useful for: +- **Version Control**: Keep service configurations in your project repository +- **Multiple Projects**: Separate service configurations per project +- **Team Collaboration**: Share service setups across development teams + +#### Migration from Legacy Format + +If you have existing services in the old format, use the migration script: + +```bash +# Migrate existing registry to new format +./migrate-registry.sh + +# The script will: +# - Detect old format automatically +# - Create a backup of the old registry +# - Convert to new format with proper tracking +# - Preserve all existing service information +``` + ### PM2 Services When using PM2 as the service provider: -```bash -# PM2-specific commands -pm2 list # List all PM2 processes -pm2 logs auth # View logs -pm2 monit # Real-time monitoring -pm2 restart auth # Restart service -pm2 delete auth # Remove service +* [PM2 CLI Documentation](https://pm2.io/docs/runtime/reference/pm2-cli/) -# Save PM2 configuration -pm2 save -pm2 startup # Auto-start on boot -``` +**Automatic PM2 Persistence**: The service manager automatically configures PM2 for persistence across reboots by: +- Running `pm2 startup` to set up the startup script +- Running `pm2 save` after each service creation/modification +- This ensures your services automatically start when the system reboots NOTE: pm2 cannot run tmux/screen sessions, but you can always use the `attach` command to connect to the service console because pm2 supports interactive mode. @@ -407,6 +460,12 @@ NOTE: pm2 cannot run tmux/screen sessions, but you can always use the `attach` c The startup scripts recognize several environment variables for configuration and runtime behavior: +#### Configuration Directory Variables + +- **`AC_SERVICE_CONFIG_DIR`**: Override the default configuration directory for services registry and configurations + - Default: `${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services` + - Used for storing service registry and run-engine configurations + #### Service Detection Variables - **`AC_LAUNCHED_BY_PM2`**: Set to `1` when launched by PM2 (automatically set by service-manager) @@ -551,4 +610,18 @@ npm install -g pm2 sudo npm install -g pm2 ``` +#### 7. Registry Out of Sync +```bash +# If the service registry shows services that don't actually exist +``` +**Solution**: Use registry sync or restore +```bash +# Check and restore missing services (also cleans up orphaned entries) +./service-manager.sh restore + +# If you have a very old registry format, migrate it +./migrate-registry.sh +``` + + diff --git a/apps/startup-scripts/src/migrate-registry.sh b/apps/startup-scripts/src/migrate-registry.sh new file mode 100755 index 000000000..c5898794f --- /dev/null +++ b/apps/startup-scripts/src/migrate-registry.sh @@ -0,0 +1,144 @@ +#!/usr/bin/env bash + +# One-time migration script for service registry +# Converts old format to new format + +set -euo pipefail # Strict error handling + +CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services}" +REGISTRY_FILE="$CONFIG_DIR/service_registry.json" +BACKUP_FILE="$CONFIG_DIR/service_registry.json.backup" + +# Colors +readonly YELLOW='\033[1;33m' +readonly GREEN='\033[0;32m' +readonly RED='\033[0;31m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' + +echo -e "${BLUE}AzerothCore Service Registry Migration Tool${NC}" +echo "==============================================" + +# Check dependencies +if ! command -v jq >/dev/null 2>&1; then + echo -e "${RED}Error: jq is required but not installed. Please install jq package.${NC}" + exit 1 +fi + +# Create config directory if it doesn't exist +mkdir -p "$CONFIG_DIR" + +# Check if registry exists +if [ ! -f "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No registry file found. Nothing to migrate.${NC}" + exit 0 +fi + +# Validate JSON format +if ! jq empty "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${RED}Error: Registry file contains invalid JSON.${NC}" + echo "Please check the file: $REGISTRY_FILE" + exit 1 +fi + +# Check if it's already new format +if jq -e 'type == "array" and (length == 0 or .[0] | has("bin_path"))' "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${GREEN}Registry is already in new format. No migration needed.${NC}" + exit 0 +fi + +# Check if it's old format +if ! jq -e 'type == "array" and (length == 0 or .[0] | has("config"))' "$REGISTRY_FILE" >/dev/null 2>&1; then + echo -e "${YELLOW}Registry format not recognized. Manual review needed.${NC}" + echo "Current registry content:" + cat "$REGISTRY_FILE" + exit 1 +fi + +echo -e "${YELLOW}Old format detected. Starting migration...${NC}" + +# Create backup +if ! cp "$REGISTRY_FILE" "$BACKUP_FILE"; then + echo -e "${RED}Error: Failed to create backup file.${NC}" + exit 1 +fi +echo -e "${BLUE}Backup created: $BACKUP_FILE${NC}" + +# Convert to new format +echo "[]" > "$REGISTRY_FILE.new" + +services_migrated=0 +while IFS= read -r service; do + if [ -n "$service" ] && [ "$service" != "null" ]; then + name=$(echo "$service" | jq -r '.name // ""') + provider=$(echo "$service" | jq -r '.provider // ""') + type=$(echo "$service" | jq -r '.type // ""') + config=$(echo "$service" | jq -r '.config // ""') + + # Validate required fields + if [ -z "$name" ] || [ -z "$provider" ] || [ -z "$type" ]; then + echo -e "${YELLOW}Skipping invalid service entry: $service${NC}" + continue + fi + + echo -e "${YELLOW}Migrating service: $name${NC}" + + # Create new format entry with all required fields + new_entry=$(jq -n \ + --arg name "$name" \ + --arg provider "$provider" \ + --arg type "$type" \ + --arg bin_path "unknown" \ + --arg args "" \ + --arg created "$(date -Iseconds)" \ + --arg status "migrated" \ + --arg systemd_type "--user" \ + --arg restart_policy "always" \ + --arg session_manager "none" \ + --arg gdb_enabled "0" \ + --arg pm2_opts "" \ + --arg server_config "" \ + --arg legacy_config "$config" \ + '{ + name: $name, + provider: $provider, + type: $type, + bin_path: $bin_path, + args: $args, + created: $created, + status: $status, + systemd_type: $systemd_type, + restart_policy: $restart_policy, + session_manager: $session_manager, + gdb_enabled: $gdb_enabled, + pm2_opts: $pm2_opts, + server_config: $server_config, + legacy_config: $legacy_config + }') + + # Add to new registry with error checking + if ! jq --argjson entry "$new_entry" '. += [$entry]' "$REGISTRY_FILE.new" > "$REGISTRY_FILE.new.tmp"; then + echo -e "${RED}Error: Failed to add service $name to new registry${NC}" + rm -f "$REGISTRY_FILE.new" "$REGISTRY_FILE.new.tmp" + exit 1 + fi + mv "$REGISTRY_FILE.new.tmp" "$REGISTRY_FILE.new" + + services_migrated=$((services_migrated + 1)) + fi +done < <(jq -c '.[]?' "$BACKUP_FILE" 2>/dev/null || echo "") + +# Replace old registry with new one +if ! mv "$REGISTRY_FILE.new" "$REGISTRY_FILE"; then + echo -e "${RED}Error: Failed to replace old registry with new one${NC}" + exit 1 +fi + +echo -e "${GREEN}Migration completed successfully!${NC}" +echo -e "${BLUE}Services migrated: $services_migrated${NC}" +echo -e "${BLUE}Use 'service-manager.sh restore' to review and update services.${NC}" +echo -e "${YELLOW}Note: Migrated services have bin_path='unknown' and need manual recreation.${NC}" +echo "" +echo -e "${BLUE}To recreate services, use commands like:${NC}" +echo " ./service-manager.sh create auth authserver --provider pm2 --bin-path /path/to/your/bin" +echo " ./service-manager.sh create world worldserver --provider systemd --bin-path /path/to/your/bin" diff --git a/apps/startup-scripts/src/service-manager.sh b/apps/startup-scripts/src/service-manager.sh index 980dfbb3a..34dc4c4d5 100755 --- a/apps/startup-scripts/src/service-manager.sh +++ b/apps/startup-scripts/src/service-manager.sh @@ -4,6 +4,8 @@ # A unified interface for managing AzerothCore services with PM2 or systemd # This script provides commands to create, update, delete, and manage server instances +set -euo pipefail # Strict error handling + # Script location CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -11,16 +13,16 @@ SCRIPT_DIR="$CURRENT_PATH" ROOT_DIR="$(cd "$CURRENT_PATH/../../.." && pwd)" -# Configuration directory -CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services" +# Configuration directory (can be overridden with AC_SERVICE_CONFIG_DIR) +CONFIG_DIR="${AC_SERVICE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services}" REGISTRY_FILE="$CONFIG_DIR/service_registry.json" # Colors for output -YELLOW='\033[1;33m' -GREEN='\033[0;32m' -RED='\033[0;31m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color +readonly YELLOW='\033[1;33m' +readonly GREEN='\033[0;32m' +readonly RED='\033[0;31m' +readonly BLUE='\033[0;34m' +readonly NC='\033[0m' # No Color # Create config directory if it doesn't exist mkdir -p "$CONFIG_DIR" @@ -38,6 +40,198 @@ check_dependencies() { } } +# Registry management functions +function add_service_to_registry() { + local service_name="$1" + local provider="$2" + local service_type="$3" + local bin_path="$4" + local args="$5" + local systemd_type="$6" + local restart_policy="$7" + local session_manager="$8" + local gdb_enabled="$9" + local pm2_opts="${10}" + local server_config="${11}" + + # Remove any existing entry with the same service name to avoid duplicates + local tmp_file + tmp_file=$(mktemp) + jq --arg name "$service_name" 'map(select(.name != $name))' "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + + # Add the new entry to the registry + tmp_file=$(mktemp) + jq --arg name "$service_name" \ + --arg provider "$provider" \ + --arg type "$service_type" \ + --arg bin_path "$bin_path" \ + --arg args "$args" \ + --arg created "$(date -Iseconds)" \ + --arg systemd_type "$systemd_type" \ + --arg restart_policy "$restart_policy" \ + --arg session_manager "$session_manager" \ + --arg gdb_enabled "$gdb_enabled" \ + --arg pm2_opts "$pm2_opts" \ + --arg server_config "$server_config" \ + '. += [{"name": $name, "provider": $provider, "type": $type, "bin_path": $bin_path, "args": $args, "created": $created, "status": "active", "systemd_type": $systemd_type, "restart_policy": $restart_policy, "session_manager": $session_manager, "gdb_enabled": $gdb_enabled, "pm2_opts": $pm2_opts, "server_config": $server_config}]' \ + "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + + echo -e "${GREEN}Service '$service_name' added to registry${NC}" +} + +function remove_service_from_registry() { + local service_name="$1" + + if [ -f "$REGISTRY_FILE" ]; then + local tmp_file + tmp_file=$(mktemp) + jq --arg name "$service_name" \ + 'map(select(.name != $name))' \ + "$REGISTRY_FILE" > "$tmp_file" && mv "$tmp_file" "$REGISTRY_FILE" + echo -e "${GREEN}Service '$service_name' removed from registry${NC}" + fi +} + +function restore_missing_services() { + echo -e "${BLUE}Checking for missing services...${NC}" + + if [ ! -f "$REGISTRY_FILE" ] || [ ! -s "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No services registry found or empty${NC}" + return 0 + fi + + local missing_services=() + local services_count + services_count=$(jq length "$REGISTRY_FILE") + + if [ "$services_count" -eq 0 ]; then + echo -e "${YELLOW}No services registered${NC}" + return 0 + fi + + echo -e "${BLUE}Found $services_count registered services. Checking status...${NC}" + + # Check each service + for i in $(seq 0 $((services_count-1))); do + local service=$(jq -r ".[$i]" "$REGISTRY_FILE") + local name=$(echo "$service" | jq -r '.name') + local provider=$(echo "$service" | jq -r '.provider') + local service_type=$(echo "$service" | jq -r '.type') + local bin_path=$(echo "$service" | jq -r '.bin_path // "unknown"') + local args=$(echo "$service" | jq -r '.args // ""') + local status=$(echo "$service" | jq -r '.status // "active"') + local systemd_type=$(echo "$service" | jq -r '.systemd_type // "--user"') + local restart_policy=$(echo "$service" | jq -r '.restart_policy // "always"') + local session_manager=$(echo "$service" | jq -r '.session_manager // "none"') + local gdb_enabled=$(echo "$service" | jq -r '.gdb_enabled // "0"') + local pm2_opts=$(echo "$service" | jq -r '.pm2_opts // ""') + local server_config=$(echo "$service" | jq -r '.server_config // ""') + + local service_exists=false + + if [ "$provider" = "pm2" ]; then + if pm2 describe "$name" >/dev/null 2>&1; then + service_exists=true + fi + elif [ "$provider" = "systemd" ]; then + local user_unit="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/$name.service" + local system_unit="/etc/systemd/system/$name.service" + if [ -f "$user_unit" ] || [ -f "$system_unit" ]; then + # Unit file present, you can also check if it is active + service_exists=true + else + # Unit file missing: service needs to be recreated! + service_exists=false + fi + fi + + if [ "$service_exists" = false ]; then + missing_services+=("$i") + echo -e "${YELLOW}Missing service: $name ($provider)${NC}" + else + echo -e "${GREEN}✓ Service $name ($provider) exists${NC}" + fi + done + + # Handle missing services + if [ ${#missing_services[@]} -eq 0 ]; then + echo -e "${GREEN}All registered services are present${NC}" + return 0 + fi + + echo -e "${YELLOW}Found ${#missing_services[@]} missing services${NC}" + + for index in "${missing_services[@]}"; do + local service=$(jq -r ".[$index]" "$REGISTRY_FILE") + local name=$(echo "$service" | jq -r '.name') + local provider=$(echo "$service" | jq -r '.provider') + local service_type=$(echo "$service" | jq -r '.type') + local bin_path=$(echo "$service" | jq -r '.bin_path') + local args=$(echo "$service" | jq -r '.args') + local systemd_type=$(echo "$service" | jq -r '.systemd_type // "--user"') + local restart_policy=$(echo "$service" | jq -r '.restart_policy // "always"') + local session_manager=$(echo "$service" | jq -r '.session_manager // "none"') + local gdb_enabled=$(echo "$service" | jq -r '.gdb_enabled // "0"') + local pm2_opts=$(echo "$service" | jq -r '.pm2_opts // ""') + local server_config=$(echo "$service" | jq -r '.server_config // ""') + + echo "" + echo -e "${YELLOW}Service '$name' ($provider) is missing${NC}" + echo " Type: $service_type" + echo " Status: $status" + + if [ "$bin_path" = "unknown" ] || [ "$bin_path" = "null" ] || [ "$status" = "migrated" ]; then + echo " Binary: " + echo " Args: " + echo "" + echo -e "${YELLOW}This service needs to be recreated manually:${NC}" + echo " $0 create $service_type $name --provider $provider --bin-path /path/to/your/bin" + else + echo " Binary: $bin_path" + echo " Args: $args" + fi + echo "" + + read -p "Do you want to (r)ecreate, (d)elete from registry, or (s)kip? [r/d/s]: " choice + + case "$choice" in + r|R|recreate) + if [ "$bin_path" = "unknown" ] || [ "$status" = "migrated" ]; then + echo -e "${YELLOW}Please recreate manually with full create command${NC}" + read -p "Remove this entry from registry? [y/n]: " remove_entry + if [[ "$remove_entry" =~ ^[Yy]$ ]]; then + remove_service_from_registry "$name" + fi + else + echo -e "${BLUE}Recreating service '$name'...${NC}" + if [ "$provider" = "pm2" ]; then + if [ "$args" != "null" ] && [ -n "$args" ]; then + pm2_create_service "$name" "$bin_path $args" "$restart_policy" $pm2_opts + else + pm2_create_service "$name" "$bin_path" "$restart_policy" $pm2_opts + fi + elif [ "$provider" = "systemd" ]; then + echo -e "${BLUE}Attempting to recreate systemd service '$name' automatically...${NC}" + if systemd_create_service "$name" "$bin_path $args" "$restart_policy" "$systemd_type" "$session_manager" "$gdb_enabled" "$server_config"; then + echo -e "${GREEN}Systemd service '$name' recreated successfully${NC}" + else + echo -e "${RED}Failed to recreate systemd service '$name'. Please recreate manually.${NC}" + echo " $0 create $name $service_type --provider systemd --bin-path $bin_path" + fi + fi + fi + ;; + d|D|delete) + echo -e "${BLUE}Removing '$name' from registry...${NC}" + remove_service_from_registry "$name" + ;; + s|S|skip|*) + echo -e "${BLUE}Skipping '$name'${NC}" + ;; + esac + done +} + # Check if PM2 is installed check_pm2() { if ! command -v pm2 >/dev/null 2>&1; then @@ -81,6 +275,7 @@ function print_help() { echo " $base_name update [options]" echo " $base_name delete " echo " $base_name list [provider]" + echo " $base_name restore" echo " $base_name start|stop|restart|status " echo " $base_name logs [--follow]" echo " $base_name attach " @@ -139,6 +334,9 @@ function print_help() { echo " $base_name attach worldserver-realm1" echo " $base_name list pm2" echo "" + echo " # Restore missing services from registry" + echo " $base_name restore" + echo "" echo "Notes:" echo " - Configuration editing modifies run-engine settings (GDB, session manager, etc.)" echo " - Use --server-config for the actual server configuration file" @@ -150,26 +348,13 @@ function print_help() { echo " - attach command automatically detects the configured session manager and connects appropriately" echo " - attach always provides interactive access to the server console" echo " - Use 'logs' command to view service logs without interaction" + echo " - restore command checks registry and helps recreate missing services" + echo "" + echo "Environment Variables:" + echo " AC_SERVICE_CONFIG_DIR - Override default config directory for services registry" } -function register_service() { - local service_name="$1" - local provider="$2" - local service_type="$3" - local config_file="$CONFIG_DIR/$service_name.conf" - - # Add to registry - local tmp_file=$(mktemp) - jq --arg name "$service_name" \ - --arg provider "$provider" \ - --arg type "$service_type" \ - --arg config "$config_file" \ - '. += [{"name": $name, "provider": $provider, "type": $type, "config": $config}]' \ - "$REGISTRY_FILE" > "$tmp_file" - mv "$tmp_file" "$REGISTRY_FILE" - - echo -e "${GREEN}Service $service_name registered successfully${NC}" -} + function validate_service_exists() { local service_name="$1" @@ -210,47 +395,42 @@ function validate_service_exists() { function sync_registry() { echo -e "${YELLOW}Syncing service registry with actual services...${NC}" - local services=$(jq -c '.[]' "$REGISTRY_FILE") - local tmp_file=$(mktemp) + if [ ! -f "$REGISTRY_FILE" ] || [ ! -s "$REGISTRY_FILE" ]; then + echo -e "${YELLOW}No services registry found or empty${NC}" + return 0 + fi - # Initialize with empty array + local services_count=$(jq length "$REGISTRY_FILE") + if [ "$services_count" -eq 0 ]; then + echo -e "${YELLOW}No services registered${NC}" + return 0 + fi + + local tmp_file=$(mktemp) echo "[]" > "$tmp_file" # Check each service in registry - while read -r service_info; do - if [ -n "$service_info" ]; then - local name=$(echo "$service_info" | jq -r '.name') - local provider=$(echo "$service_info" | jq -r '.provider') - - if validate_service_exists "$name" "$provider"; then - # Service exists, add it to the new registry - jq --argjson service "$service_info" '. += [$service]' "$tmp_file" > "$tmp_file.new" - mv "$tmp_file.new" "$tmp_file" - else - echo -e "${YELLOW}Service '$name' no longer exists. Removing from registry.${NC}" - # Don't add to new registry - fi + for i in $(seq 0 $((services_count-1))); do + local service=$(jq -r ".[$i]" "$REGISTRY_FILE") + local name=$(echo "$service" | jq -r '.name') + local provider=$(echo "$service" | jq -r '.provider') + + if validate_service_exists "$name" "$provider"; then + # Service exists, add it to the new registry + jq --argjson service "$service" '. += [$service]' "$tmp_file" > "$tmp_file.new" + mv "$tmp_file.new" "$tmp_file" + else + echo -e "${YELLOW}Service '$name' no longer exists. Removing from registry.${NC}" + # Don't add to new registry fi - done <<< "$services" + done # Replace registry with synced version mv "$tmp_file" "$REGISTRY_FILE" echo -e "${GREEN}Registry synchronized.${NC}" } -function unregister_service() { - local service_name="$1" - - # Remove from registry - local tmp_file=$(mktemp) - jq --arg name "$service_name" '. | map(select(.name != $name))' "$REGISTRY_FILE" > "$tmp_file" - mv "$tmp_file" "$REGISTRY_FILE" - - # Remove configuration file - rm -f "$CONFIG_DIR/$service_name.conf" - - echo -e "${GREEN}Service $service_name unregistered${NC}" -} + function get_service_info() { local service_name="$1" @@ -317,6 +497,15 @@ function pm2_create_service() { if eval "$pm2_cmd"; then echo -e "${GREEN}PM2 service '$service_name' created successfully${NC}" pm2 save + + # Setup PM2 startup for persistence across reboots + echo -e "${BLUE}Configuring PM2 startup for persistence...${NC}" + pm2 startup --auto >/dev/null 2>&1 || true + + # Add to registry (extract command and args from the full command) + local clean_command="$command$additional_args" + add_service_to_registry "$service_name" "pm2" "executable" "$command" "$additional_args" "" "$restart_policy" "none" "0" "$max_memory $max_restarts" "" + return 0 else echo -e "${RED}Failed to create PM2 service '$service_name'${NC}" @@ -334,8 +523,8 @@ function pm2_remove_service() { # Stop the service if it's running if pm2 describe "$service_name" >/dev/null 2>&1; then - pm2 stop "$service_name" 2>/dev/null || true - pm2 delete "$service_name" 2>/dev/null + pm2 stop "$service_name" 2>&1 || true + pm2 delete "$service_name" 2>&1 || true # Wait for PM2 to process the stop/delete command with timeout local timeout=10 @@ -357,8 +546,13 @@ function pm2_remove_service() { pm2 save echo -e "${GREEN}PM2 service '$service_name' stopped and removed${NC}" + + # Remove from registry + remove_service_from_registry "$service_name" else echo -e "${YELLOW}PM2 service '$service_name' not found or already removed${NC}" + # Still try to remove from registry in case it's orphaned + remove_service_from_registry "$service_name" fi return 0 @@ -391,6 +585,7 @@ function pm2_service_logs() { # Systemd service management functions function get_systemd_dir() { local type="$1" + if [ "$type" = "--system" ]; then echo "/etc/systemd/system" else @@ -403,17 +598,32 @@ function systemd_create_service() { local command="$2" local restart_policy="$3" local systemd_type="--user" + local bin_path="" + local gdb_enabled="0" + local server_config="" shift 3 check_systemd || return 1 - # Parse systemd type + # Parse systemd type and extract additional parameters while [[ $# -gt 0 ]]; do case "$1" in --system|--user) systemd_type="$1" shift ;; + --bin-path) + bin_path="$2" + shift 2 + ;; + --gdb-enabled) + gdb_enabled="$2" + shift 2 + ;; + --server-config) + server_config="$2" + shift 2 + ;; *) command+=" $1" shift @@ -421,6 +631,18 @@ function systemd_create_service() { esac done + # If bin_path is not provided, try to extract from command + if [ -z "$bin_path" ]; then + # Try to extract bin path from run-engine command + if [[ "$command" =~ run-engine[[:space:]]+start[[:space:]]+([^[:space:]]+) ]]; then + local binary_path="${BASH_REMATCH[1]}" + bin_path="$(dirname "$binary_path")" + else + # Fallback to current directory + bin_path="$(pwd)" + fi + fi + local systemd_dir=$(get_systemd_dir "$systemd_type") local service_file="$systemd_dir/$service_name.service" @@ -457,6 +679,11 @@ function systemd_create_service() { # Create service file echo -e "${YELLOW}Creating systemd service: $service_name${NC}" + # Ensure bin_path is absolute + if [[ ! "$bin_path" = /* ]]; then + bin_path="$(realpath "$bin_path")" + fi + if [ "$systemd_type" = "--system" ]; then # System service template (with User directive) cat > "$service_file" << EOF @@ -471,7 +698,7 @@ Restart=$restart_policy RestartSec=3 User=$(whoami) Group=$(id -gn) -WorkingDirectory=$(realpath "$bin_path") +WorkingDirectory=$bin_path StandardOutput=journal+console StandardError=journal+console @@ -490,7 +717,7 @@ Type=${service_type} ExecStart=$command Restart=$restart_policy RestartSec=3 -WorkingDirectory=$(realpath "$bin_path") +WorkingDirectory=$bin_path StandardOutput=journal+console StandardError=journal+console @@ -498,10 +725,6 @@ StandardError=journal+console WantedBy=default.target EOF fi - - if [ "$systemd_type" = "--system" ]; then - sed -i 's/WantedBy=default.target/WantedBy=multi-user.target/' "$service_file" - fi # Reload systemd and enable service if [ "$systemd_type" = "--system" ]; then @@ -513,6 +736,10 @@ EOF fi echo -e "${GREEN}Systemd service '$service_name' created successfully${NC}" + + # Add to registry + add_service_to_registry "$service_name" "systemd" "service" "$command" "" "$systemd_type" "$restart_policy" "$session_manager" "$gdb_enabled" "" "$server_config" + return 0 } @@ -572,6 +799,10 @@ function systemd_remove_service() { if [ "$removal_failed" = "true" ]; then echo -e "${YELLOW}Note: Service may still be running but configuration was removed${NC}" fi + + # Remove from registry + remove_service_from_registry "$service_name" + return 0 else echo -e "${RED}Failed to remove systemd service file '$service_file'${NC}" @@ -659,7 +890,7 @@ function create_service() { # Default values for run-engine configuration local provider="auto" - local bin_path="$BINPATH/bin" # get from config or environment + local bin_path="${BINPATH:-$ROOT_DIR/bin}" # get from config or environment local server_config="" local session_manager="none" local gdb_enabled="0" @@ -839,8 +1070,6 @@ EOF # Check if service creation was successful if [ "$service_creation_success" = "true" ]; then - # Register the service - register_service "$service_name" "$provider" "$service_type" echo -e "${GREEN}Service '$service_name' created successfully${NC}" echo -e "${BLUE}Run-engine config: $run_engine_config${NC}" @@ -880,14 +1109,20 @@ function update_service() { # Extract service information local provider=$(echo "$service_info" | jq -r '.provider') local service_type=$(echo "$service_info" | jq -r '.type') - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load current configuration + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" + return 1 + fi source "$config_file" # Load current run-engine configuration if [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then source "$RUN_ENGINE_CONFIG_FILE" + else + echo -e "${YELLOW}Warning: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" fi # Parse options to update @@ -1020,11 +1255,13 @@ function delete_service() { # Extract provider and config local provider=$(echo "$service_info" | jq -r '.provider') - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file if [ -f "$config_file" ]; then source "$config_file" + else + echo -e "${YELLOW}Warning: Service configuration file not found: $config_file${NC}" fi echo -e "${YELLOW}Deleting service '$service_name' (provider: $provider)...${NC}" @@ -1048,8 +1285,9 @@ function delete_service() { echo -e "${GREEN}Removed run-engine config: $RUN_ENGINE_CONFIG_FILE${NC}" fi - # Unregister service - unregister_service "$service_name" + # Remove configuration file + rm -f "$config_file" + echo -e "${GREEN}Service '$service_name' deleted successfully${NC}" else echo -e "${RED}Failed to remove service '$service_name' from $provider${NC}" @@ -1166,7 +1404,7 @@ function edit_config() { fi # Get configuration file path - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file source "$config_file" @@ -1191,7 +1429,7 @@ function attach_to_service() { # Extract provider local provider=$(echo "$service_info" | jq -r '.provider') - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" # Load configuration to get run-engine config file if [ ! -f "$config_file" ]; then @@ -1206,6 +1444,11 @@ function attach_to_service() { echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" return 1 fi + + if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then + echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" + return 1 + fi source "$RUN_ENGINE_CONFIG_FILE" @@ -1264,9 +1507,22 @@ function attach_interactive_shell() { # For systemd without session manager, show helpful message local service_info=$(get_service_info "$service_name") - local config_file=$(echo "$service_info" | jq -r '.config') + local config_file="$CONFIG_DIR/$service_name.conf" + + # Check if config file exists before sourcing + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" + return 1 + fi source "$config_file" + + # Check if RUN_ENGINE_CONFIG_FILE exists before sourcing + if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then + echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" + return 1 + fi + source "$RUN_ENGINE_CONFIG_FILE" echo -e "${RED}Error: Cannot attach to systemd service '$service_name'${NC}" @@ -1375,6 +1631,9 @@ case "${1:-help}" in list) list_services "$2" ;; + restore) + restore_missing_services + ;; start|stop|restart|status) if [ $# -lt 2 ]; then echo -e "${RED}Error: Service name required for $1 command${NC}" From 2ad40a4d23f82054deb9582d58cf7b69489eb697 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Tue, 26 Aug 2025 20:58:41 -0400 Subject: [PATCH 051/155] fix(Core/GroupHandler): Adjust maximum value for rolls. (#22686) --- src/server/apps/worldserver/worldserver.conf.dist | 10 ++++++++++ src/server/game/Handlers/GroupHandler.cpp | 4 +--- src/server/game/World/WorldConfig.cpp | 2 ++ src/server/game/World/WorldConfig.h | 1 + 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 9b36fe680..9618e31a7 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -3141,6 +3141,16 @@ LeaveGroupOnLogout.Enabled = 0 Group.Raid.LevelRestriction = 10 +# +# Group.RandomRollMaximum +# +# The maximum value for use with the client '/roll' command. +# Blizzlike and maximum value is 1000000. (Based on Classic and 3.3.5a client testing respectively) +# Default: 1000000 +# + +Group.RandomRollMaximum = 1000000 + # ################################################################################################### diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index 85a9f0888..c8f14779b 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -543,10 +543,8 @@ void WorldSession::HandleRandomRollOpcode(WorldPackets::Misc::RandomRollClient& maximum = packet.Max; /** error handling **/ - if (minimum > maximum || maximum > 10000) // < 32768 for urand call - { + if (minimum > maximum || maximum > sWorld->getIntConfig(CONFIG_RANDOM_ROLL_MAXIMUM)) return; - } GetPlayer()->DoRandomRoll(minimum, maximum); } diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp index 7a0b1db40..c38b36ff0 100644 --- a/src/server/game/World/WorldConfig.cpp +++ b/src/server/game/World/WorldConfig.cpp @@ -492,6 +492,8 @@ void WorldConfig::BuildConfigCache() SetConfigValue(CONFIG_LEAVE_GROUP_ON_LOGOUT, "LeaveGroupOnLogout.Enabled", false); + SetConfigValue(CONFIG_RANDOM_ROLL_MAXIMUM, "Group.RandomRollMaximum", 1000000); + SetConfigValue(CONFIG_QUEST_POI_ENABLED, "QuestPOI.Enabled", true); SetConfigValue(CONFIG_CHANGE_FACTION_MAX_MONEY, "ChangeFaction.MaxMoney", 0); diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h index 3776fcac9..475bed337 100644 --- a/src/server/game/World/WorldConfig.h +++ b/src/server/game/World/WorldConfig.h @@ -129,6 +129,7 @@ enum ServerConfigs CONFIG_ALLOW_JOIN_BG_AND_LFG, CONFIG_MISS_CHANCE_MULTIPLIER_ONLY_FOR_PLAYERS, CONFIG_LEAVE_GROUP_ON_LOGOUT, + CONFIG_RANDOM_ROLL_MAXIMUM, CONFIG_QUEST_POI_ENABLED, CONFIG_VMAP_BLIZZLIKE_PVP_LOS, CONFIG_VMAP_BLIZZLIKE_LOS_OPEN_WORLD, From 96ad9f88ba48ff0ddbdf39e9a0073bc025ea2638 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Tue, 26 Aug 2025 23:02:04 -0400 Subject: [PATCH 052/155] fix(DB/Item): Add some stat values to Death Knight Brutal Gladiator set. (#22725) --- data/sql/updates/pending_db_world/dk-brutal-stats.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/sql/updates/pending_db_world/dk-brutal-stats.sql diff --git a/data/sql/updates/pending_db_world/dk-brutal-stats.sql b/data/sql/updates/pending_db_world/dk-brutal-stats.sql new file mode 100644 index 000000000..3c2107fa8 --- /dev/null +++ b/data/sql/updates/pending_db_world/dk-brutal-stats.sql @@ -0,0 +1,5 @@ +UPDATE `item_template` SET `armor` = 1983, `MaxDurability` = 165 WHERE `entry` = 40440; +UPDATE `item_template` SET `armor` = 1239, `MaxDurability` = 55 WHERE `entry` = 40441; +UPDATE `item_template` SET `armor` = 1611, `MaxDurability` = 100 WHERE `entry` = 40442; +UPDATE `item_template` SET `armor` = 1735, `MaxDurability` = 120 WHERE `entry` = 40443; +UPDATE `item_template` SET `armor` = 1487, `MaxDurability` = 100 WHERE `entry` = 40444; From a8284b71de9adf5f6da816cf846f6f2a9477a46e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Aug 2025 03:03:06 +0000 Subject: [PATCH 053/155] chore(DB): import pending files Referenced commit(s): 96ad9f88ba48ff0ddbdf39e9a0073bc025ea2638 --- .../dk-brutal-stats.sql => db_world/2025_08_27_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/dk-brutal-stats.sql => db_world/2025_08_27_00.sql} (90%) diff --git a/data/sql/updates/pending_db_world/dk-brutal-stats.sql b/data/sql/updates/db_world/2025_08_27_00.sql similarity index 90% rename from data/sql/updates/pending_db_world/dk-brutal-stats.sql rename to data/sql/updates/db_world/2025_08_27_00.sql index 3c2107fa8..13cac6c78 100644 --- a/data/sql/updates/pending_db_world/dk-brutal-stats.sql +++ b/data/sql/updates/db_world/2025_08_27_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_25_02 -> 2025_08_27_00 UPDATE `item_template` SET `armor` = 1983, `MaxDurability` = 165 WHERE `entry` = 40440; UPDATE `item_template` SET `armor` = 1239, `MaxDurability` = 55 WHERE `entry` = 40441; UPDATE `item_template` SET `armor` = 1611, `MaxDurability` = 100 WHERE `entry` = 40442; From 0fc05ed4d25ca17604a22146979f10e32fdd854b Mon Sep 17 00:00:00 2001 From: Quartzi <34374881+realQuartzi@users.noreply.github.com> Date: Wed, 27 Aug 2025 09:32:02 +0200 Subject: [PATCH 054/155] feat(Core/Scripting): Add OnPlayerGiveReputation script hook (#21869) --- src/server/game/Entities/Player/Player.cpp | 7 +++++++ src/server/game/Entities/Player/Player.h | 11 ----------- .../game/Scripting/ScriptDefines/PlayerScript.cpp | 5 +++++ .../game/Scripting/ScriptDefines/PlayerScript.h | 4 ++++ src/server/game/Scripting/ScriptMgr.h | 1 + src/server/shared/SharedDefines.h | 11 +++++++++++ 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 6d5e8b24c..00cde81a6 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -6008,6 +6008,7 @@ void Player::RewardReputation(Unit* victim) if (Rep->RepFaction1 && (!Rep->TeamDependent || teamId == TEAM_ALLIANCE)) { float donerep1 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevel(), static_cast(Rep->RepValue1), ChampioningFaction ? ChampioningFaction : Rep->RepFaction1); + sScriptMgr->OnPlayerGiveReputation(this, Rep->RepFaction1, donerep1, REPUTATION_SOURCE_KILL); FactionEntry const* factionEntry1 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction1); if (factionEntry1) @@ -6019,6 +6020,7 @@ void Player::RewardReputation(Unit* victim) if (Rep->RepFaction2 && (!Rep->TeamDependent || teamId == TEAM_HORDE)) { float donerep2 = CalculateReputationGain(REPUTATION_SOURCE_KILL, victim->GetLevel(), static_cast(Rep->RepValue2), ChampioningFaction ? ChampioningFaction : Rep->RepFaction2); + sScriptMgr->OnPlayerGiveReputation(this, Rep->RepFaction2, donerep2, REPUTATION_SOURCE_KILL); FactionEntry const* factionEntry2 = sFactionStore.LookupEntry(ChampioningFaction ? ChampioningFaction : Rep->RepFaction2); if (factionEntry2) @@ -6058,22 +6060,27 @@ void Player::RewardReputation(Quest const* quest) if (quest->IsDaily()) { rep = CalculateReputationGain(REPUTATION_SOURCE_DAILY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_DAILY_QUEST); } else if (quest->IsWeekly()) { rep = CalculateReputationGain(REPUTATION_SOURCE_WEEKLY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_WEEKLY_QUEST); } else if (quest->IsMonthly()) { rep = CalculateReputationGain(REPUTATION_SOURCE_MONTHLY_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_MONTHLY_QUEST); } else if (quest->IsRepeatable()) { rep = CalculateReputationGain(REPUTATION_SOURCE_REPEATABLE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_REPEATABLE_QUEST); } else { rep = CalculateReputationGain(REPUTATION_SOURCE_QUEST, GetQuestLevel(quest), rep, quest->RewardFactionId[i], false); + sScriptMgr->OnPlayerGiveReputation(this, quest->RewardFactionId[i], rep, REPUTATION_SOURCE_QUEST); } if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(quest->RewardFactionId[i])) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index ae6b6767c..17e18c0ef 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -235,17 +235,6 @@ enum ActionButtonType ACTION_BUTTON_ITEM = 0x80 }; -enum ReputationSource -{ - REPUTATION_SOURCE_KILL, - REPUTATION_SOURCE_QUEST, - REPUTATION_SOURCE_DAILY_QUEST, - REPUTATION_SOURCE_WEEKLY_QUEST, - REPUTATION_SOURCE_MONTHLY_QUEST, - REPUTATION_SOURCE_REPEATABLE_QUEST, - REPUTATION_SOURCE_SPELL -}; - enum QuestSound { QUEST_SOUND_FAILURE = 847 diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index 57978f7a5..26ba8cba5 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -139,6 +139,11 @@ void ScriptMgr::OnPlayerReputationRankChange(Player* player, uint32 factionID, R CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_REPUTATION_RANK_CHANGE, script->OnPlayerReputationRankChange(player, factionID, newRank, oldRank, increased)); } +void ScriptMgr::OnPlayerGiveReputation(Player* player, int32 factionID, float& amount, ReputationSource repSource) +{ + CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_GIVE_REPUTATION, script->OnPlayerGiveReputation(player, factionID, amount, repSource)); +} + void ScriptMgr::OnPlayerLearnSpell(Player* player, uint32 spellID) { CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_LEARN_SPELL, script->OnPlayerLearnSpell(player, spellID)); diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h index 692e5c4cc..2faa19539 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h @@ -210,6 +210,7 @@ enum PlayerHook PLAYERHOOK_CAN_RESURRECT, PLAYERHOOK_ON_CAN_GIVE_LEVEL, PLAYERHOOK_ON_SEND_LIST_INVENTORY, + PLAYERHOOK_ON_GIVE_REPUTATION, PLAYERHOOK_END }; @@ -283,6 +284,9 @@ public: // Called when a player's reputation rank changes (before it is actually changed) virtual void OnPlayerReputationRankChange(Player* /*player*/, uint32 /*factionID*/, ReputationRank /*newRank*/, ReputationRank /*olRank*/, bool /*increased*/) { } + // Called when a player gains Reputation (before anything is given) + virtual void OnPlayerGiveReputation(Player* /*player*/, int32 /*factionID*/, float& /*amount*/, ReputationSource /*repSource*/) { } + // Called when a player learned new spell virtual void OnPlayerLearnSpell(Player* /*player*/, uint32 /*spellID*/) {} diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 7a048e1a1..e15ac9050 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -313,6 +313,7 @@ public: /* PlayerScript */ void OnPlayerGiveXP(Player* player, uint32& amount, Unit* victim, uint8 xpSource); bool OnPlayerReputationChange(Player* player, uint32 factionID, int32& standing, bool incremental); void OnPlayerReputationRankChange(Player* player, uint32 factionID, ReputationRank newRank, ReputationRank oldRank, bool increased); + void OnPlayerGiveReputation(Player* player, int32 factionID, float& amount, ReputationSource repSource); void OnPlayerLearnSpell(Player* player, uint32 spellID); void OnPlayerForgotSpell(Player* player, uint32 spellID); void OnPlayerDuelRequest(Player* target, Player* challenger); diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index e114c997d..fb95d6cea 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -187,6 +187,17 @@ enum ReputationRank : uint8 REP_EXALTED = 7 }; +enum ReputationSource +{ + REPUTATION_SOURCE_KILL, + REPUTATION_SOURCE_QUEST, + REPUTATION_SOURCE_DAILY_QUEST, + REPUTATION_SOURCE_WEEKLY_QUEST, + REPUTATION_SOURCE_MONTHLY_QUEST, + REPUTATION_SOURCE_REPEATABLE_QUEST, + REPUTATION_SOURCE_SPELL +}; + enum FactionTemplates { FACTION_NONE = 0, From cdceb775a03ecfec8117d922108b21817aff1394 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Wed, 27 Aug 2025 05:28:18 -0400 Subject: [PATCH 055/155] fix(Scripts/Karazhan): Implement Tenris Mirkblood. (#22551) Co-authored-by: amed80 <8395873+amed80@users.noreply.github.com> --- .../pending_db_world/tenris-mirkblood.sql | 82 ++++ .../Karazhan/boss_tenris_mirkblood.cpp | 375 ++++++++++++++++++ .../Karazhan/instance_karazhan.cpp | 1 + .../EasternKingdoms/Karazhan/karazhan.h | 8 +- .../eastern_kingdoms_script_loader.cpp | 2 + 5 files changed, 466 insertions(+), 2 deletions(-) create mode 100644 data/sql/updates/pending_db_world/tenris-mirkblood.sql create mode 100644 src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp diff --git a/data/sql/updates/pending_db_world/tenris-mirkblood.sql b/data/sql/updates/pending_db_world/tenris-mirkblood.sql new file mode 100644 index 000000000..67a76c31a --- /dev/null +++ b/data/sql/updates/pending_db_world/tenris-mirkblood.sql @@ -0,0 +1,82 @@ +SET @SAY_APPROACH = 0, +@SAY_AGGRO = 1, +@SAY_SUMMON = 2, +@GUID = 12748; + +DELETE FROM `areatrigger_scripts` WHERE `entry` IN (5014, 5015); +INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES +(5014, 'at_karazhan_mirkblood_approach'), +(5015, 'at_karazhan_mirkblood_entrance'); + +UPDATE `creature_template` SET `minlevel` = 73, `maxlevel` = 73, `speed_run` = 1.85714285714, `ScriptName` = 'boss_tenris_mirkblood' WHERE `entry` = 28194; +UPDATE `creature_template` SET `speed_walk` = 0.4, `speed_run` = 0.14285714285, `ScriptName` = 'npc_sanguine_spirit' WHERE `entry` = 28232; +UPDATE `creature_template` SET `unit_flags` = 33554432, `AIName` = 'SmartAI' WHERE `entry` = 28485; +UPDATE `creature_template` SET `unit_flags` = 33555200 WHERE `entry` = 28493; + +UPDATE `creature_model_info` SET `BoundingRadius` = 0.200000002980232238, `CombatReach` = 0.400000005960464477 WHERE `DisplayID` = 25296; +UPDATE `creature_model_info` SET `BoundingRadius` = 0.465000003576278686, `CombatReach` = 1.5 WHERE `DisplayID` = 25541; + +DELETE FROM `creature_text` WHERE `CreatureID` = 28194; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(28194, @SAY_APPROACH, 0, 'I smell... $r. Delicious!', 14, 0, 100, 0, 0, 0, 27780, 0, 'Prince Tenris Mirkblood - SAY_APPROACH'), +(28194, @SAY_AGGRO, 0, 'I shall consume you!', 14, 0, 100, 0, 0, 0, 27781, 0, 'Prince Tenris Mirkblood - SAY_AGGRO'), +(28194, @SAY_SUMMON, 0, 'Drink, mortals! Taste my blood! Taste your death!', 12, 0, 100, 0, 0, 0, 27712, 0, 'Prince Tenris Mirkblood - SAY_SUMMON'); + +UPDATE `gameobject_template` SET `ScriptName` = 'go_blood_drenched_door' WHERE `entry` = 181032; + +DELETE FROM `spell_script_names` WHERE `spell_id` IN (50883, 50925, 51013); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(50883, 'spell_mirkblood_blood_mirror_target_picker'), +(50925, 'spell_mirkblood_dash_gash_return_to_tank_pre_spell'), +(51013, 'spell_mirkblood_exsanguinate'); + +DELETE FROM `spell_linked_spell` WHERE `spell_trigger` = -50845 AND `spell_effect` = -50844; +INSERT INTO `spell_linked_spell` (`spell_trigger`, `spell_effect`, `type`, `comment`) VALUES +(-50845, -50844, 0, 'Tenris Mirkblood Blood Mirror'); + +DELETE FROM `creature_template_addon` WHERE `entry` IN (28232, 28485, 28493); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(28232, 0, 0, 0, 0, 0, 0, '51282'), +(28485, 0, 0, 0, 0, 0, 0, '30987'), +(28493, 0, 0, 0, 0, 383, 0, ''); + +DELETE FROM `creature_template_movement` WHERE `CreatureId` IN (28485, 28493); +INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES +(28485, 0, 0, 1, 0, 0, 0, NULL), +(28493, 0, 0, 1, 0, 0, 0, NULL); + +DELETE FROM `creature` WHERE `guid` BETWEEN @GUID+0 AND @GUID+9 AND `id1` IN (28485, 28493); +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +(@GUID+0, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11087.619, -1996.4193, 82.59072, 0.453785598278045654, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+1, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11104.703, -1973.5052, 82.73294, 0.05235987901687622, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+2, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11084.556, -1981.4388, 82.4658, 0.575958669185638427, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+3, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11091.643, -1961.8134, 82.77006, 0.104719758033752441, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+4, 28485, 0, 0, 532, 0, 0, 1, 1, 0, -11097.971, -1982.734, 82.39082, 0.418879032135009765, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+5, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11097.721, -1982.62, 77.43985, 5.113814830780029296, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+6, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11104.523, -1973.4592, 78.07421, 1.396263360977172851, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+7, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11084.696, -1981.4202, 77.87848, 4.904375076293945312, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+8, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11091.594, -1962.1276, 78.054115, 2.146754980087280273, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL), +(@GUID+9, 28493, 0, 0, 532, 0, 0, 1, 1, 0, -11087.617, -1996.2291, 77.72322, 0.436332315206527709, 300, 0, 0, 4050, 0, 0, 0, 0, 0, '', 49345, 2, NULL); + +DELETE FROM `game_event_creature` WHERE `eventEntry` = 120 AND `guid` BETWEEN @GUID+0 AND @GUID+9; +INSERT INTO `game_event_creature` (`eventEntry`, `guid`) VALUES +(120, @GUID+0), +(120, @GUID+1), +(120, @GUID+2), +(120, @GUID+3), +(120, @GUID+4), +(120, @GUID+5), +(120, @GUID+6), +(120, @GUID+7), +(120, @GUID+8), +(120, @GUID+9); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28485) AND (`source_type` = 0) AND (`id` IN (0)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28485, 0, 0, 0, 37, 0, 100, 0, 0, 0, 0, 0, 0, 11, 51773, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Blood Vat Bunny - On Initialize - Cast \'Scourge Invasion Blood Vat Bunny\''); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` = 51773 AND `ConditionTypeOrReference` = 31 AND `ConditionValue2` = 28485; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 51773, 0, 0, 31, 0, 3, 28485, 0, 0, 0, 0, '', 'Target must be unit Blood Vat Bunny'); + +UPDATE `spell_dbc` SET `Effect_1` = 28, `EffectMiscValue_1` = 28232, `EffectMiscValueB_1` = 64 WHERE `ID` = 50996; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp new file mode 100644 index 000000000..518a46055 --- /dev/null +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp @@ -0,0 +1,375 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "karazhan.h" +#include "AreaTriggerScript.h" +#include "CreatureScript.h" +#include "GameObjectAI.h" +#include "GameObjectScript.h" +#include "Player.h" +#include "ScriptedCreature.h" +#include "SpellInfo.h" +#include "SpellScript.h" +#include "SpellScriptLoader.h" +#include "UnitAI.h" + +enum Text +{ + SAY_APPROACH = 0, + SAY_AGGRO = 1, + SAY_SUMMON = 2 +}; + +enum Spells +{ + SPELL_BLOOD_MIRROR0 = 50844, + SPELL_BLOOD_MIRROR1 = 50845, + SPELL_BLOOD_MIRROR_TARGET_PICKER = 50883, + SPELL_BLOOD_MIRROR_TRANSITION_VISUAL = 50910, + SPELL_BLOOD_MIRROR_DAMAGE = 50846, + + SPELL_BLOOD_TAP = 51135, + + SPELL_BLOOD_SWOOP = 50922, + SPELL_DASH_GASH_PRE_SPELL = 50923, + SPELL_DASH_GASH_RETURN_TO_TANK = 50924, + // SPELL_DASH_GASH_RETURN_TO_TANK_PRE_SPELL = 50925, + // SPELL_DASH_GASH_RETURN_TO_TANK_PRE_SPELL_ROOT = 50932, + + SPELL_DESPAWN_SANGUINE_SPIRIT_VISUAL = 51214, + SPELL_DESPAWN_SANGUINE_SPIRITS = 51212, + SPELL_SANGUINE_SPIRIT_AURA = 50993, + SPELL_SANGUINE_SPIRIT_PRE_AURA = 51282, + SPELL_SANGUINE_SPIRIT_PRE_AURA2 = 51283, + SPELL_SUMMON_SANGUINE_SPIRIT0 = 50996, + SPELL_SUMMON_SANGUINE_SPIRIT1 = 50998, + // SPELL_SUMMON_SANGUINE_SPIRIT2 = 51204, + SPELL_SUMMON_SANGUINE_SPIRIT_MISSILE_BURST = 51208, + SPELL_SUMMON_SANGUINE_SPIRIT_SHORT_MISSILE_BURST = 51280, + SPELL_SUMMON_SANGUINE_SPIRIT_ON_KILL = 51205, + SPELL_EXSANGUINATE = 51013, + SPELL_DUMMY_NUKE_RANGE_SELF = 51106, +}; + +enum Events +{ + EVENT_SAY = 1, + EVENT_FLAG = 2 +}; + +struct boss_tenris_mirkblood : public BossAI +{ + boss_tenris_mirkblood(Creature* creature) : BossAI(creature, DATA_MIRKBLOOD) + { + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } + + void Reset() override + { + _Reset(); + + me->SetImmuneToPC(true); + + ScheduleHealthCheckEvent(50, [&] { + Talk(SAY_SUMMON); + DoCast(SPELL_SUMMON_SANGUINE_SPIRIT_MISSILE_BURST); + }); + + ScheduleHealthCheckEvent(45, [&] { + ScheduleTimedEvent(10s, 15s, [&] { + DoCast(SPELL_BLOOD_TAP); + }, 15s, 40s); + }); + } + + void JustEngagedWith(Unit* /*who*/) override + { + DoZoneInCombat(); + + ScheduleTimedEvent(1s, 5s, [&] { + // Blood Mirror + DoCast(SPELL_BLOOD_MIRROR_TARGET_PICKER); + }, 20s, 50s); + ScheduleTimedEvent(30s, [&] { + // Blood Swoop + DoCast(SPELL_DASH_GASH_PRE_SPELL); + }, 15s, 40s); + ScheduleTimedEvent(6s, 15s, [&] { + // Sanguine Spirit + DoCast(SPELL_SUMMON_SANGUINE_SPIRIT_SHORT_MISSILE_BURST); + }, 6s, 15s); + } + + void KilledUnit(Unit* victim) override + { + if (!victim) + return; + + DoCast(victim, SPELL_SUMMON_SANGUINE_SPIRIT_ON_KILL); + } + + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType damageType, SpellSchoolMask damageSchoolMask) override + { + BossAI::DamageTaken(attacker, damage, damageType, damageSchoolMask); + + if (!me->HasAura(SPELL_BLOOD_MIRROR0)) + return; + + if (!_mirrorTarget) + return; + + int32 damageTaken = damage; + + me->CastCustomSpell(_mirrorTarget, SPELL_BLOOD_MIRROR_DAMAGE, &damageTaken, &damageTaken, &damageTaken, true, nullptr, nullptr, me->GetGUID()); + } + + void SpellHit(Unit* caster, SpellInfo const* spell) override + { + if (spell->Id == SPELL_BLOOD_MIRROR0 && caster != me) + _mirrorTarget = caster; + } + + void EnterEvadeMode(EvadeReason why) override + { + _EnterEvadeMode(why); + me->SetImmuneToPC(false); + } + +private: + Unit* _mirrorTarget = nullptr; +}; + +struct npc_sanguine_spirit : public ScriptedAI +{ + npc_sanguine_spirit(Creature* creature) : ScriptedAI(creature) {} + + void Reset() override + { + scheduler.CancelAll(); + me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ALL, true); + + me->SetReactState(REACT_PASSIVE); + + DoCastSelf(SPELL_SANGUINE_SPIRIT_PRE_AURA); + + scheduler.Schedule(5s, [this](TaskContext /*context*/) + { + DoCastSelf(SPELL_SANGUINE_SPIRIT_PRE_AURA2); + }).Schedule(3s, [this](TaskContext /*context*/) + { + me->SetReactState(REACT_AGGRESSIVE); + me->SetInCombatWithZone(); + DoCastSelf(SPELL_SANGUINE_SPIRIT_AURA); + }); + } + + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + UpdateVictim(); + } +}; + +class spell_mirkblood_blood_mirror_target_picker : public SpellScript +{ + PrepareSpellScript(spell_mirkblood_blood_mirror_target_picker) + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_BLOOD_MIRROR0, SPELL_BLOOD_MIRROR1, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL }); + } + + void HandleHit() + { + Unit* caster = GetCaster(); + + if (!caster->ToCreature()) + return; + + Unit* target = caster->GetAI()->SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true, false); + + if (!target) // Only Blood Mirror the tank if they're the only one around + target = caster->GetVictim(); + + if (!target) + return; + + caster->CastSpell(caster, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL, TRIGGERED_FULL_MASK); + caster->CastSpell(target, SPELL_BLOOD_MIRROR_TRANSITION_VISUAL, TRIGGERED_FULL_MASK); + + caster->AddAura(SPELL_BLOOD_MIRROR1, caster); // Should be a cast, but channeled spell results in either Mirkblood or player being unactionable + caster->AddAura(SPELL_BLOOD_MIRROR1, target); // Adding aura manually causes visual to not appear properly, but better than breaking gameplay + + target->CastSpell(caster, SPELL_BLOOD_MIRROR0); // Clone player + } + + void Register() override + { + OnCast += SpellCastFn(spell_mirkblood_blood_mirror_target_picker::HandleHit); + } +}; + +class spell_mirkblood_dash_gash_return_to_tank_pre_spell : public SpellScript +{ + PrepareSpellScript(spell_mirkblood_dash_gash_return_to_tank_pre_spell) + + bool Validate(SpellInfo const* /*spell*/) override + { + return ValidateSpellInfo({ SPELL_DASH_GASH_RETURN_TO_TANK }); + } + + void HandleCast() + { + if (!GetCaster() || !GetCaster()->GetThreatMgr().GetCurrentVictim()) + return; + // Probably wrong, maybe don't charge if would charge the same target? + if (GetCaster()->GetDistance2d(GetCaster()->GetThreatMgr().GetCurrentVictim()) < 5.0f) + return; + + GetCaster()->CastSpell(GetCaster()->GetVictim(), SPELL_DASH_GASH_RETURN_TO_TANK); + } + + void Register() override + { + OnCast += SpellCastFn(spell_mirkblood_dash_gash_return_to_tank_pre_spell::HandleCast); + } +}; + +class spell_mirkblood_exsanguinate : public SpellScript +{ + PrepareSpellScript(spell_mirkblood_exsanguinate) + + void CalculateDamage() + { + if (!GetHitUnit()) + return; + + SetHitDamage(std::max((GetHitUnit()->GetHealth() * 0.66f), 2000.0f)); + } + + void Register() override + { + OnHit += SpellHitFn(spell_mirkblood_exsanguinate::CalculateDamage); + } +}; + +class at_karazhan_mirkblood_approach : public AreaTriggerScript +{ +public: + at_karazhan_mirkblood_approach() : AreaTriggerScript("at_karazhan_mirkblood_approach") {} + + bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override + { + if (InstanceScript* instance = player->GetInstanceScript()) + if (instance->GetBossState(DATA_MIRKBLOOD) != DONE) + if (Creature* mirkblood = instance->GetCreature(DATA_MIRKBLOOD)) + mirkblood->AI()->Talk(SAY_APPROACH, player); + + return false; + } +}; + +class at_karazhan_mirkblood_entrance : public AreaTriggerScript +{ +public: + at_karazhan_mirkblood_entrance() : AreaTriggerScript("at_karazhan_mirkblood_entrance") {} + + bool OnTrigger(Player* player, AreaTrigger const* /*trigger*/) override + { + if (InstanceScript* instance = player->GetInstanceScript()) + if (instance->GetBossState(DATA_MIRKBLOOD) != DONE) + if (Creature* mirkblood = instance->GetCreature(DATA_MIRKBLOOD)) + mirkblood->SetImmuneToPC(false); + + return false; + } +}; + +class go_blood_drenched_door : public GameObjectScript +{ +public: + go_blood_drenched_door() : GameObjectScript("go_blood_drenched_door") {} + + struct go_blood_drenched_doorAI : public GameObjectAI + { + go_blood_drenched_doorAI(GameObject* go) : GameObjectAI(go) {} + + EventMap events; + Creature* mirkblood; + Player* opener; + + bool GossipHello(Player* player, bool /*reportUse*/) override + { + events.Reset(); + + if (InstanceScript* instance = player->GetInstanceScript()) + if (instance->GetBossState(DATA_MIRKBLOOD) != DONE) + { + opener = player; + mirkblood = instance->GetCreature(DATA_MIRKBLOOD); + + events.ScheduleEvent(EVENT_SAY, 1s); + events.ScheduleEvent(EVENT_FLAG, 5s); + me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); + } + + return true; + } + + void UpdateAI(uint32 diff) override + { + if (events.Empty()) + return; + + events.Update(diff); + switch (events.ExecuteEvent()) + { + case EVENT_SAY: + if (!mirkblood) + return; + mirkblood->AI()->Talk(SAY_AGGRO, opener); + break; + case EVENT_FLAG: + if (!mirkblood) + return; + mirkblood->SetImmuneToPC(false); + me->Delete(); + break; + } + } + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_blood_drenched_doorAI(go); + } +}; + +void AddSC_boss_tenris_mirkblood() +{ + RegisterKarazhanCreatureAI(boss_tenris_mirkblood); + RegisterKarazhanCreatureAI(npc_sanguine_spirit); + RegisterSpellScript(spell_mirkblood_blood_mirror_target_picker); + RegisterSpellScript(spell_mirkblood_dash_gash_return_to_tank_pre_spell); + RegisterSpellScript(spell_mirkblood_exsanguinate); + new at_karazhan_mirkblood_approach(); + new at_karazhan_mirkblood_entrance(); + new go_blood_drenched_door(); +} diff --git a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp index 2f4f27dae..a283e65cc 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp @@ -47,6 +47,7 @@ ObjectData const creatureData[] = { NPC_JULIANNE, DATA_JULIANNE }, { NPC_NIGHTBANE, DATA_NIGHTBANE }, { NPC_TERESTIAN_ILLHOOF, DATA_TERESTIAN }, + { NPC_TENRIS_MIRKBLOOD, DATA_MIRKBLOOD }, { 0, 0 } }; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h index 4e0d57f4b..890fdcf2d 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h @@ -73,7 +73,9 @@ enum KZDataTypes DATA_ROAR = 41, DATA_STRAWMAN = 42, DATA_TINHEAD = 43, - DATA_TITO = 44 + DATA_TITO = 44, + + DATA_MIRKBLOOD = 45 }; enum KZOperaEvents @@ -135,7 +137,9 @@ enum KZCreatures // Malchezaar Helpers NPC_INFERNAL_TARGET = 17644, - NPC_INFERNAL_RELAY = 17645 + NPC_INFERNAL_RELAY = 17645, + + NPC_TENRIS_MIRKBLOOD = 28194 }; diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index 67360281a..b92b35215 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -78,6 +78,7 @@ void AddSC_bosses_opera(); void AddSC_boss_netherspite(); void AddSC_karazhan(); void AddSC_boss_nightbane(); +void AddSC_boss_tenris_mirkblood(); void AddSC_boss_felblood_kaelthas(); // Magister's Terrace void AddSC_boss_selin_fireheart(); void AddSC_boss_vexallus(); @@ -229,6 +230,7 @@ void AddEasternKingdomsScripts() AddSC_boss_netherspite(); AddSC_karazhan(); AddSC_boss_nightbane(); + AddSC_boss_tenris_mirkblood(); AddSC_boss_felblood_kaelthas(); // Magister's Terrace AddSC_boss_selin_fireheart(); AddSC_boss_vexallus(); From 71c22ff6cf8c9d442579ec5905c5eee73408b029 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Aug 2025 09:29:25 +0000 Subject: [PATCH 056/155] chore(DB): import pending files Referenced commit(s): cdceb775a03ecfec8117d922108b21817aff1394 --- .../tenris-mirkblood.sql => db_world/2025_08_27_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/tenris-mirkblood.sql => db_world/2025_08_27_01.sql} (99%) diff --git a/data/sql/updates/pending_db_world/tenris-mirkblood.sql b/data/sql/updates/db_world/2025_08_27_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/tenris-mirkblood.sql rename to data/sql/updates/db_world/2025_08_27_01.sql index 67a76c31a..eab3c1735 100644 --- a/data/sql/updates/pending_db_world/tenris-mirkblood.sql +++ b/data/sql/updates/db_world/2025_08_27_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_27_00 -> 2025_08_27_01 SET @SAY_APPROACH = 0, @SAY_AGGRO = 1, @SAY_SUMMON = 2, From a3131d5cdb81f6817bbcef8c43ee535ed4c831cf Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Wed, 27 Aug 2025 11:43:39 -0700 Subject: [PATCH 057/155] =?UTF-8?q?fix(Core/Player):=20Recast=20lost=20by?= =?UTF-8?q?=20death=20item=20obtain=20spells=20of=20any=20item=E2=80=A6=20?= =?UTF-8?q?(#22736)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ariel- --- src/server/game/Entities/Player/Player.cpp | 43 +++++++++++++++++++ src/server/game/Entities/Player/Player.h | 3 ++ .../game/Entities/Player/PlayerStorage.cpp | 25 +++++------ 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 00cde81a6..f9fb147a8 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -4550,6 +4550,9 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) // update visibility UpdateObjectVisibility(); + // recast lost by death auras of any items held in the inventory + CastAllObtainSpells(); + sScriptMgr->OnPlayerResurrect(this, restore_percent, applySickness); if (!applySickness) @@ -7024,6 +7027,46 @@ void Player::_ApplyWeaponDamage(uint8 slot, ItemTemplate const* proto, ScalingSt UpdateDamagePhysical(WeaponAttackType(attType)); } +void Player::CastAllObtainSpells() +{ + for (uint8 slot = INVENTORY_SLOT_ITEM_START; slot < INVENTORY_SLOT_ITEM_END; ++slot) + if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, slot)) + ApplyItemObtainSpells(item, true); + + for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + Bag* bag = GetBagByPos(i); + if (!bag) + continue; + + for (uint32 slot = 0; slot < bag->GetBagSize(); ++slot) + if (Item* item = bag->GetItemByPos(slot)) + ApplyItemObtainSpells(item, true); + } +} + +void Player::ApplyItemObtainSpells(Item* item, bool apply) +{ + ItemTemplate const* itemTemplate = item->GetTemplate(); + for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (itemTemplate->Spells[i].SpellTrigger != ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger + continue; + + int32 const spellId = itemTemplate->Spells[i].SpellId; + if (spellId <= 0) + continue; + + if (apply) + { + if (!HasAura(spellId)) + CastSpell(this, spellId, true, item); + } + else + RemoveAurasDueToSpell(spellId); + } +} + SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const { if (Item const* weapon = GetWeaponForAttack(attackType, true)) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 17e18c0ef..a79392e7f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2206,6 +2206,9 @@ public: void ResetAllPowers(); + void CastAllObtainSpells(); + void ApplyItemObtainSpells(Item* item, bool apply); + SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const override; void _ApplyWeaponDependentAuraMods(Item* item, WeaponAttackType attackType, bool apply); diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 01595faa5..4836811e2 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -2577,8 +2577,6 @@ Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update) return nullptr; Item* lastItem = pItem; - ItemTemplate const* proto = pItem->GetTemplate(); - for (ItemPosCountVec::const_iterator itr = dest.begin(); itr != dest.end();) { uint16 pos = itr->pos; @@ -2595,13 +2593,6 @@ Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update) lastItem = _StoreItem(pos, pItem, count, true, update); } - // cast after item storing - some checks in checkcast requires item to be present!! - if (lastItem) - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger - if (!HasAura(proto->Spells[i].SpellId)) - CastSpell(this, proto->Spells[i].SpellId, true, lastItem); - return lastItem; } @@ -2664,6 +2655,9 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool AddEnchantmentDurations(pItem); AddItemDurations(pItem); + if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)) + ApplyItemObtainSpells(pItem, true); + return pItem; } else @@ -2700,6 +2694,9 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool pItem2->SetState(ITEM_CHANGED, this); + if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)) + ApplyItemObtainSpells(pItem2, true); + return pItem2; } } @@ -3046,10 +3043,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) pItem->ClearSoulboundTradeable(this); RemoveTradeableItem(pItem); - ItemTemplate const* proto = pItem->GetTemplate(); - for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) - if (proto->Spells[i].SpellTrigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE && proto->Spells[i].SpellId > 0) // On obtain trigger - RemoveAurasDueToSpell(proto->Spells[i].SpellId); + ApplyItemObtainSpells(pItem, false); ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount()); @@ -3102,8 +3096,9 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) pBag->RemoveItem(slot, update); // Xinef: item is removed, remove loot from storage if any - if (proto->HasFlag(ITEM_FLAG_HAS_LOOT)) - sLootItemStorage->RemoveStoredLoot(pItem->GetGUID()); + if (ItemTemplate const* proto = pItem->GetTemplate()) + if (proto->HasFlag(ITEM_FLAG_HAS_LOOT)) + sLootItemStorage->RemoveStoredLoot(pItem->GetGUID()); if (IsInWorld() && update) { From a957b4a5b740407794f5f6fbe4d06b5ac1378b8d Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Wed, 27 Aug 2025 15:32:59 -0700 Subject: [PATCH 058/155] fix(DB/SmartAI): port Shadow of Doom script to SmartAI (#22735) --- .../rev_1756307808923704438.sql | 13 ++++ src/server/scripts/World/scourge_invasion.cpp | 64 ------------------- src/server/scripts/World/scourge_invasion.h | 4 -- 3 files changed, 13 insertions(+), 68 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1756307808923704438.sql diff --git a/data/sql/updates/pending_db_world/rev_1756307808923704438.sql b/data/sql/updates/pending_db_world/rev_1756307808923704438.sql new file mode 100644 index 000000000..1b638115a --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756307808923704438.sql @@ -0,0 +1,13 @@ +-- +UPDATE `creature_template` SET `ScriptName` = '', `AIName` = 'SmartAI' WHERE (`entry` = 16143); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 16143) AND (`source_type` = 0); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16143, 0, 0, 1, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 5000, 1, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Just Summoned - Say Line 0'), +(16143, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 10389, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Just Summoned - Cast \'Spawn Smoke\''), +(16143, 0, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 256, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Just Summoned - Set Flags Immune To Players'), +(16143, 0, 3, 0, 52, 0, 100, 0, 0, 0, 0, 0, 0, 0, 19, 256, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Text 0 Over - Remove Flags Immune To Players'), +(16143, 0, 4, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 28056, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Just Died - Cast \'Zap Crystal Corpse\''), +(16143, 0, 5, 0, 8, 0, 100, 0, 17680, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - On Spellhit \'Spirit Spawn-out\' - Despawn In 3000 ms'), +(16143, 0, 6, 0, 0, 0, 100, 0, 2000, 2000, 6500, 13000, 0, 0, 11, 16568, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - In Combat - Cast \'Mind Flay\''), +(16143, 0, 7, 0, 0, 0, 100, 0, 2000, 2000, 14500, 14500, 0, 0, 11, 12542, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Shadow of Doom - In Combat - Cast \'Fear\''); diff --git a/src/server/scripts/World/scourge_invasion.cpp b/src/server/scripts/World/scourge_invasion.cpp index eac52f705..a53075309 100644 --- a/src/server/scripts/World/scourge_invasion.cpp +++ b/src/server/scripts/World/scourge_invasion.cpp @@ -802,69 +802,6 @@ struct npc_cultist_engineer : public ScriptedAI } }; -struct npc_shadow_of_doom : public CombatAI -{ - npc_shadow_of_doom(Creature* creature) : CombatAI(creature) { } - - void JustEngagedWith(Unit* /*who*/) override - { - scheduler.Schedule(2s, [&](TaskContext context) - { - DoCastVictim(SPELL_MINDFLAY); - context.Repeat(6500ms, 13s); - }).Schedule(2s, [&](TaskContext context) - { - DoCastVictim(SPELL_FEAR); - context.Repeat(14500ms, 14500ms); - }); - } - - void Reset() override - { - scheduler.CancelAll(); - me->SetImmuneToPC(false); - } - - void IsSummonedBy(WorldObject* summoner) override - { - if (!summoner) - return; - - if (Player* player = summoner->ToPlayer()) - { - me->SetImmuneToPC(true); - me->SetFacingToObject(player); - - Talk(SHADOW_OF_DOOM_SAY_AGGRO, player); - DoCastSelf(SPELL_SPAWN_SMOKE, true); - - scheduler.Schedule(5s, [this, player](TaskContext const& /*context*/) - { - me->SetImmuneToPC(false); - if (me->CanStartAttack(player)) - AttackStart(player); - }); - } - } - - void JustDied(Unit* /*pKiller*/) override - { - DoCastSelf(SPELL_ZAP_CRYSTAL_CORPSE, true); - } - - void SpellHit(Unit* /* caster */, SpellInfo const* spell) override - { - if (spell->Id == SPELL_SPIRIT_SPAWN_OUT) - me->DespawnOrUnsummon(3000); - } - - void UpdateAI(uint32 const diff) override - { - scheduler.Update(diff); - DoMeleeAttackIfReady(); - } -}; - struct npc_flameshocker : public CombatAI { npc_flameshocker(Creature* creature) : CombatAI(creature) { } @@ -1121,7 +1058,6 @@ void AddSC_scourge_invasion() RegisterCreatureAI(npc_minion_spawner); RegisterCreatureAI(npc_pallid_horror); RegisterCreatureAI(npc_cultist_engineer); - RegisterCreatureAI(npc_shadow_of_doom); RegisterCreatureAI(npc_flameshocker); RegisterSpellScript(spell_communique_trigger); RegisterSpellScript(spell_despawner_self); diff --git a/src/server/scripts/World/scourge_invasion.h b/src/server/scripts/World/scourge_invasion.h index fa927340a..0bc511ace 100644 --- a/src/server/scripts/World/scourge_invasion.h +++ b/src/server/scripts/World/scourge_invasion.h @@ -137,10 +137,7 @@ enum ScourgeInvasionSpells */ // Shadow of Doom - SPELL_SPAWN_SMOKE = 10389, // Spawning Visual. SPELL_ZAP_CRYSTAL_CORPSE = 28056, // Casted on Shard if Shadow of Doom dies. - SPELL_MINDFLAY = 16568, - SPELL_FEAR = 12542, // Pallid Horror - Patchwerk Terror (also uses: 28315) SPELL_SUMMON_CRACKED_NECROTIC_CRYSTAL = 28424, // Alliance. @@ -336,7 +333,6 @@ enum ScourgeInvasionTalk HERALD_OF_THE_LICH_KING_SAY_ATTACK_END = 1, HERALD_OF_THE_LICH_KING_SAY_ATTACK_RANDOM = 2, PALLID_HORROR_SAY_RANDOM_YELL = 0, - SHADOW_OF_DOOM_SAY_AGGRO = 0, SYLVANAS_SAY_ATTACK_END = 3, VARIAN_SAY_ATTACK_END = 3 }; From 36138dccb7e31d1e982cc8a5b9d07152493cd63b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 27 Aug 2025 22:34:06 +0000 Subject: [PATCH 059/155] chore(DB): import pending files Referenced commit(s): a957b4a5b740407794f5f6fbe4d06b5ac1378b8d --- .../rev_1756307808923704438.sql => db_world/2025_08_27_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756307808923704438.sql => db_world/2025_08_27_02.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1756307808923704438.sql b/data/sql/updates/db_world/2025_08_27_02.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1756307808923704438.sql rename to data/sql/updates/db_world/2025_08_27_02.sql index 1b638115a..ee7d79294 100644 --- a/data/sql/updates/pending_db_world/rev_1756307808923704438.sql +++ b/data/sql/updates/db_world/2025_08_27_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_27_01 -> 2025_08_27_02 -- UPDATE `creature_template` SET `ScriptName` = '', `AIName` = 'SmartAI' WHERE (`entry` = 16143); From e39333abcb8150bfb71ccaf3c6dc0ad00322eeef Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Thu, 28 Aug 2025 12:34:45 -0400 Subject: [PATCH 060/155] fix(Core/WorldState): Start boss activation event when starting Scourge Invasion. (#22739) --- src/server/game/World/WorldState.cpp | 2 ++ src/server/game/World/WorldState.h | 1 + 2 files changed, 3 insertions(+) diff --git a/src/server/game/World/WorldState.cpp b/src/server/game/World/WorldState.cpp index 0ca7ea0c0..a1f71a2a5 100644 --- a/src/server/game/World/WorldState.cpp +++ b/src/server/game/World/WorldState.cpp @@ -1312,6 +1312,8 @@ void WorldState::StartScourgeInvasion(bool sendMail) Acore::Containers::RandomShuffle(randomIds); for (uint32 id : randomIds) OnEnable(m_siData.m_activeInvasions[id]); + + sGameEventMgr->StartEvent(GAME_EVENT_SCOURGE_INVASION_BOSSES); } } diff --git a/src/server/game/World/WorldState.h b/src/server/game/World/WorldState.h index be5379246..b850010ac 100644 --- a/src/server/game/World/WorldState.h +++ b/src/server/game/World/WorldState.h @@ -65,6 +65,7 @@ enum WorldStateGameEvents { // Scourge Invasion GAME_EVENT_SCOURGE_INVASION = 17, + GAME_EVENT_SCOURGE_INVASION_BOSSES = 120, GAME_EVENT_SCOURGE_INVASION_WINTERSPRING = 121, GAME_EVENT_SCOURGE_INVASION_TANARIS = 122, GAME_EVENT_SCOURGE_INVASION_AZSHARA = 123, From 99463bce9a7f1b13b4032b172dce172053627204 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Thu, 28 Aug 2025 19:41:54 -0400 Subject: [PATCH 061/155] fix(DB/Creature): Bind players to instance upon killing Tenris Mirkblood. (#22743) --- data/sql/updates/pending_db_world/tenris-bind.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 data/sql/updates/pending_db_world/tenris-bind.sql diff --git a/data/sql/updates/pending_db_world/tenris-bind.sql b/data/sql/updates/pending_db_world/tenris-bind.sql new file mode 100644 index 000000000..83fc95a94 --- /dev/null +++ b/data/sql/updates/pending_db_world/tenris-bind.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 1 WHERE `entry` = 28194; From 50bbddc7bf54dca615f863ddb90224542bed1a10 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 28 Aug 2025 23:42:55 +0000 Subject: [PATCH 062/155] chore(DB): import pending files Referenced commit(s): 99463bce9a7f1b13b4032b172dce172053627204 --- .../tenris-bind.sql => db_world/2025_08_28_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/tenris-bind.sql => db_world/2025_08_28_00.sql} (66%) diff --git a/data/sql/updates/pending_db_world/tenris-bind.sql b/data/sql/updates/db_world/2025_08_28_00.sql similarity index 66% rename from data/sql/updates/pending_db_world/tenris-bind.sql rename to data/sql/updates/db_world/2025_08_28_00.sql index 83fc95a94..befc80e5b 100644 --- a/data/sql/updates/pending_db_world/tenris-bind.sql +++ b/data/sql/updates/db_world/2025_08_28_00.sql @@ -1 +1,2 @@ +-- DB update 2025_08_27_02 -> 2025_08_28_00 UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 1 WHERE `entry` = 28194; From 4bf57d11f2d2cc137c00612ec398dccdfecf88c1 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Thu, 28 Aug 2025 22:53:26 -0400 Subject: [PATCH 063/155] fix(DB/Loot): Adjust drops for various Scourge Invasion related items. (#22738) --- .../scourge-invasion-loot.sql | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 data/sql/updates/pending_db_world/scourge-invasion-loot.sql diff --git a/data/sql/updates/pending_db_world/scourge-invasion-loot.sql b/data/sql/updates/pending_db_world/scourge-invasion-loot.sql new file mode 100644 index 000000000..6b812b084 --- /dev/null +++ b/data/sql/updates/pending_db_world/scourge-invasion-loot.sql @@ -0,0 +1,42 @@ +-- Necrotic Runes +DELETE FROM `creature_loot_template` WHERE `Item` = 22484; +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(14697, 22484, 0, 100, 0, 1, 0, 2, 3, 'Lumbering Horror - Necrotic Rune'), +(16379, 22484, 0, 100, 0, 1, 0, 2, 3, 'Spirit of the Damned - Necrotic Rune'), +(16380, 22484, 0, 100, 0, 1, 0, 2, 3, 'Bone Witch - Necrotic Rune'), +(16143, 22484, 0, 100, 0, 1, 0, 30, 30, 'Shadow of Doom - Necrotic Rune'), +(16141, 22484, 0, 33.33, 0, 1, 0, 1, 1, 'Ghoul Berserker - Necrotic Rune'), +(16298, 22484, 0, 33.33, 0, 1, 0, 1, 1, 'Spectral Soldier - Necrotic Rune'), +(16299, 22484, 0, 33.33, 0, 1, 0, 1, 1, 'Skeletal Shocktrooper - Necrotic Rune'), +(16383, 22484, 0, 33.33, 0, 1, 0, 1, 1, 'Flameshocker - Necrotic Rune'); + +-- Sealed Research Report items +DELETE FROM `creature_loot_template` WHERE `Item` IN (22970, 22972, 22973, 22974, 22975, 22977); +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(16141, 22970, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Bloodstained Envelope'), +(16141, 22972, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Careworn Note'), +(16141, 22973, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Crumpled Missive'), +(16141, 22974, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Ragged Page'), +(16141, 22975, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Smudged Document'), +(16141, 22977, 0, 2, 0, 1, 1, 1, 1, 'Ghoul Berserker - A Torn Letter'), +(16298, 22970, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Bloodstained Envelope'), +(16298, 22972, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Careworn Note'), +(16298, 22973, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Crumpled Missive'), +(16298, 22974, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Ragged Page'), +(16298, 22975, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Smudged Document'), +(16298, 22977, 0, 2, 0, 1, 1, 1, 1, 'Spectral Soldier - A Torn Letter'), +(16299, 22970, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Bloodstained Envelope'), +(16299, 22972, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Careworn Note'), +(16299, 22973, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Crumpled Missive'), +(16299, 22974, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Ragged Page'), +(16299, 22975, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Smudged Document'), +(16299, 22977, 0, 2, 0, 1, 1, 1, 1, 'Skeletal Shocktrooper - A Torn Letter'), +(16383, 22970, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Bloodstained Envelope'), +(16383, 22972, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Careworn Note'), +(16383, 22973, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Crumpled Missive'), +(16383, 22974, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Ragged Page'), +(16383, 22975, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Smudged Document'), +(16383, 22977, 0, 2, 0, 1, 1, 1, 1, 'Flameshocker - A Torn Letter'); + +-- Dim Necrotic Stone +UPDATE `creature_loot_template` SET `Chance` = 25 WHERE `Item` = 22892; From 7756b41160e171ff850a34811f9b80fb3bdb8d16 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 29 Aug 2025 02:54:27 +0000 Subject: [PATCH 064/155] chore(DB): import pending files Referenced commit(s): 4bf57d11f2d2cc137c00612ec398dccdfecf88c1 --- .../scourge-invasion-loot.sql => db_world/2025_08_29_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/scourge-invasion-loot.sql => db_world/2025_08_29_00.sql} (98%) diff --git a/data/sql/updates/pending_db_world/scourge-invasion-loot.sql b/data/sql/updates/db_world/2025_08_29_00.sql similarity index 98% rename from data/sql/updates/pending_db_world/scourge-invasion-loot.sql rename to data/sql/updates/db_world/2025_08_29_00.sql index 6b812b084..0d320af88 100644 --- a/data/sql/updates/pending_db_world/scourge-invasion-loot.sql +++ b/data/sql/updates/db_world/2025_08_29_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_28_00 -> 2025_08_29_00 -- Necrotic Runes DELETE FROM `creature_loot_template` WHERE `Item` = 22484; INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES From c2b298eaacf3b9325e040e5284e30611fe946ca7 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Fri, 29 Aug 2025 00:43:36 -0300 Subject: [PATCH 065/155] fix(Scripts/SI): crash fix (#22744) --- src/server/scripts/World/scourge_invasion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/scripts/World/scourge_invasion.cpp b/src/server/scripts/World/scourge_invasion.cpp index a53075309..982049624 100644 --- a/src/server/scripts/World/scourge_invasion.cpp +++ b/src/server/scripts/World/scourge_invasion.cpp @@ -499,7 +499,7 @@ struct npc_necrotic_shard : public ScriptedAI // Only Minions and the shard itself can deal damage. void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override { - if (attacker->GetFactionTemplateEntry() != me->GetFactionTemplateEntry()) + if (attacker && attacker->GetFactionTemplateEntry() != me->GetFactionTemplateEntry()) damage = 0; } From 49dc26bf1d8cc23264fedc4e77ca7e850c7a5952 Mon Sep 17 00:00:00 2001 From: Annamaria <20357406+Annamaria-CC@users.noreply.github.com> Date: Fri, 29 Aug 2025 15:37:29 +0200 Subject: [PATCH 066/155] fix (db/smart_scripts): Skeletal Horrors only cast when 2 or more players (#22745) --- data/sql/updates/pending_db_world/skeletalhorrors.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/skeletalhorrors.sql diff --git a/data/sql/updates/pending_db_world/skeletalhorrors.sql b/data/sql/updates/pending_db_world/skeletalhorrors.sql new file mode 100644 index 000000000..d64c02fe6 --- /dev/null +++ b/data/sql/updates/pending_db_world/skeletalhorrors.sql @@ -0,0 +1,3 @@ +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 202) AND (`source_type` = 0) AND (`id` IN (0)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(202, 0, 0, 0, 101, 0, 100, 0, 2, 5, 5000, 9000, 13000, 0, 11, 7399, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Horror - On 2 or More Players in Range - Cast \'Terrify\''); From ba607dad882fbd65b50c9af186a234a731306b49 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 29 Aug 2025 13:40:27 +0000 Subject: [PATCH 067/155] chore(DB): import pending files Referenced commit(s): 49dc26bf1d8cc23264fedc4e77ca7e850c7a5952 --- .../skeletalhorrors.sql => db_world/2025_08_29_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/skeletalhorrors.sql => db_world/2025_08_29_01.sql} (94%) diff --git a/data/sql/updates/pending_db_world/skeletalhorrors.sql b/data/sql/updates/db_world/2025_08_29_01.sql similarity index 94% rename from data/sql/updates/pending_db_world/skeletalhorrors.sql rename to data/sql/updates/db_world/2025_08_29_01.sql index d64c02fe6..0abfb0727 100644 --- a/data/sql/updates/pending_db_world/skeletalhorrors.sql +++ b/data/sql/updates/db_world/2025_08_29_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_29_00 -> 2025_08_29_01 DELETE FROM `smart_scripts` WHERE (`entryorguid` = 202) AND (`source_type` = 0) AND (`id` IN (0)); INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES (202, 0, 0, 0, 101, 0, 100, 0, 2, 5, 5000, 9000, 13000, 0, 11, 7399, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Horror - On 2 or More Players in Range - Cast \'Terrify\''); From 445161d3b9d5276d1a63831e98c83015f39a9522 Mon Sep 17 00:00:00 2001 From: iThorgrim <125808072+iThorgrim@users.noreply.github.com> Date: Fri, 29 Aug 2025 22:19:57 +0200 Subject: [PATCH 068/155] refactor(Core/Script): Rename Creature_SelectLevel to OnCreatureSelectLevel for clarity (#22750) --- src/server/game/Entities/Creature/Creature.cpp | 2 +- src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp | 4 ++-- src/server/game/Scripting/ScriptDefines/AllCreatureScript.h | 2 +- src/server/game/Scripting/ScriptMgr.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 996b5ae05..43534a409 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1578,7 +1578,7 @@ void Creature::SelectLevel(bool changelevel) SetModifierValue(UNIT_MOD_ATTACK_POWER, BASE_VALUE, stats->AttackPower); SetModifierValue(UNIT_MOD_ATTACK_POWER_RANGED, BASE_VALUE, stats->RangedAttackPower); - sScriptMgr->Creature_SelectLevel(cInfo, this); + sScriptMgr->OnCreatureSelectLevel(cInfo, this); } float Creature::_GetHealthMod(int32 Rank) diff --git a/src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp b/src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp index 45d90cf10..f34599d81 100644 --- a/src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/AllCreatureScript.cpp @@ -57,11 +57,11 @@ void ScriptMgr::OnBeforeCreatureSelectLevel(const CreatureTemplate* cinfo, Creat }); } -void ScriptMgr::Creature_SelectLevel(const CreatureTemplate* cinfo, Creature* creature) +void ScriptMgr::OnCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature) { ExecuteScript([&](AllCreatureScript* script) { - script->Creature_SelectLevel(cinfo, creature); + script->OnCreatureSelectLevel(cinfo, creature); }); } diff --git a/src/server/game/Scripting/ScriptDefines/AllCreatureScript.h b/src/server/game/Scripting/ScriptDefines/AllCreatureScript.h index a4f83dd2b..71e230e8b 100644 --- a/src/server/game/Scripting/ScriptDefines/AllCreatureScript.h +++ b/src/server/game/Scripting/ScriptDefines/AllCreatureScript.h @@ -33,7 +33,7 @@ public: virtual void OnBeforeCreatureSelectLevel(const CreatureTemplate* /*cinfo*/, Creature* /*creature*/, uint8& /*level*/) { } // Called from End of Creature SelectLevel. - virtual void Creature_SelectLevel(const CreatureTemplate* /*cinfo*/, Creature* /*creature*/) { } + virtual void OnCreatureSelectLevel(const CreatureTemplate* /*cinfo*/, Creature* /*creature*/) { } /** * @brief This hook runs after add creature in world diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index e15ac9050..6b3efdb86 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -572,7 +572,7 @@ public: /* AllCreatureScript */ //listener function (OnAllCreatureUpdate) is called by OnCreatureUpdate //void OnAllCreatureUpdate(Creature* creature, uint32 diff); void OnBeforeCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature, uint8& level); - void Creature_SelectLevel(const CreatureTemplate* cinfo, Creature* creature); + void OnCreatureSelectLevel(const CreatureTemplate* cinfo, Creature* creature); void OnCreatureSaveToDB(Creature* creature); public: /* AllGameobjectScript */ From d9c99cd0156a4329250ae3fb419d923ca25b5df5 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Sat, 30 Aug 2025 08:43:06 -0400 Subject: [PATCH 069/155] fix(Scripts/Karazhan): Minor adjustments to Tenris Mirkblood. (#22753) --- .../pending_db_world/mirkblood-spawntime.sql | 1 + .../Karazhan/boss_tenris_mirkblood.cpp | 15 ++++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) create mode 100644 data/sql/updates/pending_db_world/mirkblood-spawntime.sql diff --git a/data/sql/updates/pending_db_world/mirkblood-spawntime.sql b/data/sql/updates/pending_db_world/mirkblood-spawntime.sql new file mode 100644 index 000000000..a884c1288 --- /dev/null +++ b/data/sql/updates/pending_db_world/mirkblood-spawntime.sql @@ -0,0 +1 @@ +UPDATE `creature` SET `spawntimesecs` = 604800 WHERE `id1` = 28194; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp index 518a46055..bed336447 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_tenris_mirkblood.cpp @@ -177,6 +177,9 @@ struct npc_sanguine_spirit : public ScriptedAI me->SetReactState(REACT_AGGRESSIVE); me->SetInCombatWithZone(); DoCastSelf(SPELL_SANGUINE_SPIRIT_AURA); + }).Schedule(30s, [this](TaskContext /*context*/) + { + me->DespawnOrUnsummon(); }); } @@ -320,6 +323,7 @@ public: events.Reset(); if (InstanceScript* instance = player->GetInstanceScript()) + { if (instance->GetBossState(DATA_MIRKBLOOD) != DONE) { opener = player; @@ -327,8 +331,10 @@ public: events.ScheduleEvent(EVENT_SAY, 1s); events.ScheduleEvent(EVENT_FLAG, 5s); - me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); } + } + + me->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE); return true; } @@ -342,14 +348,13 @@ public: switch (events.ExecuteEvent()) { case EVENT_SAY: - if (!mirkblood) + if (!mirkblood || !mirkblood->IsAlive()) return; mirkblood->AI()->Talk(SAY_AGGRO, opener); break; case EVENT_FLAG: - if (!mirkblood) - return; - mirkblood->SetImmuneToPC(false); + if (mirkblood) + mirkblood->SetImmuneToPC(false); me->Delete(); break; } From 6065270a37c35663ee928b05e0f43cbe6a39e191 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 30 Aug 2025 12:44:18 +0000 Subject: [PATCH 070/155] chore(DB): import pending files Referenced commit(s): d9c99cd0156a4329250ae3fb419d923ca25b5df5 --- .../mirkblood-spawntime.sql => db_world/2025_08_30_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/mirkblood-spawntime.sql => db_world/2025_08_30_00.sql} (60%) diff --git a/data/sql/updates/pending_db_world/mirkblood-spawntime.sql b/data/sql/updates/db_world/2025_08_30_00.sql similarity index 60% rename from data/sql/updates/pending_db_world/mirkblood-spawntime.sql rename to data/sql/updates/db_world/2025_08_30_00.sql index a884c1288..884506500 100644 --- a/data/sql/updates/pending_db_world/mirkblood-spawntime.sql +++ b/data/sql/updates/db_world/2025_08_30_00.sql @@ -1 +1,2 @@ +-- DB update 2025_08_29_01 -> 2025_08_30_00 UPDATE `creature` SET `spawntimesecs` = 604800 WHERE `id1` = 28194; From 427557c3d8722704b33fd35b3c4e84f2a3fedaf1 Mon Sep 17 00:00:00 2001 From: sudlud Date: Sat, 30 Aug 2025 18:38:17 +0200 Subject: [PATCH 071/155] fix(DB/Creature): Sniffed Values for 'Tahu Sagewind' spawns (#22754) --- .../sql/updates/pending_db_world/rev_1756552312753254300.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756552312753254300.sql diff --git a/data/sql/updates/pending_db_world/rev_1756552312753254300.sql b/data/sql/updates/pending_db_world/rev_1756552312753254300.sql new file mode 100644 index 000000000..82ccaa783 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756552312753254300.sql @@ -0,0 +1,5 @@ +-- Update creature 'Tahu Sagewind' with sniffed values +-- new spawns +DELETE FROM `creature` WHERE (`id1` IN (34528)) AND (`guid` IN (37)); +INSERT INTO `creature` (`guid`, `id1`, `map`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +(37, 34528, 1, 1, 1, 0, -1047.1771240234375, -287.98785400390625, 159.113677978515625, 2.728425025939941406, 120, 0, 0, 0, 0, 0, "", 45435, 1, NULL); From 78b9f6ab148fa3340f680ee286ec618a2080bd43 Mon Sep 17 00:00:00 2001 From: sudlud Date: Sat, 30 Aug 2025 18:38:32 +0200 Subject: [PATCH 072/155] fix(DB/Gameobject): Sniffed Values for 'Mag'har Rug' spawns (#22755) --- .../pending_db_world/rev_1756553610733937000.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756553610733937000.sql diff --git a/data/sql/updates/pending_db_world/rev_1756553610733937000.sql b/data/sql/updates/pending_db_world/rev_1756553610733937000.sql new file mode 100644 index 000000000..d92ad22f3 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756553610733937000.sql @@ -0,0 +1,12 @@ +-- Update gameobject 'Mag'har Rug' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (182257)) AND (`guid` IN (22684, 22685, 22686)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(22684, 182257, 530, 0, 0, 1, 1, -1235.0460205078125, 7247.54248046875, 57.33856201171875, 4.97418975830078125, 0, 0, -0.60876083374023437, 0.793353796005249023, 120, 255, 1, "", 45704, NULL), +(22685, 182257, 530, 0, 0, 1, 1, -1242.7056884765625, 7246.685546875, 57.29010009765625, 4.97418975830078125, 0, 0, -0.60876083374023437, 0.793353796005249023, 120, 255, 1, "", 45704, NULL), +(22686, 182257, 530, 0, 0, 1, 1, -1238.972412109375, 7247.02001953125, 57.3078765869140625, 4.97418975830078125, 0, 0, -0.60876083374023437, 0.793353796005249023, 120, 255, 1, "", 45704, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (182257)) AND (`guid` IN (38)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(38, 182257, 1, 0, 0, 1, 1, -1049.82470703125, -286.196197509765625, 159.0303497314453125, 2.548179388046264648, 0, 0, 0.956304550170898437, 0.292372345924377441, 120, 255, 1, "", 45435, NULL); From 9a80edbe9289d1d274f79e98a6607c720c23b174 Mon Sep 17 00:00:00 2001 From: sudlud Date: Sat, 30 Aug 2025 18:38:47 +0200 Subject: [PATCH 073/155] fix(DB/Gameobject): Sniffed Values for 'Stranglekelp Sack' spawns (#22756) --- .../pending_db_world/rev_1756556406016957600.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756556406016957600.sql diff --git a/data/sql/updates/pending_db_world/rev_1756556406016957600.sql b/data/sql/updates/pending_db_world/rev_1756556406016957600.sql new file mode 100644 index 000000000..da4dc2079 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756556406016957600.sql @@ -0,0 +1,11 @@ +-- Update gameobject 'Stranglekelp Sack' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (185004)) AND (`guid` IN (25928)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(25928, 185004, 530, 0, 0, 1, 1, 728.25927734375, 6844.61083984375, -66.3580474853515625, 4.433136463165283203, 0, 0, -0.79863548278808593, 0.60181504487991333, 120, 255, 1, "", 45942, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (185004)) AND (`guid` IN (84, 85)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(84, 185004, 1, 0, 0, 1, 1, -1049.5347900390625, -290.34722900390625, 159.0303497314453125, 0.209439441561698913, 0, 0, 0.104528427124023437, 0.994521915912628173, 120, 255, 1, "", 45435, NULL), +(85, 185004, 1, 0, 0, 1, 1, -1050.2257080078125, -290.552093505859375, 159.0303497314453125, 2.495818138122558593, 0, 0, 0.948323249816894531, 0.317305892705917358, 120, 255, 1, "", 45435, NULL); From 5c31e3b411ba8dfec9ecdfacca494accf7f59119 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 30 Aug 2025 16:39:19 +0000 Subject: [PATCH 074/155] chore(DB): import pending files Referenced commit(s): 427557c3d8722704b33fd35b3c4e84f2a3fedaf1 --- .../rev_1756552312753254300.sql => db_world/2025_08_30_01.sql} | 1 + .../rev_1756553610733937000.sql => db_world/2025_08_30_02.sql} | 1 + .../rev_1756556406016957600.sql => db_world/2025_08_30_03.sql} | 1 + 3 files changed, 3 insertions(+) rename data/sql/updates/{pending_db_world/rev_1756552312753254300.sql => db_world/2025_08_30_01.sql} (93%) rename data/sql/updates/{pending_db_world/rev_1756553610733937000.sql => db_world/2025_08_30_02.sql} (97%) rename data/sql/updates/{pending_db_world/rev_1756556406016957600.sql => db_world/2025_08_30_03.sql} (96%) diff --git a/data/sql/updates/pending_db_world/rev_1756552312753254300.sql b/data/sql/updates/db_world/2025_08_30_01.sql similarity index 93% rename from data/sql/updates/pending_db_world/rev_1756552312753254300.sql rename to data/sql/updates/db_world/2025_08_30_01.sql index 82ccaa783..184fa73ce 100644 --- a/data/sql/updates/pending_db_world/rev_1756552312753254300.sql +++ b/data/sql/updates/db_world/2025_08_30_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_30_00 -> 2025_08_30_01 -- Update creature 'Tahu Sagewind' with sniffed values -- new spawns DELETE FROM `creature` WHERE (`id1` IN (34528)) AND (`guid` IN (37)); diff --git a/data/sql/updates/pending_db_world/rev_1756553610733937000.sql b/data/sql/updates/db_world/2025_08_30_02.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1756553610733937000.sql rename to data/sql/updates/db_world/2025_08_30_02.sql index d92ad22f3..89b84f42c 100644 --- a/data/sql/updates/pending_db_world/rev_1756553610733937000.sql +++ b/data/sql/updates/db_world/2025_08_30_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_30_01 -> 2025_08_30_02 -- Update gameobject 'Mag'har Rug' with sniffed values -- updated spawns DELETE FROM `gameobject` WHERE (`id` IN (182257)) AND (`guid` IN (22684, 22685, 22686)); diff --git a/data/sql/updates/pending_db_world/rev_1756556406016957600.sql b/data/sql/updates/db_world/2025_08_30_03.sql similarity index 96% rename from data/sql/updates/pending_db_world/rev_1756556406016957600.sql rename to data/sql/updates/db_world/2025_08_30_03.sql index da4dc2079..c2927a889 100644 --- a/data/sql/updates/pending_db_world/rev_1756556406016957600.sql +++ b/data/sql/updates/db_world/2025_08_30_03.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_30_02 -> 2025_08_30_03 -- Update gameobject 'Stranglekelp Sack' with sniffed values -- updated spawns DELETE FROM `gameobject` WHERE (`id` IN (185004)) AND (`guid` IN (25928)); From 5a79a4edce0f39541d2e9b363dcbe0cc79c32a1e Mon Sep 17 00:00:00 2001 From: Yehonal Date: Sat, 30 Aug 2025 23:44:07 +0200 Subject: [PATCH 075/155] Feat/refactoring-module-menu (#22733) --- .github/workflows/dashboard-ci.yml | 9 +- apps/compiler/test/test_compiler.bats | 6 +- apps/installer/includes/functions.sh | 152 +--- .../includes/modules-manager/README.md | 188 +++++ .../includes/modules-manager/modules.sh | 735 ++++++++++++++++++ apps/installer/main.sh | 81 +- apps/installer/test/bats.conf | 14 + apps/installer/test/test_module_commands.bats | 354 +++++++++ conf/dist/config.sh | 13 + deps/acore/joiner/joiner.sh | 36 +- 10 files changed, 1398 insertions(+), 190 deletions(-) create mode 100644 apps/installer/includes/modules-manager/README.md create mode 100644 apps/installer/includes/modules-manager/modules.sh create mode 100644 apps/installer/test/bats.conf create mode 100755 apps/installer/test/test_module_commands.bats diff --git a/.github/workflows/dashboard-ci.yml b/.github/workflows/dashboard-ci.yml index 25843a690..fbdd88124 100644 --- a/.github/workflows/dashboard-ci.yml +++ b/.github/workflows/dashboard-ci.yml @@ -42,7 +42,12 @@ jobs: - name: Install requirements run: | - sudo apt install -y bats + sudo apt-get update + # Install bats-core >= 1.5.0 to support bats_require_minimum_version + sudo apt-get install -y git curl + git clone --depth 1 https://github.com/bats-core/bats-core.git /tmp/bats-core + sudo /tmp/bats-core/install.sh /usr/local + bats --version ./acore.sh install-deps - name: Run bash script tests for ${{ matrix.test-module }} @@ -50,7 +55,7 @@ jobs: TERM: xterm-256color run: | cd apps/test-framework - ./run-tests.sh --tap + ./run-tests.sh --tap --all build-and-test: name: Build and Integration Test diff --git a/apps/compiler/test/test_compiler.bats b/apps/compiler/test/test_compiler.bats index ff217e638..79152b68e 100755 --- a/apps/compiler/test/test_compiler.bats +++ b/apps/compiler/test/test_compiler.bats @@ -1,7 +1,9 @@ #!/usr/bin/env bats -# Require minimum BATS version to avoid warnings -bats_require_minimum_version 1.5.0 +# Require minimum BATS version when supported (older distro packages lack this) +if type -t bats_require_minimum_version >/dev/null 2>&1; then + bats_require_minimum_version 1.5.0 +fi # AzerothCore Compiler Scripts Test Suite # Tests the functionality of the compiler scripts using the unified test framework diff --git a/apps/installer/includes/functions.sh b/apps/installer/includes/functions.sh index b4bd14caf..eb9fe1a24 100644 --- a/apps/installer/includes/functions.sh +++ b/apps/installer/includes/functions.sh @@ -118,142 +118,28 @@ function inst_allInOne() { inst_download_client_data } -function inst_getVersionBranch() { - local res="master" - local v="not-defined" - local MODULE_MAJOR=0 - local MODULE_MINOR=0 - local MODULE_PATCH=0 - local MODULE_SPECIAL=0; - local ACV_MAJOR=0 - local ACV_MINOR=0 - local ACV_PATCH=0 - local ACV_SPECIAL=0; - local curldata=$(curl -f --silent -H 'Cache-Control: no-cache' "$1" || echo "{}") - local parsed=$(echo "$curldata" | "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.compatibility.*.[version,branch]') +############################################################ +# Module helpers and dispatcher # +############################################################ - semverParseInto "$ACORE_VERSION" ACV_MAJOR ACV_MINOR ACV_PATCH ACV_SPECIAL - - if [[ ! -z "$parsed" ]]; then - readarray -t vers < <(echo "$parsed") - local idx - res="none" - # since we've the pair version,branch alternated in not associative and one-dimensional - # array, we've to simulate the association with length/2 trick - for idx in `seq 0 $((${#vers[*]}/2-1))`; do - semverParseInto "${vers[idx*2]}" MODULE_MAJOR MODULE_MINOR MODULE_PATCH MODULE_SPECIAL - if [[ $MODULE_MAJOR -eq $ACV_MAJOR && $MODULE_MINOR -le $ACV_MINOR ]]; then - res="${vers[idx*2+1]}" - v="${vers[idx*2]}" - fi - done +# Returns the default branch name of a GitHub repo in the azerothcore org. +# If the API call fails, defaults to "master". +function inst_get_default_branch() { + local repo="$1" + local def + def=$(curl --silent "https://api.github.com/repos/azerothcore/${repo}" \ + | "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.default_branch') + if [ -z "$def" ]; then + def="master" fi - - echo "$v" "$res" -} - -function inst_module_search { - - local res="$1" - local idx=0; - - if [ -z "$1" ]; then - echo "Type what to search or leave blank for full list" - read -p "Insert name: " res - fi - - local search="+$res" - - echo "Searching $res..." - echo ""; - - readarray -t MODS < <(curl --silent "https://api.github.com/search/repositories?q=org%3Aazerothcore${search}+fork%3Atrue+topic%3Acore-module+sort%3Astars&type=" \ - | "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.items.*.name') - while (( ${#MODS[@]} > idx )); do - mod="${MODS[idx++]}" - read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/azerothcore/$mod/master/acore-module.json") - - if [[ "$b" != "none" ]]; then - echo "-> $mod (tested with AC version: $v)" - else - echo "-> $mod (no revision available for AC v$AC_VERSION, it could not work!)" - fi - done - - echo ""; - echo ""; -} - -function inst_module_install { - local res - if [ -z "$1" ]; then - echo "Type the name of the module to install" - read -p "Insert name: " res - else - res="$1" - fi - - read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/azerothcore/$res/master/acore-module.json") - - if [[ "$b" != "none" ]]; then - Joiner:add_repo "https://github.com/azerothcore/$res" "$res" "$b" && echo "Done, please re-run compiling and db assembly. Read instruction on module repository for more information" - else - echo "Cannot install $res module: it doesn't exists or no version compatible with AC v$ACORE_VERSION are available" - fi - - echo ""; - echo ""; -} - -function inst_module_update { - local res; - local _tmp; - local branch; - local p; - - if [ -z "$1" ]; then - echo "Type the name of the module to update" - read -p "Insert name: " res - else - res="$1" - fi - - _tmp=$PWD - - if [ -d "$J_PATH_MODULES/$res/" ]; then - read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/azerothcore/$res/master/acore-module.json") - - cd "$J_PATH_MODULES/$res/" - - # use current branch if something wrong with json - if [[ "$v" == "none" || "$v" == "not-defined" ]]; then - b=`git rev-parse --abbrev-ref HEAD` - fi - - Joiner:upd_repo "https://github.com/azerothcore/$res" "$res" "$b" && echo "Done, please re-run compiling and db assembly" || echo "Cannot update" - cd $_tmp - else - echo "Cannot update! Path doesn't exist" - fi; - - echo ""; - echo ""; -} - -function inst_module_remove { - if [ -z "$1" ]; then - echo "Type the name of the module to remove" - read -p "Insert name: " res - else - res="$1" - fi - - Joiner:remove "$res" && echo "Done, please re-run compiling" || echo "Cannot remove" - - echo ""; - echo ""; + echo "$def" } +# ============================================================================= +# Module Management System +# ============================================================================= +# Load the module manager functions from the dedicated modules-manager directory +source "$AC_PATH_INSTALLER/includes/modules-manager/modules.sh" function inst_simple_restarter { echo "Running $1 ..." @@ -292,4 +178,4 @@ function inst_download_client_data { && echo "unzip downloaded file in $path..." && unzip -q -o "$zipPath" -d "$path/" \ && echo "Remove downloaded file" && rm "$zipPath" \ && echo "INSTALLED_VERSION=$VERSION" > "$dataVersionFile" -} +} \ No newline at end of file diff --git a/apps/installer/includes/modules-manager/README.md b/apps/installer/includes/modules-manager/README.md new file mode 100644 index 000000000..f478035be --- /dev/null +++ b/apps/installer/includes/modules-manager/README.md @@ -0,0 +1,188 @@ +# AzerothCore Module Manager + +This directory contains the module management system for AzerothCore, providing advanced functionality for installing, updating, and managing server modules. + +## 🚀 Features + +- **Advanced Syntax**: Support for `repo[:dirname][@branch[:commit]]` format +- **Cross-Format Recognition**: Intelligent matching across URLs, SSH, and simple names +- **Custom Directory Naming**: Prevent conflicts with custom directory names +- **Duplicate Prevention**: Smart detection and prevention of duplicate installations +- **Multi-Host Support**: GitHub, GitLab, and other Git hosts + +## 📁 File Structure + +``` +modules-manager/ +├── modules.sh # Core module management functions +└── README.md # This documentation file +``` + +## 🔧 Module Specification Syntax + +The module manager supports flexible syntax for specifying modules: + +### New Syntax Format +```bash +repo[:dirname][@branch[:commit]] +``` + +### Examples + +| Specification | Description | +|---------------|-------------| +| `mod-transmog` | Simple module name, uses default branch and directory | +| `mod-transmog:my-custom-dir` | Custom directory name | +| `mod-transmog@develop` | Specific branch | +| `mod-transmog:custom@develop:abc123` | Custom directory, branch, and commit | +| `https://github.com/owner/repo.git@main` | Full URL with branch | +| `git@github.com:owner/repo.git:custom-dir` | SSH URL with custom directory | + +## 🎯 Usage Examples + +### Installing Modules + +```bash +# Simple module installation +./acore.sh module install mod-transmog + +# Install with custom directory name +./acore.sh module install mod-transmog:my-transmog-dir + +# Install specific branch +./acore.sh module install mod-transmog@develop + +# Install with full specification +./acore.sh module install mod-transmog:custom-dir@develop:abc123 + +# Install from URL +./acore.sh module install https://github.com/azerothcore/mod-transmog.git@main + +# Install multiple modules +./acore.sh module install mod-transmog mod-eluna:custom-eluna + +# Install all modules from list +./acore.sh module install --all +``` + +### Updating Modules + +```bash +# Update specific module +./acore.sh module update mod-transmog + +# Update all modules +./acore.sh module update --all + +# Update with branch specification +./acore.sh module update mod-transmog@develop +``` + +### Removing Modules + +```bash +# Remove by simple name (cross-format recognition) +./acore.sh module remove mod-transmog + +# Remove by URL (recognizes same module) +./acore.sh module remove https://github.com/azerothcore/mod-transmog.git + +# Remove multiple modules +./acore.sh module remove mod-transmog mod-eluna +``` + +### Searching Modules + +```bash +# Search for modules +./acore.sh module search transmog + +# Search with multiple terms +./acore.sh module search auction house + +# Show all available modules +./acore.sh module search +``` + +## 🔍 Cross-Format Recognition + +The system intelligently recognizes the same module across different specification formats: + +```bash +# These all refer to the same module: +mod-transmog +azerothcore/mod-transmog +https://github.com/azerothcore/mod-transmog.git +git@github.com:azerothcore/mod-transmog.git +``` + +This allows: +- Installing with one format and removing with another +- Preventing duplicates regardless of specification format +- Consistent module tracking across different input methods + +## 🛡️ Conflict Prevention + +The system prevents common conflicts: + +### Directory Conflicts +```bash +# If 'mod-transmog' directory already exists: +$ ./acore.sh module install mod-transmog:mod-transmog +Error: Directory 'mod-transmog' already exists. +Possible solutions: + 1. Use a different directory name: mod-transmog:my-custom-name + 2. Remove the existing directory first + 3. Use the update command if this is the same module +``` + +## 🔄 Integration + +### Including in Scripts +```bash +# Source the module functions +source "$AC_PATH_INSTALLER/includes/modules-manager/modules.sh" + +# Use module functions +inst_module_install "mod-transmog:custom-dir@develop" +``` + +### Testing +The module system is tested through the main installer test suite: +```bash +./apps/installer/test/test_module_commands.bats +``` + +## 📋 Module List Format + +Modules are tracked in `conf/modules.list` with the format: +``` +# Comments start with # +repo_reference branch commit + +# Examples: +azerothcore/mod-transmog master abc123def456 +https://github.com/custom/mod-custom.git develop def456abc789 +mod-eluna:custom-eluna-dir main 789abc123def +``` +## 🔧 Configuration + +### Environment Variables +- `MODULES_LIST_FILE`: Override default modules list path +- `J_PATH_MODULES`: Modules installation directory +- `AC_PATH_ROOT`: AzerothCore root path + +### Default Paths +- Modules list: `$AC_PATH_ROOT/conf/modules.list` + +## 🤝 Contributing + +When modifying the module manager: + +1. Maintain backwards compatibility +2. Update tests in `test_module_commands.bats` +3. Update this documentation +4. Test cross-format recognition thoroughly +5. Ensure helpful error messages + + diff --git a/apps/installer/includes/modules-manager/modules.sh b/apps/installer/includes/modules-manager/modules.sh new file mode 100644 index 000000000..93e643306 --- /dev/null +++ b/apps/installer/includes/modules-manager/modules.sh @@ -0,0 +1,735 @@ +#!/usr/bin/env bash + +# ============================================================================= +# AzerothCore Module Manager Functions +# ============================================================================= +# This file contains all functions related to module management in AzerothCore. +# It provides capabilities for installing, updating, removing, and searching +# modules with support for advanced syntax and intelligent cross-format matching. +# +# Main Features: +# - Advanced syntax: repo[:dirname][@branch[:commit]] +# - Legacy compatibility: repo:branch:commit +# - Cross-format module recognition (URLs, SSH, simple names) +# - Custom directory naming to prevent conflicts +# - Intelligent duplicate prevention +# +# Usage: +# source "path/to/modules.sh" +# inst_module_install "mod-transmog:my-custom-dir@develop:abc123" +# +# ============================================================================= + +# Dispatcher for the unified `module` command. +# Usage: ./acore.sh module [args...] +function inst_module() { + # Normalize arguments into an array + local tokens=() + read -r -a tokens <<< "$*" + local cmd="${tokens[0]}" + local args=("${tokens[@]:1}") + + case "$cmd" in + ""|"help"|"-h"|"--help") + echo "Usage:" + echo " ./acore.sh module search [terms...]" + echo " ./acore.sh module install [--all | modules...]" + echo " modules can be specified as: name[:branch[:commit]]" + echo " ./acore.sh module update [modules...]" + echo " ./acore.sh module remove [modules...]" + ;; + "search"|"s") + inst_module_search "${args[@]}" + ;; + "install"|"i") + inst_module_install "${args[@]}" + ;; + "update"|"u") + inst_module_update "${args[@]}" + ;; + "remove"|"r") + inst_module_remove "${args[@]}" + ;; + *) + echo "Unknown subcommand: $cmd" + echo "Try: ./acore.sh module help" + ;; + esac +} + +# ============================================================================= +# Module Specification Parsing +# ============================================================================= + +# Parse a module spec with advanced syntax: +# - New syntax: repo[:dirname][@branch[:commit]] +# +# Examples: +# "mod-transmog" -> uses default branch, directory name = mod-transmog +# "mod-transmog:custom-dir" -> uses default branch, directory name = custom-dir +# "mod-transmog@develop" -> uses develop branch, directory name = mod-transmog +# "mod-transmog:custom-dir@develop:abc123" -> custom directory, develop branch, specific commit +# +# Output: "repo_ref owner name branch commit url dirname" +function inst_parse_module_spec() { + local spec="$1" + + local dirname="" branch="" commit="" repo_part="" + + # Parse the new syntax: repo[:dirname][@branch[:commit]] + + # First, extract custom directory name if present (format: repo:dirname@branch) + local repo_with_branch="$spec" + if [[ "$spec" =~ ^([^@:]+):([^@:]+)(@.*)?$ ]]; then + repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" + dirname="${BASH_REMATCH[2]}" + fi + + # Now parse branch and commit from the repo part + if [[ "$repo_with_branch" =~ ^([^@]+)@([^:]+)(:(.+))?$ ]]; then + repo_part="${BASH_REMATCH[1]}" + branch="${BASH_REMATCH[2]}" + commit="${BASH_REMATCH[4]:-}" + else + repo_part="$repo_with_branch" + fi + + # Normalize repo reference and extract owner/name. + local repo_ref owner name url owner_repo + repo_ref="$repo_part" + + # If repo_ref is a URL, extract owner/name from path when possible + if [[ "$repo_ref" =~ :// ]] || [[ "$repo_ref" =~ ^git@ ]]; then + # Extract owner/name (last two path components) + owner_repo=$(echo "$repo_ref" | sed -E 's#(git@[^:]+:|https?://[^/]+/|ssh://[^/]+/)?(.*?)(\.git)?$#\2#') + owner="$(echo "$owner_repo" | awk -F'/' '{print $(NF-1)}')" + name="$(echo "$owner_repo" | awk -F'/' '{print $NF}' | sed -E 's/\.git$//')" + else + owner_repo="$repo_ref" + if [[ "$owner_repo" == *"/"* ]]; then + owner="$(echo "$owner_repo" | cut -d'/' -f1)" + name="$(echo "$owner_repo" | cut -d'/' -f2)" + else + owner="azerothcore" + name="$owner_repo" + repo_ref="$owner/$name" + fi + fi + + # Build URL only if repo_ref is not already a URL + if [[ "$repo_ref" =~ :// ]] || [[ "$repo_ref" =~ ^git@ ]]; then + url="$repo_ref" + else + url="https://github.com/${repo_ref}" + fi + + # Use custom dirname if provided, otherwise default to module name + if [ -z "$dirname" ]; then + dirname="$name" + fi + + echo "$repo_ref" "$owner" "$name" "${branch:--}" "${commit:--}" "$url" "$dirname" +} + +# ============================================================================= +# Cross-Format Module Recognition +# ============================================================================= + +# Extract owner/name from any repository reference for intelligent matching. +# This enables recognizing the same module regardless of specification format. +# +# Supported formats: +# - GitHub HTTPS: https://github.com/owner/name.git +# - GitHub SSH: git@github.com:owner/name.git +# - GitLab HTTPS: https://gitlab.com/owner/name.git +# - Owner/name: owner/name +# - Simple name: mod-name (assumes azerothcore namespace) +# +# Returns: "owner/name" format for consistent comparison +function inst_extract_owner_name { + local repo_ref="$1" + + # For URLs, don't remove dirname suffix since : is part of the URL + local base_ref="$repo_ref" + if [[ ! "$repo_ref" =~ :// ]] && [[ ! "$repo_ref" =~ ^git@ ]]; then + # Only remove dirname suffix for non-URL formats + base_ref="${repo_ref%%:*}" + fi + + if [[ "$base_ref" =~ ^https?://github\.com/([^/]+)/([^/]+)(\.git)?(/.*)?$ ]]; then + # HTTPS URL format - check this first before owner/name pattern + local name="${BASH_REMATCH[2]}" + name="${name%.git}" # Remove .git suffix if present + echo "${BASH_REMATCH[1]}/$name" + elif [[ "$base_ref" =~ ^https?://gitlab\.com/([^/]+)/([^/]+)(\.git)?(/.*)?$ ]]; then + # GitLab URL format + local name="${BASH_REMATCH[2]}" + name="${name%.git}" # Remove .git suffix if present + echo "${BASH_REMATCH[1]}/$name" + elif [[ "$base_ref" =~ ^git@github\.com:([^/]+)/([^/]+)(\.git)?$ ]]; then + # SSH URL format + local name="${BASH_REMATCH[2]}" + name="${name%.git}" # Remove .git suffix if present + echo "${BASH_REMATCH[1]}/$name" + elif [[ "$base_ref" =~ ^[^/]+/[^/]+$ ]]; then + # Format: owner/name (check after URL patterns) + echo "$base_ref" + elif [[ "$base_ref" =~ ^(mod-|module-)?([a-zA-Z0-9-]+)$ ]]; then + # Simple module name, assume azerothcore namespace + local modname="${BASH_REMATCH[2]}" + if [[ "$base_ref" == mod-* ]]; then + modname="$base_ref" + else + modname="mod-$modname" + fi + echo "azerothcore/$modname" + else + # Unknown format, return as-is + echo "$base_ref" + fi +} + +# ============================================================================= +# Module List Management +# ============================================================================= + +# Returns path to modules list file (configurable via MODULES_LIST_FILE). +function inst_modules_list_path() { + local path="${MODULES_LIST_FILE:-"$AC_PATH_ROOT/conf/modules.list"}" + echo "$path" +} + +# Ensure the modules list file exists and its directory is created. +function inst_mod_list_ensure() { + local file + file="$(inst_modules_list_path)" + mkdir -p "$(dirname "$file")" + [ -f "$file" ] || touch "$file" +} + +# Read modules list into stdout as triplets: "name branch commit" +# Skips comments (# ...) and blank lines. +function inst_mod_list_read() { + local file + file="$(inst_modules_list_path)" + [ -f "$file" ] || return 0 + # shellcheck disable=SC2013 + while IFS= read -r line; do + [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue + echo "$line" + done < "$file" +} + +# Add or update an entry in the list: repo_ref branch commit +# Removes any existing entries with the same owner/name to avoid duplicates +function inst_mod_list_upsert() { + local repo_ref="$1"; shift + local branch="$1"; shift + local commit="$1"; shift + local target_owner_name + target_owner_name=$(inst_extract_owner_name "$repo_ref") + + inst_mod_list_ensure + local file tmp tmp_uns tmp_sorted + file="$(inst_modules_list_path)" + tmp="${file}.tmp" + tmp_uns="${file}.unsorted" + tmp_sorted="${file}.sorted" + + # Build a list without existing duplicates + : > "$tmp_uns" + while read -r existing_ref existing_branch existing_commit; do + [[ -z "$existing_ref" ]] && continue + local existing_owner_name + existing_owner_name=$(inst_extract_owner_name "$existing_ref") + if [[ "$existing_owner_name" != "$target_owner_name" ]]; then + echo "$existing_ref $existing_branch $existing_commit" >> "$tmp_uns" + fi + done < <(inst_mod_list_read) + # Add/replace the new entry (preserving original repo_ref format) + echo "$repo_ref $branch $commit" >> "$tmp_uns" + + # Create key-prefixed lines to sort by normalized owner/name + : > "$tmp" + while read -r r b c; do + [[ -z "$r" ]] && continue + local k + k=$(inst_extract_owner_name "$r") + printf "%s\t%s %s %s\n" "$k" "$r" "$b" "$c" >> "$tmp" + done < "$tmp_uns" + + # Stable sort by key and strip the key + LC_ALL=C sort -t $'\t' -k1,1 -s "$tmp" | cut -f2- > "$tmp_sorted" && mv "$tmp_sorted" "$file" + rm -f "$tmp" "$tmp_uns" "$tmp_sorted" 2>/dev/null || true +} + +# Remove an entry from the list by matching owner/name. +# This allows removing modules regardless of how they were specified (URL vs owner/name) +function inst_mod_list_remove() { + local repo_ref="$1" + local target_owner_name + target_owner_name=$(inst_extract_owner_name "$repo_ref") + + local file + file="$(inst_modules_list_path)" + [ -f "$file" ] || return 0 + + local tmp_uns="${file}.unsorted" + local tmp="${file}.tmp" + local tmp_sorted="${file}.sorted" + + # Keep only lines where owner/name doesn't match + : > "$tmp_uns" + while read -r existing_ref existing_branch existing_commit; do + [[ -z "$existing_ref" ]] && continue + local existing_owner_name + existing_owner_name=$(inst_extract_owner_name "$existing_ref") + if [[ "$existing_owner_name" != "$target_owner_name" ]]; then + echo "$existing_ref $existing_branch $existing_commit" >> "$tmp_uns" + fi + done < <(inst_mod_list_read) + + # Key-prefix and sort for deterministic alphabetical order + : > "$tmp" + while read -r r b c; do + [[ -z "$r" ]] && continue + local k + k=$(inst_extract_owner_name "$r") + printf "%s\t%s %s %s\n" "$k" "$r" "$b" "$c" >> "$tmp" + done < "$tmp_uns" + + LC_ALL=C sort -t $'\t' -k1,1 -s "$tmp" | cut -f2- > "$tmp_sorted" && mv "$tmp_sorted" "$file" + rm -f "$tmp" "$tmp_uns" "$tmp_sorted" 2>/dev/null || true +} + +# Check if a module is already installed by comparing owner/name +# Returns the existing repo_ref if found, empty if not found +function inst_mod_is_installed() { + local spec="$1" + local target_owner_name + target_owner_name=$(inst_extract_owner_name "$spec") + + # Use a different approach: read into a variable first, then process + local modules_content + modules_content=$(inst_mod_list_read) + + # Process each line + while IFS= read -r line; do + [[ -z "$line" ]] && continue + read -r repo_ref branch commit <<< "$line" + local existing_owner_name + existing_owner_name=$(inst_extract_owner_name "$repo_ref") + if [[ "$existing_owner_name" == "$target_owner_name" ]]; then + echo "$repo_ref" # Return the existing entry + return 0 + fi + done <<< "$modules_content" + + return 1 +} + +# ============================================================================= +# Conflict Detection and Validation +# ============================================================================= + +# Check for module directory conflicts with helpful error messages +function inst_check_module_conflict { + local dirname="$1" + local repo_ref="$2" + + if [ -d "$J_PATH_MODULES/$dirname" ]; then + echo "Error: Directory '$dirname' already exists." + echo "Possible solutions:" + echo " 1. Use a different directory name: $repo_ref:my-custom-name" + echo " 2. Remove the existing directory first" + echo " 3. Use the update command if this is the same module" + return 1 + fi + return 0 +} + +# ============================================================================= +# Module Operations +# ============================================================================= + +# Get version and branch information from acore-module.json +function inst_getVersionBranch() { + local res="master" + local v="not-defined" + local MODULE_MAJOR=0 + local MODULE_MINOR=0 + local MODULE_PATCH=0 + local MODULE_SPECIAL=0; + local ACV_MAJOR=0 + local ACV_MINOR=0 + local ACV_PATCH=0 + local ACV_SPECIAL=0; + local curldata=$(curl -f --silent -H 'Cache-Control: no-cache' "$1" || echo "{}") + local parsed=$(echo "$curldata" | "$AC_PATH_DEPS/jsonpath/JSONPath.sh" -b '$.compatibility.*.[version,branch]') + + semverParseInto "$ACORE_VERSION" ACV_MAJOR ACV_MINOR ACV_PATCH ACV_SPECIAL + + if [[ ! -z "$parsed" ]]; then + readarray -t vers < <(echo "$parsed") + local idx + res="none" + # since we've the pair version,branch alternated in not associative and one-dimensional + # array, we've to simulate the association with length/2 trick + for idx in `seq 0 $((${#vers[*]}/2-1))`; do + semverParseInto "${vers[idx*2]}" MODULE_MAJOR MODULE_MINOR MODULE_PATCH MODULE_SPECIAL + if [[ $MODULE_MAJOR -eq $ACV_MAJOR && $MODULE_MINOR -le $ACV_MINOR ]]; then + res="${vers[idx*2+1]}" + v="${vers[idx*2]}" + fi + done + fi + + echo "$v" "$res" +} + +# Search for modules in the AzerothCore repository +function inst_module_search { + # Accept 0..N search terms; if none provided, prompt the user. + local terms=("$@") + if [ ${#terms[@]} -eq 0 ]; then + echo "Type what to search (blank for full list)" + read -p "Insert name(s): " _line + if [ -n "$_line" ]; then + read -r -a terms <<< "$_line" + fi + fi + + local CATALOG_URL="https://www.azerothcore.org/data/catalogue.json" + + echo "Searching ${terms[*]}..." + echo "" + + # Build candidate list from catalogue (full_name = owner/repo) + local MODS=() + if command -v jq >/dev/null 2>&1; then + mapfile -t MODS < <(curl --silent -L "$CATALOG_URL" \ + | jq -r ' + [ .. | objects + | select(.full_name and .topics) + | select(.topics | index("azerothcore-module")) + ] + | unique_by(.full_name) + | sort_by(.stargazers_count // 0) | reverse + | .[].full_name + ') + else + # Fallback without jq: best-effort extraction of owner/repo + mapfile -t MODS < <(curl --silent -L "$CATALOG_URL" \ + | grep -oE '\"full_name\"\\s*:\\s*\"[^\"/[:space:]]+/[^\"[:space:]]+\"' \ + | sed -E 's/.*\"full_name\"\\s*:\\s*\"([^\"]+)\".*/\\1/' \ + | sort -u) + fi + + # Local AND filter on user terms (case-insensitive) against full_name + if (( ${#terms[@]} > 0 )); then + local filtered=() + local item + for item in "${MODS[@]}"; do + local keep=1 + local lower="${item,,}" + local t + for t in "${terms[@]}"; do + [ -z "$t" ] && continue + if [[ "$lower" != *"${t,,}"* ]]; then + keep=0; break + fi + done + (( keep )) && filtered+=("$item") + done + MODS=("${filtered[@]}") + fi + + if (( ${#MODS[@]} == 0 )); then + echo "No results." + echo "" + return 0 + fi + + local idx=0 + while (( ${#MODS[@]} > idx )); do + local mod_full="${MODS[idx++]}" # owner/repo + local mod="${mod_full##*/}" # repo name only for display + read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${mod_full}/master/acore-module.json") + + if [[ "$b" != "none" ]]; then + echo "-> $mod (tested with AC version: $v)" + else + echo "-> $mod (NOTE: The module latest tested AC revision is Unknown)" + fi + done + + echo "" + echo "" +} + + +# Install one or more modules with advanced syntax support +function inst_module_install { + # Support multiple modules and the --all flag; prompt if none specified. + local args=("$@") + local use_all=false + if [ ${#args[@]} -gt 0 ] && { [ "${args[0]}" = "--all" ] || [ "${args[0]}" = "-a" ]; }; then + use_all=true + shift || true + fi + + local modules=("$@") + + echo "Installing modules: ${modules[*]}" + + if $use_all; then + # Install all modules from the list (respecting recorded branch and commit). + inst_mod_list_ensure + local line repo_ref branch commit url owner modname dirname + + # First pass: detect duplicate target directories (flat structure) + declare -A _seen _first + local dup_error=0 + while read -r repo_ref branch commit; do + [ -z "$repo_ref" ] && continue + parsed_output=$(inst_parse_module_spec "$repo_ref") + IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" + # dirname defaults to repo name; flat install path uses dirname only + if [[ -n "${_seen[$dirname]:-}" ]]; then + echo "Error: duplicate module target directory '$dirname' detected in modules.list:" + echo " - ${_first[$dirname]}" + echo " - ${repo_ref}" + echo "Use a custom folder name to disambiguate, e.g.: ${repo_ref}:$dirname-alt" + dup_error=1 + else + _seen[$dirname]=1 + _first[$dirname]="$repo_ref" + fi + done < <(inst_mod_list_read) + if [[ "$dup_error" -ne 0 ]]; then + return 1 + fi + + # Second pass: install in flat modules directory (no owner subfolders) + while read -r repo_ref branch commit; do + [ -z "$repo_ref" ] && continue + parsed_output=$(inst_parse_module_spec "$repo_ref") + IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" + + if [ -d "$J_PATH_MODULES/$dirname" ]; then + echo "[$repo_ref] Already installed (skipping)." + continue + fi + + if Joiner:add_repo "$url" "$dirname" "$branch" ""; then + # Checkout the recorded commit if present + if [ -n "$commit" ]; then + git -C "$J_PATH_MODULES/$dirname" fetch --all --quiet || true + if git -C "$J_PATH_MODULES/$dirname" rev-parse --verify "$commit" >/dev/null 2>&1; then + git -C "$J_PATH_MODULES/$dirname" checkout --quiet "$commit" + fi + fi + local curCommit + curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$branch" "$curCommit" + echo "[$repo_ref] Installed." + else + echo "[$repo_ref] Install failed." + exit 1; + fi + done < <(inst_mod_list_read) + else + # Install specified modules; prompt if none specified. + if [ ${#modules[@]} -eq 0 ]; then + echo "Type the name(s) of the module(s) to install" + read -p "Insert name(s): " _line + read -r -a modules <<< "$_line" + fi + + local spec name override_branch override_commit v b def curCommit existing_repo_ref dirname + for spec in "${modules[@]}"; do + [ -z "$spec" ] && continue + + # Check if module is already installed (by owner/name matching) + existing_repo_ref=$(inst_mod_is_installed "$spec" || true) + if [ -n "$existing_repo_ref" ]; then + echo "[$spec] Already installed as [$existing_repo_ref] (skipping)." + continue + fi + + parsed_output=$(inst_parse_module_spec "$spec") + IFS=' ' read -r repo_ref owner modname override_branch override_commit url dirname <<< "$parsed_output" + [ -z "$repo_ref" ] && continue + + # Check for directory conflicts with custom directory names + if ! inst_check_module_conflict "$dirname" "$repo_ref"; then + continue + fi + + # override_branch takes precedence; otherwise consult acore-module.json on azerothcore unless repo_ref contains owner or URL + if [ -n "$override_branch" ] && [ "$override_branch" != "-" ]; then + b="$override_branch" + else + # For GitHub repositories, use raw.githubusercontent.com to check acore-module.json + if [[ "$url" =~ github.com ]] || [[ "$repo_ref" =~ ^[^/]+/[^/]+$ ]]; then + read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${owner}/${modname}/master/acore-module.json") + else + # Unknown host: try the repository URL as-is (may fail) + read v b < <(inst_getVersionBranch "${url}/master/acore-module.json") + fi + if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then + def="$(inst_get_default_branch "$repo_ref")" + echo "Warning: $repo_ref has no compatible acore-module.json; installing from branch '$def' (latest commit)." + b="$def" + fi + fi + + # Use flat directory structure with custom directory name + if [ -d "$J_PATH_MODULES/$dirname" ]; then + echo "[$repo_ref] Already installed (skipping)." + curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$b" "$curCommit" + continue + fi + + if Joiner:add_repo "$url" "$dirname" "$b" ""; then + # If a commit was provided, try to checkout it + if [ -n "$override_commit" ] && [ "$override_commit" != "-" ]; then + git -C "$J_PATH_MODULES/$dirname" fetch --all --quiet || true + if git -C "$J_PATH_MODULES/$dirname" rev-parse --verify "$override_commit" >/dev/null 2>&1; then + git -C "$J_PATH_MODULES/$dirname" checkout --quiet "$override_commit" + else + echo "[$repo_ref] Warning: provided commit '$override_commit' not found; staying on branch '$b' HEAD." + fi + fi + curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$b" "$curCommit" + echo "[$repo_ref] Installed in '$dirname'. Please re-run compiling and db assembly." + else + echo "[$repo_ref] Install failed or module not found" + exit 1; + fi + done + fi + + echo "" + echo "" +} + +# Update one or more modules +function inst_module_update { + # Support multiple modules and the --all flag; prompt if none specified. + local args=("$@") + local use_all=false + if [ ${#args[@]} -gt 0 ] && { [ "${args[0]}" = "--all" ] || [ "${args[0]}" = "-a" ]; }; then + use_all=true + shift || true + fi + + local _tmp=$PWD + + if $use_all; then + local line repo_ref branch commit newCommit owner modname url dirname + while read -r repo_ref branch commit; do + [ -z "$repo_ref" ] && continue + parsed_output=$(inst_parse_module_spec "$repo_ref") + IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" + + dirname="${dirname:-$modname}" + if [ ! -d "$J_PATH_MODULES/$dirname/" ]; then + echo "[$repo_ref] Not installed locally, skipping." + continue + fi + + if Joiner:upd_repo "$url" "$dirname" "$branch" ""; then + newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$branch" "$newCommit" + echo "[$repo_ref] Updated to latest commit on '$branch'." + else + echo "[$repo_ref] Cannot update" + fi + done < <(inst_mod_list_read) + else + local modules=("$@") + if [ ${#modules[@]} -eq 0 ]; then + echo "Type the name(s) of the module(s) to update" + read -p "Insert name(s): " _line + read -r -a modules <<< "$_line" + fi + + local spec repo_ref override_branch override_commit owner modname url dirname v b branch def newCommit + for spec in "${modules[@]}"; do + [ -z "$spec" ] && continue + parsed_output=$(inst_parse_module_spec "$spec") + IFS=' ' read -r repo_ref owner modname override_branch override_commit url dirname <<< "$parsed_output" + + dirname="${dirname:-$modname}" + if [ -d "$J_PATH_MODULES/$dirname/" ]; then + # determine preferred branch if not provided + if [ -n "$override_branch" ] && [ "$override_branch" != "-" ]; then + b="$override_branch" + else + # try reading acore-module.json for this repo + if [[ "$url" =~ github.com ]]; then + read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${owner}/${modname}/master/acore-module.json") + else + read v b < <(inst_getVersionBranch "${url}/master/acore-module.json") + fi + if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then + if branch=$(git -C "$J_PATH_MODULES/$dirname" rev-parse --abbrev-ref HEAD 2>/dev/null); then + echo "Warning: $repo_ref has no compatible acore-module.json; updating current branch '$branch'." + b="$branch" + else + def="$(inst_get_default_branch "$repo_ref")" + echo "Warning: $repo_ref has no compatible acore-module.json and no git branch detected; updating default branch '$def'." + b="$def" + fi + fi + fi + + if Joiner:upd_repo "$url" "$dirname" "$b" ""; then + newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") + inst_mod_list_upsert "$repo_ref" "$b" "$newCommit" + echo "[$repo_ref] Done, please re-run compiling and db assembly" + else + echo "[$repo_ref] Cannot update" + fi + else + echo "[$repo_ref] Cannot update! Path doesn't exist ($J_PATH_MODULES/$dirname/)" + fi + done + fi + + echo "" + echo "" +} + +# Remove one or more modules +function inst_module_remove { + # Support multiple modules; prompt if none specified. + local modules=("$@") + if [ ${#modules[@]} -eq 0 ]; then + echo "Type the name(s) of the module(s) to remove" + read -p "Insert name(s): " _line + read -r -a modules <<< "$_line" + fi + + local spec repo_ref owner modname url override_branch override_commit dirname + for spec in "${modules[@]}"; do + [ -z "$spec" ] && continue + parsed_output=$(inst_parse_module_spec "$spec") + IFS=' ' read -r repo_ref owner modname override_branch override_commit url dirname <<< "$parsed_output" + [ -z "$repo_ref" ] && continue + + dirname="${dirname:-$modname}" + if Joiner:remove "$dirname" ""; then + inst_mod_list_remove "$repo_ref" + echo "[$repo_ref] Done, please re-run compiling" + else + echo "[$repo_ref] Cannot remove" + fi + done + + echo "" + echo "" +} diff --git a/apps/installer/main.sh b/apps/installer/main.sh index 396dafaad..5911ccbf7 100644 --- a/apps/installer/main.sh +++ b/apps/installer/main.sh @@ -6,22 +6,22 @@ source "$CURRENT_PATH/includes/includes.sh" PS3='[Please enter your choice]: ' options=( - "init (i): First Installation" # 1 - "install-deps (d): Configure OS dep" # 2 - "pull (u): Update Repository" # 3 - "reset (r): Reset & Clean Repository" # 4 - "compiler (c): Run compiler tool" # 5 - "module-search (ms): Module Search by keyword" # 6 - "module-install (mi): Module Install by name" # 7 - "module-update (mu): Module Update by name" # 8 - "module-remove: (mr): Module Remove by name" # 9 - "client-data: (gd): download client data from github repository (beta)" # 10 - "run-worldserver (rw): execute a simple restarter for worldserver" # 11 - "run-authserver (ra): execute a simple restarter for authserver" # 12 - "docker (dr): Run docker tools" # 13 - "version (v): Show AzerothCore version" # 14 - "service-manager (sm): Run service manager to run authserver and worldserver in background" # 15 - "quit: Exit from this menu" # 16 + "init (i): First Installation" + "install-deps (d): Configure OS dep" + "pull (u): Update Repository" + "reset (r): Reset & Clean Repository" + "compiler (c): Run compiler tool" + "module (m): Module manager (search/install/update/remove)" + "module-install (mi): Module Install by name [DEPRECATED]" + "module-update (mu): Module Update by name [DEPRECATED]" + "module-remove: (mr): Module Remove by name [DEPRECATED]" + "client-data: (gd): download client data from github repository (beta)" + "run-worldserver (rw): execute a simple restarter for worldserver" + "run-authserver (ra): execute a simple restarter for authserver" + "docker (dr): Run docker tools" + "version (v): Show AzerothCore version" + "service-manager (sm): Run service manager to run authserver and worldserver in background" + "quit (q): Exit from this menu" ) function _switch() { @@ -29,56 +29,64 @@ function _switch() { _opt="$2" case $_reply in - ""|"i"|"init"|"1") + ""|"i"|"init") inst_allInOne ;; - ""|"d"|"install-deps"|"2") + ""|"d"|"install-deps") inst_configureOS ;; - ""|"u"|"pull"|"3") + ""|"u"|"pull") inst_updateRepo ;; - ""|"r"|"reset"|"4") + ""|"r"|"reset") inst_resetRepo ;; - ""|"c"|"compiler"|"5") + ""|"c"|"compiler") bash "$AC_PATH_APPS/compiler/compiler.sh" $_opt ;; - ""|"ms"|"module-search"|"6") - inst_module_search "$_opt" + ""|"m"|"module") + # Unified module command: supports subcommands search|install|update|remove + inst_module "${@:2}" ;; - ""|"mi"|"module-install"|"7") - inst_module_install "$_opt" + ""|"ms"|"module-search") + echo "[DEPRECATED] Use: ./acore.sh module search " + inst_module_search "${@:2}" ;; - ""|"mu"|"module-update"|"8") - inst_module_update "$_opt" + ""|"mi"|"module-install") + echo "[DEPRECATED] Use: ./acore.sh module install " + inst_module_install "${@:2}" ;; - ""|"mr"|"module-remove"|"9") - inst_module_remove "$_opt" + ""|"mu"|"module-update") + echo "[DEPRECATED] Use: ./acore.sh module update " + inst_module_update "${@:2}" ;; - ""|"gd"|"client-data"|"10") + ""|"mr"|"module-remove") + echo "[DEPRECATED] Use: ./acore.sh module remove " + inst_module_remove "${@:2}" + ;; + ""|"gd"|"client-data") inst_download_client_data ;; - ""|"rw"|"run-worldserver"|"11") + ""|"rw"|"run-worldserver") inst_simple_restarter worldserver ;; - ""|"ra"|"run-authserver"|"12") + ""|"ra"|"run-authserver") inst_simple_restarter authserver ;; - ""|"dr"|"docker"|"13") + ""|"dr"|"docker") DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "${@:2}" exit ;; - ""|"v"|"version"|"14") + ""|"v"|"version") # denoRunFile "$AC_PATH_APPS/installer/main.ts" "version" printf "AzerothCore Rev. %s\n" "$ACORE_VERSION" exit ;; - ""|"sm"|"service-manager"|"15") + ""|"sm"|"service-manager") bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "${@:2}" exit ;; - ""|"quit"|"16") + ""|"q"|"quit") echo "Goodbye!" exit ;; @@ -102,4 +110,5 @@ do _switch $REPLY break done + echo "opt: $opt" done diff --git a/apps/installer/test/bats.conf b/apps/installer/test/bats.conf new file mode 100644 index 000000000..8034254e7 --- /dev/null +++ b/apps/installer/test/bats.conf @@ -0,0 +1,14 @@ +# BATS Test Configuration + +# Set test timeout (in seconds) +export BATS_TEST_TIMEOUT=30 + +# Enable verbose output for debugging +export BATS_VERBOSE_RUN=1 + +# Test output format +export BATS_FORMATTER=pretty + +# Enable colored output +export BATS_NO_PARALLELIZE_ACROSS_FILES=1 +export BATS_NO_PARALLELIZE_WITHIN_FILE=1 diff --git a/apps/installer/test/test_module_commands.bats b/apps/installer/test/test_module_commands.bats new file mode 100755 index 000000000..40c284941 --- /dev/null +++ b/apps/installer/test/test_module_commands.bats @@ -0,0 +1,354 @@ +#!/usr/bin/env bats + +# Tests for installer module commands (search/install/update/remove) +# Focused on installer:module install behavior using a mocked joiner + +load '../../test-framework/bats_libs/acore-support' +load '../../test-framework/bats_libs/acore-assert' + +setup() { + acore_test_setup + # Point to the installer src directory (not needed in this test) + + # Set installer/paths environment for the test + export AC_PATH_APPS="$TEST_DIR/apps" + export AC_PATH_ROOT="$TEST_DIR" + export AC_PATH_DEPS="$TEST_DIR/deps" + export AC_PATH_MODULES="$TEST_DIR/modules" + export MODULES_LIST_FILE="$TEST_DIR/conf/modules.list" + + # Create stubbed deps: joiner.sh (sourced by includes) and semver + mkdir -p "$TEST_DIR/deps/acore/joiner" + cat > "$TEST_DIR/deps/acore/joiner/joiner.sh" << 'EOF' +#!/usr/bin/env bash +# Stub joiner functions used by installer +Joiner:add_repo() { + # arguments: url name branch basedir + echo "ADD $@" > "$TEST_DIR/joiner_called.txt" + return 0 +} +Joiner:upd_repo() { + echo "UPD $@" > "$TEST_DIR/joiner_called.txt" + return 0 +} +Joiner:remove() { + echo "REM $@" > "$TEST_DIR/joiner_called.txt" + return 0 +} +EOF + chmod +x "$TEST_DIR/deps/acore/joiner/joiner.sh" + + mkdir -p "$TEST_DIR/deps/semver_bash" + # Minimal semver stub + cat > "$TEST_DIR/deps/semver_bash/semver.sh" << 'EOF' +#!/usr/bin/env bash +# semver stub +semver::satisfies() { return 0; } +EOF + chmod +x "$TEST_DIR/deps/semver_bash/semver.sh" + + # Provide a minimal compiler includes file expected by installer + mkdir -p "$TEST_DIR/apps/compiler/includes" + touch "$TEST_DIR/apps/compiler/includes/includes.sh" + + # Provide minimal bash_shared includes to satisfy installer include + mkdir -p "$TEST_DIR/apps/bash_shared" + cat > "$TEST_DIR/apps/bash_shared/includes.sh" << 'EOF' +#!/usr/bin/env bash +# minimal stub +EOF + + # Copy the real installer app into the test apps dir + mkdir -p "$TEST_DIR/apps" + cp -r "$(cd "$AC_TEST_ROOT/apps/installer" && pwd)" "$TEST_DIR/apps/installer" +} + +teardown() { + acore_test_teardown +} + +@test "module install should call joiner and record entry in modules list" { + cd "$TEST_DIR" + + # Source installer includes and call the install function directly to avoid menu interaction + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install example-module@main:abcd1234" + + # Check that joiner was called + [ -f "$TEST_DIR/joiner_called.txt" ] + grep -q "ADD" "$TEST_DIR/joiner_called.txt" + + # Check modules list was created and contains the repo_ref and branch + [ -f "$TEST_DIR/conf/modules.list" ] + grep -q "azerothcore/example-module main" "$TEST_DIR/conf/modules.list" +} + +@test "module install with owner/name format should work" { + cd "$TEST_DIR" + + # Test with owner/name format + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install myorg/mymodule" + + # Check that joiner was called with correct URL + [ -f "$TEST_DIR/joiner_called.txt" ] + grep -q "ADD https://github.com/myorg/mymodule mymodule" "$TEST_DIR/joiner_called.txt" + + # Check modules list contains the entry + [ -f "$TEST_DIR/conf/modules.list" ] + grep -q "myorg/mymodule" "$TEST_DIR/conf/modules.list" +} + +@test "module remove should call joiner remove and update modules list" { + cd "$TEST_DIR" + + # First install a module + bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install test-module" + + # Then remove it + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_remove test-module" + + # Check that joiner remove was called + [ -f "$TEST_DIR/joiner_called.txt" ] + # With flat structure, basedir is empty; ensure name is present + grep -q "REM test-module" "$TEST_DIR/joiner_called.txt" + + # Check modules list no longer contains the entry + [ -f "$TEST_DIR/conf/modules.list" ] + ! grep -q "azerothcore/test-module" "$TEST_DIR/conf/modules.list" +} + +# Tests for intelligent module management (duplicate prevention and cross-format removal) + +@test "inst_extract_owner_name should extract owner/name from various formats" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test simple name + run inst_extract_owner_name "mod-transmog" + [ "$output" = "azerothcore/mod-transmog" ] + + # Test owner/name format + run inst_extract_owner_name "azerothcore/mod-transmog" + [ "$output" = "azerothcore/mod-transmog" ] + + # Test HTTPS URL + run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog.git" + [ "$output" = "azerothcore/mod-transmog" ] + + # Test SSH URL + run inst_extract_owner_name "git@github.com:azerothcore/mod-transmog.git" + [ "$output" = "azerothcore/mod-transmog" ] + + # Test GitLab URL + run inst_extract_owner_name "https://gitlab.com/myorg/mymodule.git" + [ "$output" = "myorg/mymodule" ] +} + +@test "duplicate module entries should be prevented across different formats" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Add module via simple name + inst_mod_list_upsert "mod-transmog" "master" "abc123" + + # Verify it's in the list + grep -q "mod-transmog master abc123" "$TEST_DIR/conf/modules.list" + + # Add same module via owner/name format - should replace, not duplicate + inst_mod_list_upsert "azerothcore/mod-transmog" "dev" "def456" + + # Should only have one entry (the new one) + [ "$(grep -c "azerothcore/mod-transmog" "$TEST_DIR/conf/modules.list")" -eq 1 ] + grep -q "azerothcore/mod-transmog dev def456" "$TEST_DIR/conf/modules.list" + ! grep -q "mod-transmog master abc123" "$TEST_DIR/conf/modules.list" +} + +@test "module installed via URL should be recognized when checking with different formats" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Install via HTTPS URL + inst_mod_list_upsert "https://github.com/azerothcore/mod-transmog.git" "master" "abc123" + + # Should be detected as installed using simple name + run inst_mod_is_installed "mod-transmog" + [ "$status" -eq 0 ] + + # Should be detected as installed using owner/name + run inst_mod_is_installed "azerothcore/mod-transmog" + [ "$status" -eq 0 ] + + # Should be detected as installed using SSH URL + run inst_mod_is_installed "git@github.com:azerothcore/mod-transmog.git" + [ "$status" -eq 0 ] + + # Non-existent module should not be detected + run inst_mod_is_installed "mod-nonexistent" + [ "$status" -ne 0 ] +} + +@test "cross-format module removal should work" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Install via SSH URL + inst_mod_list_upsert "git@github.com:azerothcore/mod-transmog.git" "master" "abc123" + + # Verify it's installed + grep -q "git@github.com:azerothcore/mod-transmog.git" "$TEST_DIR/conf/modules.list" + + # Remove using simple name + inst_mod_list_remove "mod-transmog" + + # Should be completely removed + ! grep -q "azerothcore/mod-transmog" "$TEST_DIR/conf/modules.list" + ! grep -q "git@github.com:azerothcore/mod-transmog.git" "$TEST_DIR/conf/modules.list" +} + +@test "module installation should prevent duplicates when already installed" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Install via simple name first + inst_mod_list_upsert "mod-worldchat" "master" "abc123" + + # Try to install same module via URL - should detect it's already installed + run inst_mod_is_installed "https://github.com/azerothcore/mod-worldchat.git" + [ "$status" -eq 0 ] + + # Add via URL should replace the existing entry + inst_mod_list_upsert "https://github.com/azerothcore/mod-worldchat.git" "dev" "def456" + + # Should only have one entry + [ "$(grep -c "azerothcore/mod-worldchat" "$TEST_DIR/conf/modules.list")" -eq 1 ] + grep -q "https://github.com/azerothcore/mod-worldchat.git dev def456" "$TEST_DIR/conf/modules.list" +} + +@test "module update --all uses flat structure (no branch subfolders)" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Prepare modules.list with one entry and a matching local directory + mkdir -p "$TEST_DIR/conf" + echo "azerothcore/mod-transmog master abc123" > "$TEST_DIR/conf/modules.list" + mkdir -p "$TEST_DIR/modules/mod-transmog" + + # Run update all + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_update --all" + + # Verify Joiner:upd_repo received flat structure args (no basedir) + [ -f "$TEST_DIR/joiner_called.txt" ] + grep -q "UPD https://github.com/azerothcore/mod-transmog mod-transmog master" "$TEST_DIR/joiner_called.txt" +} + +@test "module update specific uses flat structure with override branch" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Create local directory so update proceeds + mkdir -p "$TEST_DIR/modules/mymodule" + + # Run update specifying owner/name and branch + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_update myorg/mymodule@dev" + + # Should call joiner with name 'mymodule' and branch 'dev' (no basedir) + [ -f "$TEST_DIR/joiner_called.txt" ] + grep -q "UPD https://github.com/myorg/mymodule mymodule dev" "$TEST_DIR/joiner_called.txt" +} + +@test "custom directory names should work with new syntax" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test parsing with custom directory name + run inst_parse_module_spec "mod-transmog:my-custom-dir@develop:abc123" + [ "$status" -eq 0 ] + # Should output: repo_ref owner name branch commit url dirname + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "azerothcore/mod-transmog" ] + [ "$owner" = "azerothcore" ] + [ "$name" = "mod-transmog" ] + [ "$branch" = "develop" ] + [ "$commit" = "abc123" ] + [ "$url" = "https://github.com/azerothcore/mod-transmog" ] + [ "$dirname" = "my-custom-dir" ] +} + +@test "directory conflict detection should work" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Create a fake existing directory + mkdir -p "$TEST_DIR/modules/existing-dir" + + # Should detect conflict + run inst_check_module_conflict "existing-dir" "mod-test" + [ "$status" -eq 1 ] + [[ "$output" =~ "Directory 'existing-dir' already exists" ]] + [[ "$output" =~ "Use a different directory name: mod-test:my-custom-name" ]] + + # Should not detect conflict for non-existing directory + run inst_check_module_conflict "non-existing-dir" "mod-test" + [ "$status" -eq 0 ] +} + +@test "module update should work with custom directories" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # First add module with custom directory to list + inst_mod_list_upsert "azerothcore/mod-transmog:custom-dir" "master" "abc123" + + # Create fake module directory structure + mkdir -p "$TEST_DIR/modules/custom-dir/.git" + echo "ref: refs/heads/master" > "$TEST_DIR/modules/custom-dir/.git/HEAD" + + # Mock git commands in the fake module directory + cat > "$TEST_DIR/modules/custom-dir/.git/config" << 'EOF' +[core] + repositoryformatversion = 0 + filemode = true + bare = false +[remote "origin"] + url = https://github.com/azerothcore/mod-transmog + fetch = +refs/heads/*:refs/remotes/origin/* +[branch "master"] + remote = origin + merge = refs/heads/master +EOF + + # Test update with custom directory should work + # Note: This would require more complex mocking for full integration test + # For now, just test the parsing recognizes the custom directory + run inst_parse_module_spec "azerothcore/mod-transmog:custom-dir" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$dirname" = "custom-dir" ] +} + +@test "URL formats should be properly normalized" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test various URL formats produce same owner/name + run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog" + local url_format="$output" + + run inst_extract_owner_name "https://github.com/azerothcore/mod-transmog.git" + local url_git_format="$output" + + run inst_extract_owner_name "git@github.com:azerothcore/mod-transmog.git" + local ssh_format="$output" + + run inst_extract_owner_name "azerothcore/mod-transmog" + local owner_name_format="$output" + + run inst_extract_owner_name "mod-transmog" + local simple_format="$output" + + # All should normalize to the same owner/name + [ "$url_format" = "azerothcore/mod-transmog" ] + [ "$url_git_format" = "azerothcore/mod-transmog" ] + [ "$ssh_format" = "azerothcore/mod-transmog" ] + [ "$owner_name_format" = "azerothcore/mod-transmog" ] + [ "$simple_format" = "azerothcore/mod-transmog" ] +} diff --git a/conf/dist/config.sh b/conf/dist/config.sh index df7ddd3fc..2e105fd44 100644 --- a/conf/dist/config.sh +++ b/conf/dist/config.sh @@ -149,4 +149,17 @@ export CPUPROFILESIGNAL=${CPUPROFILESIGNAL:-12} # Other values for HEAPCHECK: minimal, normal (equivalent to "1"), strict, draconian #export HEAPCHECK=${HEAPCHECK:-normal} +############################################## +# +# MODULES LIST FILE (for installer `module` commands) +# +# Path to the file where the installer records installed modules +# with their branch and commit. You can override this path by +# setting the MODULES_LIST_FILE inside your config.sh or as an environment variable. +# By default it points inside the repository conf folder. +# Format of each line: +# +# Lines starting with '#' and empty lines are ignored. +export MODULES_LIST_FILE=${MODULES_LIST_FILE:-"$AC_PATH_ROOT/conf/modules.list"} + diff --git a/deps/acore/joiner/joiner.sh b/deps/acore/joiner/joiner.sh index be95b673a..1b1300716 100755 --- a/deps/acore/joiner/joiner.sh +++ b/deps/acore/joiner/joiner.sh @@ -94,11 +94,11 @@ function Joiner:add_repo() ( basedir="${4:-""}" [[ -z $url ]] && hasReq=false || hasReq=true - Joiner:_help $hasReq "$1" "Syntax: joiner.sh add-repo [-d] [-e] url name branch [basedir]" + Joiner:_help "$hasReq" "$1" "Syntax: joiner.sh add-repo [-d] [-e] url name branch [basedir]" # retrieving info from url if not set if [[ -z $name ]]; then - basename=$(basename $url) + basename=$(basename "$url") name=${basename%%.*} if [[ -z "$basedir" ]]; then @@ -115,10 +115,12 @@ function Joiner:add_repo() ( if [ -e "$path/.git/" ]; then # if exists , update - git --git-dir="$path/.git/" rev-parse && git --git-dir="$path/.git/" pull origin $branch | grep 'Already up-to-date.' && changed="no" || true + echo "Updating $name on branch $branch..." + git --git-dir="$path/.git/" --work-tree="$path" rev-parse && git --git-dir="$path/.git/" --work-tree="$path" pull origin "$branch" | grep 'Already up-to-date.' && changed="no" || true else # otherwise clone - git clone $url -c advice.detachedHead=0 -b $branch "$path" + echo "Cloning $name on branch $branch..." + git clone "$url" -c advice.detachedHead=0 -b "$branch" "$path" fi if [ "$?" -ne "0" ]; then @@ -140,16 +142,16 @@ function Joiner:add_git_submodule() ( basedir=${4:-""} [[ -z $url ]] && hasReq=false || hasReq=true - Joiner:_help $hasReq "$1" "Syntax: joiner.sh add-git-submodule [-d] [-e] url name branch [basedir]" + Joiner:_help "$hasReq" "$1" "Syntax: joiner.sh add-git-submodule [-d] [-e] url name branch [basedir]" # retrieving info from url if not set if [[ -z $name ]]; then - basename=$(basename $url) + basename=$(basename "$url") name=${basename%%.*} if [[ -z $basedir ]]; then - dir=$(dirname $url) - basedir=$(basename $dir) + dir=$(dirname "$url") + basedir=$(basename "$dir") fi name="${name,,}" #to lowercase @@ -158,17 +160,17 @@ function Joiner:add_git_submodule() ( path="$J_PATH_MODULES/$basedir/$name" valid_path=`Joiner:_searchFirstValiPath "$path"` - rel_path=${path#$valid_path} + rel_path=${path#"$valid_path"} rel_path=${rel_path#/} - if [ -e $path/ ]; then + if [ -e "$path/" ]; then # if exists , update - (cd "$path" && git pull origin $branch) - (cd "$valid_path" && git submodule update -f --init $rel_path) + (cd "$path" && git pull origin "$branch") + (cd "$valid_path" && git submodule update -f --init "$rel_path") else # otherwise add - (cd "$valid_path" && git submodule add -f -b $branch $url $rel_path) - (cd "$valid_path" && git submodule update -f --init $rel_path) + (cd "$valid_path" && git submodule add -f -b "$branch" "$url" "$rel_path") + (cd "$valid_path" && git submodule update -f --init "$rel_path") fi if [ "$?" -ne "0" ]; then @@ -324,7 +326,7 @@ function Joiner:self_update() { if [ ! -z "$J_VER_REQ" ]; then # if J_VER_REQ is defined then update only if tag is different _cur_branch=`git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" rev-parse --abbrev-ref HEAD` - _cur_ver=`git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" name-rev --tags --name-only $_cur_branch` + _cur_ver=`git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" name-rev --tags --name-only "$_cur_branch"` if [ "$_cur_ver" != "$J_VER_REQ" ]; then git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" rev-parse && git --git-dir="$J_PATH/.git/" fetch --tags origin "$_cur_branch" --quiet git --git-dir="$J_PATH/.git/" --work-tree="$J_PATH/" checkout "tags/$J_VER_REQ" -b "$_cur_branch" @@ -416,8 +418,8 @@ function Joiner:menu() { while true do # run option directly if specified in argument - [ ! -z $1 ] && _switch $@ - [ ! -z $1 ] && exit 0 + [ ! -z "$1" ] && _switch $@ + [ ! -z "$1" ] && exit 0 echo "" echo "==== JOINER MENU ====" From 611c9296fffa35f1f420d319a58b9c1741d8680a Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Sun, 31 Aug 2025 15:53:26 -0400 Subject: [PATCH 076/155] refactor(Core/Packets): Rewrite various item packets to modern class. (#22758) --- src/server/game/Handlers/ItemHandler.cpp | 347 +++++++----------- src/server/game/Server/Packets/AllPackets.h | 1 + .../game/Server/Packets/ItemPackets.cpp | 163 ++++++++ src/server/game/Server/Packets/ItemPackets.h | 272 ++++++++++++++ src/server/game/Server/WorldSession.h | 58 ++- 5 files changed, 609 insertions(+), 232 deletions(-) create mode 100644 src/server/game/Server/Packets/ItemPackets.cpp create mode 100644 src/server/game/Server/Packets/ItemPackets.h diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 4ce4ceb95..ce5ca1081 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -29,91 +29,82 @@ #include "WorldSession.h" #include -void WorldSession::HandleSplitItemOpcode(WorldPacket& recvData) +#include "ItemPackets.h" + +void WorldSession::HandleSplitItemOpcode(WorldPackets::Item::SplitItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_SPLIT_ITEM"); - uint8 srcbag, srcslot, dstbag, dstslot; - uint32 count; - recvData >> srcbag >> srcslot >> dstbag >> dstslot >> count; - - uint16 src = ((srcbag << 8) | srcslot); - uint16 dst = ((dstbag << 8) | dstslot); + uint16 src = ((packet.SourceBag << 8) | packet.SourceSlot); + uint16 dst = ((packet.DestinationBag << 8) | packet.DestinationSlot); if (src == dst) return; - if (count == 0) - return; //check count - if zero it's fake packet + if (packet.Count == 0) + return; //check count - if zero it's fake packet - if (!_player->IsValidPos(srcbag, srcslot, true)) + if (!_player->IsValidPos(packet.SourceBag, packet.SourceSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } - if (!_player->IsValidPos(dstbag, dstslot, false)) // can be autostore pos + if (!_player->IsValidPos(packet.DestinationBag, packet.DestinationSlot, false)) // can be autostore pos { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, nullptr, nullptr); return; } - _player->SplitItem(src, dst, count); + _player->SplitItem(src, dst, packet.Count); } -void WorldSession::HandleSwapInvItemOpcode(WorldPacket& recvData) +void WorldSession::HandleSwapInvItemOpcode(WorldPackets::Item::SwapInventoryItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_SWAP_INV_ITEM"); - uint8 srcslot, dstslot; - recvData >> dstslot >> srcslot; - - // prevent attempt swap same item to current position generated by client at special checting sequence - if (srcslot == dstslot) + // prevent attempt swap same item to current position generated by client at special cheating sequence + if (packet.SourceSlot == packet.DestinationSlot) return; - if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, srcslot, true)) + if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, packet.SourceSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } - if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, dstslot, true)) + if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, packet.DestinationSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, nullptr, nullptr); return; } - if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, srcslot) && !CanUseBank()) + if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, packet.SourceSlot) && !CanUseBank()) { //LOG_DEBUG("network", "WORLD: HandleSwapInvItemOpcode - Unit ({}) not found or you can't interact with him.", m_currentBankerGUID.ToString()); return; } - if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, dstslot) && !CanUseBank()) + if (_player->IsBankPos(INVENTORY_SLOT_BAG_0, packet.DestinationSlot) && !CanUseBank()) { //LOG_DEBUG("network", "WORLD: HandleSwapInvItemOpcode - Unit ({}) not found or you can't interact with him.", m_currentBankerGUID.ToString()); return; } - uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot); - uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot); + uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | packet.SourceSlot); + uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | packet.DestinationSlot); _player->SwapItem(src, dst); } -void WorldSession::HandleAutoEquipItemSlotOpcode(WorldPacket& recvData) +void WorldSession::HandleAutoEquipItemSlotOpcode(WorldPackets::Item::AutoEquipItemSlot& packet) { - ObjectGuid itemguid; - uint8 dstslot; - recvData >> itemguid >> dstslot; - // cheating attempt, client should never send opcode in that case - if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot)) + if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, packet.DestinationSlot)) return; - Item* item = _player->GetItemByGuid(itemguid); - uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8); + Item* item = _player->GetItemByGuid(packet.ItemGuid); + uint16 dstpos = packet.DestinationSlot | (INVENTORY_SLOT_BAG_0 << 8); if (!item || item->GetPos() == dstpos) return; @@ -121,39 +112,36 @@ void WorldSession::HandleAutoEquipItemSlotOpcode(WorldPacket& recvData) _player->SwapItem(item->GetPos(), dstpos); } -void WorldSession::HandleSwapItem(WorldPacket& recvData) +void WorldSession::HandleSwapItem(WorldPackets::Item::SwapItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_SWAP_ITEM"); - uint8 dstbag, dstslot, srcbag, srcslot; - recvData >> dstbag >> dstslot >> srcbag >> srcslot; + uint16 src = ((packet.SourceBag << 8) | packet.SourceSlot); + uint16 dst = ((packet.DestinationBag << 8) | packet.DestinationSlot); - uint16 src = ((srcbag << 8) | srcslot); - uint16 dst = ((dstbag << 8) | dstslot); - - // prevent attempt swap same item to current position generated by client at special checting sequence + // prevent attempt swap same item to current position generated by client at special cheating sequence if (src == dst) return; - if (!_player->IsValidPos(srcbag, srcslot, true)) + if (!_player->IsValidPos(packet.SourceBag, packet.SourceSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); return; } - if (!_player->IsValidPos(dstbag, dstslot, true)) + if (!_player->IsValidPos(packet.DestinationBag, packet.DestinationSlot, true)) { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, nullptr, nullptr); return; } - if (_player->IsBankPos(srcbag, srcslot) && !CanUseBank()) + if (_player->IsBankPos(packet.SourceBag, packet.SourceSlot) && !CanUseBank()) { //LOG_DEBUG("network", "WORLD: HandleSwapItem - Unit ({}) not found or you can't interact with him.", m_currentBankerGUID.ToString()); return; } - if (_player->IsBankPos(dstbag, dstslot) && !CanUseBank()) + if (_player->IsBankPos(packet.DestinationBag, packet.DestinationSlot) && !CanUseBank()) { //LOG_DEBUG("network", "WORLD: HandleSwapItem - Unit ({}) not found or you can't interact with him.", m_currentBankerGUID.ToString()); return; @@ -162,14 +150,11 @@ void WorldSession::HandleSwapItem(WorldPacket& recvData) _player->SwapItem(src, dst); } -void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) +void WorldSession::HandleAutoEquipItemOpcode(WorldPackets::Item::AutoEquipItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_AUTOEQUIP_ITEM"); - uint8 srcbag, srcslot; - recvData >> srcbag >> srcslot; - - Item* pSrcItem = _player->GetItemByPos(srcbag, srcslot); + Item* pSrcItem = _player->GetItemByPos(packet.SourceBag, packet.SourceSlot); if (!pSrcItem) return; // only at cheat @@ -219,7 +204,7 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) if (!pDstItem) // empty slot, simple case { - _player->RemoveItem(srcbag, srcslot, true); + _player->RemoveItem(packet.SourceBag, packet.SourceSlot, true); _player->EquipItem(dest, pSrcItem, true); _player->AutoUnequipOffhandIfNeed(); } @@ -243,23 +228,23 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) uint16 eSrc = 0; if (_player->IsInventoryPos(src)) { - msg = _player->CanStoreItem(srcbag, srcslot, sSrc, pDstItem, true); + msg = _player->CanStoreItem(packet.SourceBag, packet.SourceSlot, sSrc, pDstItem, true); if (msg != EQUIP_ERR_OK) - msg = _player->CanStoreItem(srcbag, NULL_SLOT, sSrc, pDstItem, true); + msg = _player->CanStoreItem(packet.SourceBag, NULL_SLOT, sSrc, pDstItem, true); if (msg != EQUIP_ERR_OK) msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sSrc, pDstItem, true); } else if (_player->IsBankPos(src)) { - msg = _player->CanBankItem(srcbag, srcslot, sSrc, pDstItem, true); + msg = _player->CanBankItem(packet.SourceBag, packet.SourceSlot, sSrc, pDstItem, true); if (msg != EQUIP_ERR_OK) - msg = _player->CanBankItem(srcbag, NULL_SLOT, sSrc, pDstItem, true); + msg = _player->CanBankItem(packet.SourceBag, NULL_SLOT, sSrc, pDstItem, true); if (msg != EQUIP_ERR_OK) msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, sSrc, pDstItem, true); } else if (_player->IsEquipmentPos(src)) { - msg = _player->CanEquipItem(srcslot, eSrc, pDstItem, true); + msg = _player->CanEquipItem(packet.SourceSlot, eSrc, pDstItem, true); if (msg == EQUIP_ERR_OK) msg = _player->CanUnequipItem(eSrc, true); } @@ -272,7 +257,7 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) // now do moves, remove... _player->RemoveItem(dstbag, dstslot, true, true); - _player->RemoveItem(srcbag, srcslot, true, true); + _player->RemoveItem(packet.SourceBag, packet.SourceSlot, true, true); // add to dest _player->EquipItem(dest, pSrcItem, true); @@ -292,14 +277,11 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) } } -void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) +void WorldSession::HandleDestroyItemOpcode(WorldPackets::Item::DestroyItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_DESTROYITEM"); - uint8 bag, slot, count, data1, data2, data3; - recvData >> bag >> slot >> count >> data1 >> data2 >> data3; - - uint16 pos = (bag << 8) | slot; + uint16 pos = (packet.Bag << 8) | packet.Slot; // prevent drop unequipable items (in combat, for example) and non-empty bags if (_player->IsEquipmentPos(pos) || _player->IsBagPos(pos)) @@ -312,7 +294,7 @@ void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) } } - Item* pItem = _player->GetItemByPos(bag, slot); + Item* pItem = _player->GetItemByPos(packet.Bag, packet.Slot); if (!pItem) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); @@ -327,14 +309,14 @@ void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) recoveryItem(pItem); - if (count) + if (packet.Count) { - uint32 i_count = count; + uint32 i_count = packet.Count; _player->DestroyItemCount(pItem, i_count, true); } else { - _player->DestroyItem(bag, slot, true); + _player->DestroyItem(packet.Bag, packet.Slot, true); } _player->SendQuestGiverStatusMultiple(); } @@ -692,15 +674,12 @@ void WorldSession::HandleItemQuerySingleOpcode(WorldPacket& recvData) } } -void WorldSession::HandleReadItem(WorldPacket& recvData) +void WorldSession::HandleReadItem(WorldPackets::Item::ReadItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_READ_ITEM"); - uint8 bag, slot; - recvData >> bag >> slot; - //LOG_DEBUG("network.opcode", "STORAGE: Read bag = {}, slot = {}", bag, slot); - Item* pItem = _player->GetItemByPos(bag, slot); + Item* pItem = _player->GetItemByPos(packet.Bag, packet.Slot); if (pItem && pItem->GetTemplate()->PageText) { @@ -725,27 +704,22 @@ void WorldSession::HandleReadItem(WorldPacket& recvData) _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, nullptr, nullptr); } -void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) +void WorldSession::HandleSellItemOpcode(WorldPackets::Item::SellItem& packet) { - ObjectGuid vendorguid, itemguid; - uint32 count; - - recvData >> vendorguid >> itemguid >> count; - - if (!itemguid) + if (!packet.ItemGuid) return; - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR); + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(packet.VendorGuid, UNIT_NPC_FLAG_VENDOR); if (!creature) { - LOG_DEBUG("network", "WORLD: HandleSellItemOpcode - Unit ({}) not found or you can not interact with him.", vendorguid.ToString()); - _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, itemguid, 0); + LOG_DEBUG("network", "WORLD: HandleSellItemOpcode - Unit ({}) not found or you can not interact with him.", packet.VendorGuid.ToString()); + _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, packet.ItemGuid, 0); return; } if (creature->HasFlagsExtra(CREATURE_FLAG_EXTRA_NO_SELL_VENDOR)) { - _player->SendSellError(SELL_ERR_CANT_SELL_TO_THIS_MERCHANT, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_TO_THIS_MERCHANT, creature, packet.ItemGuid, 0); return; } @@ -753,7 +727,7 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - Item* pItem = _player->GetItemByGuid(itemguid); + Item* pItem = _player->GetItemByGuid(packet.ItemGuid); if (pItem) { if (!sScriptMgr->OnPlayerCanSellItem(_player, pItem, creature)) @@ -762,21 +736,21 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) // prevent sell not owner item if (_player->GetGUID() != pItem->GetOwnerGUID()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } // prevent sell non empty bag by drag-and-drop at vendor's item list if (pItem->IsNotEmptyBag()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } // prevent sell currently looted item if (_player->GetLootGUID() == pItem->GetGUID()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } @@ -787,16 +761,14 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) return; // Therefore, no feedback to client // special case at auto sell (sell all) - if (count == 0) - { - count = pItem->GetCount(); - } + if (packet.Count == 0) + packet.Count = pItem->GetCount(); else { // prevent sell more items that exist in stack (possible only not from client) - if (count > pItem->GetCount()) + if (packet.Count > pItem->GetCount()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } } @@ -806,11 +778,11 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) { if (pProto->SellPrice > 0) { - uint32 money = pProto->SellPrice * count; + uint32 money = pProto->SellPrice * packet.Count; if (_player->GetMoney() >= MAX_MONEY_AMOUNT - money) // prevent exceeding gold limit { _player->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, nullptr, nullptr); - _player->SendSellError(SELL_ERR_UNK, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_UNK, creature, packet.ItemGuid, 0); return; } @@ -828,8 +800,8 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(pProto->ItemLevel); if (!dcost) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); - LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - Wrong item lvl {} for item {} count = {}", pProto->ItemLevel, pItem->GetEntry(), count); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); + LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - Wrong item lvl {} for item {} count = {}", pProto->ItemLevel, pItem->GetEntry(), packet.Count); return; } @@ -837,8 +809,8 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) DurabilityQualityEntry const* dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId); if (!dQualitymodEntry) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); - LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - Wrong dQualityModEntry {} for item {} count = {}", dQualitymodEntryId, pItem->GetEntry(), count); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); + LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - Wrong dQualityModEntry {} for item {} count = {}", dQualitymodEntryId, pItem->GetEntry(), packet.Count); return; } @@ -846,36 +818,30 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) uint32 refund = uint32(std::ceil(LostDurability * dmultiplier * double(dQualitymodEntry->quality_mod))); if (!refund) - { refund = 1; - } //starter items can cost more to refund than vendorprice if (refund > money) - { money = 1; - } else - { money -= refund; - } } } - if (count < pItem->GetCount()) // need split items + if (packet.Count < pItem->GetCount()) // need split items { - Item* pNewItem = pItem->CloneItem(count, _player); + Item* pNewItem = pItem->CloneItem(packet.Count, _player); if (!pNewItem) { - LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - could not create clone of item {}; count = {}", pItem->GetEntry(), count); - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + LOG_ERROR("network.opcode", "WORLD: HandleSellItemOpcode - could not create clone of item {}; count = {}", pItem->GetEntry(), packet.Count); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } pNewItem->SetUInt32Value(ITEM_FIELD_DURABILITY, pItem->GetUInt32Value(ITEM_FIELD_DURABILITY)); - pItem->SetCount(pItem->GetCount() - count); - _player->ItemRemovedQuestCheck(pItem->GetEntry(), count); + pItem->SetCount(pItem->GetCount() - packet.Count); + _player->ItemRemovedQuestCheck(pItem->GetEntry(), packet.Count); if (_player->IsInWorld()) pItem->SendUpdateToPlayer(_player); pItem->SetState(ITEM_CHANGED, _player); @@ -897,25 +863,20 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS, money); } else - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, packet.ItemGuid, 0); return; } } - _player->SendSellError(SELL_ERR_CANT_FIND_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_FIND_ITEM, creature, packet.ItemGuid, 0); return; } -void WorldSession::HandleBuybackItem(WorldPacket& recvData) +void WorldSession::HandleBuybackItem(WorldPackets::Item::BuybackItem& packet) { - ObjectGuid vendorguid; - uint32 slot; - - recvData >> vendorguid >> slot; - - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR); + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(packet.VendorGuid, UNIT_NPC_FLAG_VENDOR); if (!creature) { - LOG_DEBUG("network", "WORLD: HandleBuybackItem - Unit ({}) not found or you can not interact with him.", vendorguid.ToString()); + LOG_DEBUG("network", "WORLD: HandleBuybackItem - Unit ({}) not found or you can not interact with him.", packet.VendorGuid.ToString()); _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, nullptr, ObjectGuid::Empty, 0); return; } @@ -924,10 +885,10 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - Item* pItem = _player->GetItemFromBuyBackSlot(slot); + Item* pItem = _player->GetItemFromBuyBackSlot(packet.Slot); if (pItem) { - uint32 price = _player->GetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START); + uint32 price = _player->GetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + packet.Slot - BUYBACK_SLOT_START); if (!_player->HasEnoughMoney(price)) { _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, pItem->GetEntry(), 0); @@ -948,7 +909,7 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) } _player->ModifyMoney(-(int32)price); - _player->RemoveItemFromBuyBackSlot(slot, false); + _player->RemoveItemFromBuyBackSlot(packet.Slot, false); _player->ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount()); _player->StoreItem(dest, pItem, true); } @@ -960,24 +921,18 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) _player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, 0, 0); } -void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData) +void WorldSession::HandleBuyItemInSlotOpcode(WorldPackets::Item::BuyItemInSlot& packet) { - ObjectGuid vendorguid, bagguid; - uint32 item, slot, count; - uint8 bagslot; - - recvData >> vendorguid >> item >> slot >> bagguid >> bagslot >> count; - // client expects count starting at 1, and we send vendorslot+1 to client already - if (slot > 0) - --slot; + if (packet.Slot > 0) + --packet.Slot; else - return; // cheating + return; // cheating - uint8 bag = NULL_BAG; // init for case invalid bagGUID + uint8 bag = NULL_BAG; // init for case invalid bagGUID // find bag slot by bag guid - if (bagguid == _player->GetGUID()) + if (packet.BagGuid == _player->GetGUID()) bag = INVENTORY_SLOT_BAG_0; else { @@ -985,7 +940,7 @@ void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData) { if (Bag* pBag = _player->GetBagByPos(i)) { - if (bagguid == pBag->GetGUID()) + if (packet.BagGuid == pBag->GetGUID()) { bag = i; break; @@ -998,38 +953,28 @@ void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData) if (bag == NULL_BAG) return; - GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, bag, bagslot); + GetPlayer()->BuyItemFromVendorSlot(packet.VendorGuid, packet.Slot, packet.Item, packet.Count, bag, packet.BagSlot); } -void WorldSession::HandleBuyItemOpcode(WorldPacket& recvData) +void WorldSession::HandleBuyItemOpcode(WorldPackets::Item::BuyItem& packet) { - ObjectGuid vendorguid; - uint32 item, slot, count; - uint8 unk1; - - recvData >> vendorguid >> item >> slot >> count >> unk1; - // client expects count starting at 1, and we send vendorslot+1 to client already - if (slot > 0) - --slot; + if (packet.Slot > 0) + --packet.Slot; else return; // cheating - GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, NULL_BAG, NULL_SLOT); + GetPlayer()->BuyItemFromVendorSlot(packet.VendorGuid, packet.Slot, packet.Item, packet.Count, NULL_BAG, NULL_SLOT); } -void WorldSession::HandleListInventoryOpcode(WorldPacket& recvData) +void WorldSession::HandleListInventoryOpcode(WorldPackets::Item::ListInventory& packet) { - ObjectGuid guid; - - recvData >> guid; - if (!GetPlayer()->IsAlive()) return; LOG_DEBUG("network", "WORLD: Recvd CMSG_LIST_INVENTORY"); - SendListInventory(guid); + SendListInventory(packet.VendorGuid); } void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry) @@ -1143,18 +1088,14 @@ void WorldSession::SendListInventory(ObjectGuid vendorGuid, uint32 vendorEntry) SendPacket(&data); } -void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData) +void WorldSession::HandleAutoStoreBagItemOpcode(WorldPackets::Item::AutoStoreBagItem& packet) { //LOG_DEBUG("network.opcode", "WORLD: CMSG_AUTOSTORE_BAG_ITEM"); - uint8 srcbag, srcslot, dstbag; - - recvData >> srcbag >> srcslot >> dstbag; - - Item* pItem = _player->GetItemByPos(srcbag, srcslot); + Item* pItem = _player->GetItemByPos(packet.SourceBag, packet.SourceSlot); if (!pItem) return; - if (!_player->IsValidPos(dstbag, NULL_SLOT, false)) // can be autostore pos + if (!_player->IsValidPos(packet.DestinationBag, NULL_SLOT, false)) // can be autostore pos { _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, nullptr, nullptr); return; @@ -1174,7 +1115,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData) } ItemPosCountVec dest; - InventoryResult msg = _player->CanStoreItem(dstbag, NULL_SLOT, dest, pItem, false); + InventoryResult msg = _player->CanStoreItem(packet.DestinationBag, NULL_SLOT, dest, pItem, false); if (msg != EQUIP_ERR_OK) { _player->SendEquipError(msg, pItem, nullptr); @@ -1189,7 +1130,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData) return; } - _player->RemoveItem(srcbag, srcslot, true); + _player->RemoveItem(packet.SourceBag, packet.SourceSlot, true); _player->StoreItem(dest, pItem, true); _player->UpdateTitansGrip(); } @@ -1223,23 +1164,22 @@ void WorldSession::HandleSetAmmoOpcode(WorldPacket& recvData) void WorldSession::SendEnchantmentLog(ObjectGuid target, ObjectGuid caster, uint32 itemId, uint32 enchantId) { - WorldPacket data(SMSG_ENCHANTMENTLOG, (8 + 8 + 4 + 4)); // last check 2.0.10 - data << target.WriteAsPacked(); - data << caster.WriteAsPacked(); - data << uint32(itemId); - data << uint32(enchantId); - GetPlayer()->SendMessageToSet(&data, true); + WorldPackets::Item::EnchantmentLog enchantmentLog; + enchantmentLog.Target = target.WriteAsPacked(); + enchantmentLog.Caster = caster.WriteAsPacked(); + enchantmentLog.ItemId = itemId; + enchantmentLog.EnchantId = enchantId; + GetPlayer()->SendMessageToSet(enchantmentLog.Write(), true); } void WorldSession::SendItemEnchantTimeUpdate(ObjectGuid Playerguid, ObjectGuid Itemguid, uint32 slot, uint32 Duration) { - // last check 2.0.10 - WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8 + 4 + 4 + 8)); - data << Itemguid; - data << uint32(slot); - data << uint32(Duration); - data << Playerguid; - SendPacket(&data); + WorldPackets::Item::ItemEnchantTimeUpdate itemEnchantTimeUpdate; + itemEnchantTimeUpdate.ItemGuid = Itemguid; + itemEnchantTimeUpdate.Slot = slot; + itemEnchantTimeUpdate.Duration = Duration; + itemEnchantTimeUpdate.PlayerGuid = Playerguid; + SendPacket(itemEnchantTimeUpdate.Write()); } void WorldSession::HandleItemNameQueryOpcode(WorldPacket& recvData) @@ -1266,18 +1206,13 @@ void WorldSession::HandleItemNameQueryOpcode(WorldPacket& recvData) } } -void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) +void WorldSession::HandleWrapItemOpcode(WorldPackets::Item::WrapItem& packet) { LOG_DEBUG("network", "Received opcode CMSG_WRAP_ITEM"); - uint8 gift_bag, gift_slot, item_bag, item_slot; + LOG_DEBUG("network", "WRAP: receive GiftBag = {}, GiftSlot = {}, ItemBag = {}, ItemSlot = {}", packet.GiftBag, packet.GiftSlot, packet.ItemBag, packet.ItemSlot); - recvData >> gift_bag >> gift_slot; // paper - recvData >> item_bag >> item_slot; // item - - LOG_DEBUG("network", "WRAP: receive gift_bag = {}, gift_slot = {}, item_bag = {}, item_slot = {}", gift_bag, gift_slot, item_bag, item_slot); - - Item* gift = _player->GetItemByPos(gift_bag, gift_slot); + Item* gift = _player->GetItemByPos(packet.GiftBag, packet.GiftSlot); if (!gift) { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, nullptr); @@ -1290,7 +1225,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) return; } - Item* item = _player->GetItemByPos(item_bag, item_slot); + Item* item = _player->GetItemByPos(packet.ItemBag, packet.ItemSlot); if (!item) { @@ -1305,7 +1240,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) return; } - if (item == gift) // not possable with pacjket from real client + if (item == gift) // not possible with packet from real client { _player->SendEquipError(EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, nullptr); return; @@ -1399,26 +1334,19 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) _player->DestroyItemCount(gift, count, true); } -void WorldSession::HandleSocketOpcode(WorldPacket& recvData) +void WorldSession::HandleSocketOpcode(WorldPackets::Item::SocketGems& packet) { LOG_DEBUG("network", "WORLD: CMSG_SOCKET_GEMS"); - ObjectGuid item_guid; - ObjectGuid gem_guids[MAX_GEM_SOCKETS]; - - recvData >> item_guid; - if (!item_guid) + if (!packet.ItemGuid) return; - for (int i = 0; i < MAX_GEM_SOCKETS; ++i) - recvData >> gem_guids[i]; - //cheat -> tried to socket same gem multiple times - if ((gem_guids[0] && (gem_guids[0] == gem_guids[1] || gem_guids[0] == gem_guids[2])) || - (gem_guids[1] && (gem_guids[1] == gem_guids[2]))) + if ((packet.GemGuids[0] && (packet.GemGuids[0] == packet.GemGuids[1] || packet.GemGuids[0] == packet.GemGuids[2])) || + (packet.GemGuids[1] && (packet.GemGuids[1] == packet.GemGuids[2]))) return; - Item* itemTarget = _player->GetItemByGuid(item_guid); + Item* itemTarget = _player->GetItemByGuid(packet.ItemGuid); if (!itemTarget) //missing item to socket return; @@ -1431,7 +1359,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) Item* Gems[MAX_GEM_SOCKETS]; for (int i = 0; i < MAX_GEM_SOCKETS; ++i) - Gems[i] = gem_guids[i] ? _player->GetItemByGuid(gem_guids[i]) : nullptr; + Gems[i] = packet.GemGuids[i] ? _player->GetItemByGuid(packet.GemGuids[i]) : nullptr; GemPropertiesEntry const* GemProps[MAX_GEM_SOCKETS]; for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //get geminfo from dbc storage @@ -1572,7 +1500,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) if (GemEnchants[i]) { itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT + i), GemEnchants[i], 0, 0, _player->GetGUID()); - if (Item* guidItem = _player->GetItemByGuid(gem_guids[i])) + if (Item* guidItem = _player->GetItemByGuid(packet.GemGuids[i])) _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true); } } @@ -1597,19 +1525,15 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) itemTarget->SendUpdateSockets(); } -void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPacket& recvData) +void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPackets::Item::CancelTempEnchantment& packet) { LOG_DEBUG("network", "WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT"); - uint32 eslot; - - recvData >> eslot; - // apply only to equipped item - if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, eslot)) + if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, packet.EquipmentSlot)) return; - Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot); + Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, packet.EquipmentSlot); if (!item) return; @@ -1621,14 +1545,11 @@ void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPacket& recvData) item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT); } -void WorldSession::HandleItemRefundInfoRequest(WorldPacket& recvData) +void WorldSession::HandleItemRefundInfoRequest(WorldPackets::Item::ItemRefundInfo& packet) { LOG_DEBUG("network", "WORLD: CMSG_ITEM_REFUND_INFO"); - ObjectGuid guid; - recvData >> guid; // item guid - - Item* item = _player->GetItemByGuid(guid); + Item* item = _player->GetItemByGuid(packet.ItemGuid); if (!item) { LOG_DEBUG("network", "Item refund: item not found!"); @@ -1638,13 +1559,11 @@ void WorldSession::HandleItemRefundInfoRequest(WorldPacket& recvData) GetPlayer()->SendRefundInfo(item); } -void WorldSession::HandleItemRefund(WorldPacket& recvData) +void WorldSession::HandleItemRefund(WorldPackets::Item::ItemRefund& packet) { LOG_DEBUG("network", "WORLD: CMSG_ITEM_REFUND"); - ObjectGuid guid; - recvData >> guid; // item guid - Item* item = _player->GetItemByGuid(guid); + Item* item = _player->GetItemByGuid(packet.ItemGuid); if (!item) { LOG_DEBUG("network", "Item refund: item not found!"); @@ -1652,7 +1571,7 @@ void WorldSession::HandleItemRefund(WorldPacket& recvData) } // Don't try to refund item currently being disenchanted - if (_player->GetLootGUID() == guid) + if (_player->GetLootGUID() == packet.ItemGuid) return; GetPlayer()->RefundItem(item); diff --git a/src/server/game/Server/Packets/AllPackets.h b/src/server/game/Server/Packets/AllPackets.h index 30aecbb6f..498dafb90 100644 --- a/src/server/game/Server/Packets/AllPackets.h +++ b/src/server/game/Server/Packets/AllPackets.h @@ -24,6 +24,7 @@ #include "CombatLogPackets.h" #include "CombatPackets.h" #include "GuildPackets.h" +#include "ItemPackets.h" #include "LFGPackets.h" #include "MiscPackets.h" #include "PetPackets.h" diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp new file mode 100644 index 000000000..e86ae3879 --- /dev/null +++ b/src/server/game/Server/Packets/ItemPackets.cpp @@ -0,0 +1,163 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "ItemPackets.h" + +void WorldPackets::Item::SplitItem::Read() +{ + _worldPacket >> SourceBag; + _worldPacket >> SourceSlot; + _worldPacket >> DestinationBag; + _worldPacket >> DestinationSlot; + _worldPacket >> Count; +} + +void WorldPackets::Item::SwapInventoryItem::Read() +{ + _worldPacket >> DestinationSlot; + _worldPacket >> SourceSlot; +} + +void WorldPackets::Item::AutoEquipItemSlot::Read() +{ + _worldPacket >> ItemGuid; + _worldPacket >> DestinationSlot; +} + +void WorldPackets::Item::SwapItem::Read() +{ + _worldPacket >> DestinationBag; + _worldPacket >> DestinationSlot; + _worldPacket >> SourceBag; + _worldPacket >> SourceSlot; +} + +void WorldPackets::Item::AutoEquipItem::Read() +{ + _worldPacket >> SourceBag; + _worldPacket >> SourceSlot; +} + +void WorldPackets::Item::DestroyItem::Read() +{ + _worldPacket >> Bag; + _worldPacket >> Slot; + _worldPacket >> Count; + _worldPacket >> Data1; + _worldPacket >> Data2; + _worldPacket >> Data3; +} + +void WorldPackets::Item::ReadItem::Read() +{ + _worldPacket >> Bag; + _worldPacket >> Slot; +} + +void WorldPackets::Item::SellItem::Read() +{ + _worldPacket >> VendorGuid; + _worldPacket >> ItemGuid; + _worldPacket >> Count; +} + +void WorldPackets::Item::BuybackItem::Read() +{ + _worldPacket >> VendorGuid; + _worldPacket >> Slot; +} + +void WorldPackets::Item::BuyItemInSlot::Read() +{ + _worldPacket >> VendorGuid; + _worldPacket >> Item; + _worldPacket >> Slot; + _worldPacket >> BagGuid; + _worldPacket >> BagSlot; + _worldPacket >> Count; +} + +void WorldPackets::Item::BuyItem::Read() +{ + _worldPacket >> VendorGuid; + _worldPacket >> Item; + _worldPacket >> Slot; + _worldPacket >> Count; + _worldPacket >> Unk; +} + +void WorldPackets::Item::ListInventory::Read() +{ + _worldPacket >> VendorGuid; +} + +void WorldPackets::Item::AutoStoreBagItem::Read() +{ + _worldPacket >> SourceBag; + _worldPacket >> SourceSlot; + _worldPacket >> DestinationBag; +} + +WorldPacket const* WorldPackets::Item::EnchantmentLog::Write() +{ + _worldPacket << Target; + _worldPacket << Caster; + _worldPacket << ItemId; + _worldPacket << EnchantId; + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Item::ItemEnchantTimeUpdate::Write() +{ + _worldPacket << ItemGuid; + _worldPacket << Slot; + _worldPacket << Duration; + _worldPacket << PlayerGuid; + + return &_worldPacket; +} + +void WorldPackets::Item::WrapItem::Read() +{ + _worldPacket >> GiftBag; + _worldPacket >> GiftSlot; + _worldPacket >> ItemBag; + _worldPacket >> ItemSlot; +} + +void WorldPackets::Item::SocketGems::Read() +{ + _worldPacket >> ItemGuid; + for (int i = 0; i < MAX_GEM_SOCKETS; ++i) + _worldPacket >> GemGuids[i]; +} + +void WorldPackets::Item::CancelTempEnchantment::Read() +{ + _worldPacket >> EquipmentSlot; +} + +void WorldPackets::Item::ItemRefundInfo::Read() +{ + _worldPacket >> ItemGuid; +} + +void WorldPackets::Item::ItemRefund::Read() +{ + _worldPacket >> ItemGuid; +} diff --git a/src/server/game/Server/Packets/ItemPackets.h b/src/server/game/Server/Packets/ItemPackets.h new file mode 100644 index 000000000..04d884188 --- /dev/null +++ b/src/server/game/Server/Packets/ItemPackets.h @@ -0,0 +1,272 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef ItemPackets_h__ +#define ItemPackets_h__ + +#include "Item.h" +#include "ObjectGuid.h" +#include "Packet.h" + +namespace WorldPackets +{ + namespace Item + { + class SplitItem final : public ClientPacket + { + public: + SplitItem(WorldPacket&& packet) : ClientPacket(CMSG_SPLIT_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 SourceBag = 0; + uint8 SourceSlot = 0; + uint8 DestinationBag = 0; + uint8 DestinationSlot = 0; + uint32 Count = 0; + }; + + class SwapInventoryItem final : public ClientPacket + { + public: + SwapInventoryItem(WorldPacket&& packet) : ClientPacket(CMSG_SWAP_INV_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 DestinationSlot = 0; + uint8 SourceSlot = 0; + }; + + class AutoEquipItemSlot final : public ClientPacket + { + public: + AutoEquipItemSlot(WorldPacket&& packet) : ClientPacket(CMSG_AUTOEQUIP_ITEM_SLOT, std::move(packet)) {} + + void Read() override; + + ObjectGuid ItemGuid; + uint8 DestinationSlot = 0; + }; + + class SwapItem final : public ClientPacket + { + public: + SwapItem(WorldPacket&& packet) : ClientPacket(CMSG_SWAP_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 DestinationBag = 0; + uint8 DestinationSlot = 0; + uint8 SourceBag = 0; + uint8 SourceSlot = 0; + }; + + class AutoEquipItem final : public ClientPacket + { + public: + AutoEquipItem(WorldPacket&& packet) : ClientPacket(CMSG_AUTOEQUIP_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 SourceBag = 0; + uint8 SourceSlot = 0; + }; + + class DestroyItem final : public ClientPacket + { + public: + DestroyItem(WorldPacket&& packet) : ClientPacket(CMSG_DESTROYITEM, std::move(packet)) {} + + void Read() override; + + uint8 Bag = 0; + uint8 Slot = 0; + uint8 Count = 0; + uint8 Data1 = 0; + uint8 Data2 = 0; + uint8 Data3 = 0; + }; + + class ReadItem final : public ClientPacket + { + public: + ReadItem(WorldPacket&& packet) : ClientPacket(CMSG_READ_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 Bag = 0; + uint8 Slot = 0; + }; + + class SellItem final : public ClientPacket + { + public: + SellItem(WorldPacket&& packet) : ClientPacket(CMSG_SELL_ITEM, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + ObjectGuid ItemGuid; + uint32 Count = 0; + }; + + class BuybackItem final : public ClientPacket + { + public: + BuybackItem(WorldPacket&& packet) : ClientPacket(CMSG_BUYBACK_ITEM, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + uint32 Slot = 0; + }; + + class BuyItemInSlot final : public ClientPacket + { + public: + BuyItemInSlot(WorldPacket&& packet) : ClientPacket(CMSG_BUY_ITEM_IN_SLOT, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + uint32 Item = 0; + uint32 Slot = 0; + ObjectGuid BagGuid; + uint8 BagSlot = 0; + uint32 Count = 0; + }; + + class BuyItem final : public ClientPacket + { + public: + BuyItem(WorldPacket&& packet) : ClientPacket(CMSG_BUY_ITEM, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + uint32 Item = 0; + uint32 Slot = 0; + uint32 Count = 0; + uint8 Unk = 0; + }; + + class ListInventory final : public ClientPacket + { + public: + ListInventory(WorldPacket&& packet) : ClientPacket(CMSG_LIST_INVENTORY, std::move(packet)) {} + + void Read() override; + + ObjectGuid VendorGuid; + }; + + class AutoStoreBagItem final : public ClientPacket + { + public: + AutoStoreBagItem(WorldPacket&& packet) : ClientPacket(CMSG_AUTOSTORE_BAG_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 SourceBag = 0; + uint8 SourceSlot = 0; + uint8 DestinationBag = 0; + }; + + class EnchantmentLog final : public ServerPacket + { + public: + EnchantmentLog() : ServerPacket(SMSG_ENCHANTMENTLOG, 8 + 8 + 4 + 4) {} + + WorldPacket const* Write() override; + + PackedGuid Target; + PackedGuid Caster; + uint32 ItemId = 0; + uint32 EnchantId = 0; + }; + + class ItemEnchantTimeUpdate final : public ServerPacket + { + public: + ItemEnchantTimeUpdate() : ServerPacket(SMSG_ITEM_ENCHANT_TIME_UPDATE, 8 + 4 + 4 + 8) {} + + WorldPacket const* Write() override; + + // last check 2.0.10 + ObjectGuid ItemGuid; + uint32 Slot = 0; + uint32 Duration = 0; + ObjectGuid PlayerGuid; + }; + + class WrapItem final : public ClientPacket + { + public: + WrapItem(WorldPacket&& packet) : ClientPacket(CMSG_WRAP_ITEM, std::move(packet)) {} + + void Read() override; + + uint8 GiftBag = 0; + uint8 GiftSlot = 0; + uint8 ItemBag = 0; + uint8 ItemSlot = 0; + }; + + class SocketGems final : public ClientPacket + { + public: + SocketGems(WorldPacket&& packet) : ClientPacket(CMSG_SOCKET_GEMS, std::move(packet)) {} + + void Read() override; + + ObjectGuid ItemGuid; + ObjectGuid GemGuids[MAX_GEM_SOCKETS]; + }; + + class CancelTempEnchantment final : public ClientPacket + { + public: + CancelTempEnchantment(WorldPacket&& packet) : ClientPacket(CMSG_CANCEL_TEMP_ENCHANTMENT, std::move(packet)) {} + + void Read() override; + + uint32 EquipmentSlot = 0; + }; + + class ItemRefundInfo final : public ClientPacket + { + public: + ItemRefundInfo(WorldPacket&& packet) : ClientPacket(CMSG_ITEM_REFUND_INFO, std::move(packet)) {} + + void Read() override; + + ObjectGuid ItemGuid; + }; + + class ItemRefund final : public ClientPacket + { + public: + ItemRefund(WorldPacket&& packet) : ClientPacket(CMSG_ITEM_REFUND, std::move(packet)) {} + + void Read() override; + + ObjectGuid ItemGuid; + }; + } +} + +#endif // ItemPackets_h__ diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 032e18abf..15c0a7368 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -167,6 +167,28 @@ namespace WorldPackets class TimeQuery; class CorpseMapPositionQuery; } + + namespace Item + { + class SplitItem; + class SwapInventoryItem; + class AutoEquipItemSlot; + class SwapItem; + class AutoEquipItem; + class DestroyItem; + class ReadItem; + class SellItem; + class BuybackItem; + class BuyItemInSlot; + class BuyItem; + class ListInventory; + class AutoStoreBagItem; + class WrapItem; + class SocketGems; + class CancelTempEnchantment; + class ItemRefundInfo; + class ItemRefund; + } } enum AccountDataType @@ -810,21 +832,21 @@ public: // opcodes handlers void HandleQueryNextMailTime(WorldPacket& recvData); void HandleCancelChanneling(WorldPacket& recvData); - void HandleSplitItemOpcode(WorldPacket& recvPacket); - void HandleSwapInvItemOpcode(WorldPacket& recvPacket); - void HandleDestroyItemOpcode(WorldPacket& recvPacket); - void HandleAutoEquipItemOpcode(WorldPacket& recvPacket); + void HandleSplitItemOpcode(WorldPackets::Item::SplitItem& packet); + void HandleSwapInvItemOpcode(WorldPackets::Item::SwapInventoryItem& packet); + void HandleDestroyItemOpcode(WorldPackets::Item::DestroyItem& packet); + void HandleAutoEquipItemOpcode(WorldPackets::Item::AutoEquipItem& packet); void HandleItemQuerySingleOpcode(WorldPacket& recvPacket); - void HandleSellItemOpcode(WorldPacket& recvPacket); - void HandleBuyItemInSlotOpcode(WorldPacket& recvPacket); - void HandleBuyItemOpcode(WorldPacket& recvPacket); - void HandleListInventoryOpcode(WorldPacket& recvPacket); - void HandleAutoStoreBagItemOpcode(WorldPacket& recvPacket); - void HandleReadItem(WorldPacket& recvPacket); - void HandleAutoEquipItemSlotOpcode(WorldPacket& recvPacket); - void HandleSwapItem(WorldPacket& recvPacket); - void HandleBuybackItem(WorldPacket& recvPacket); - void HandleWrapItemOpcode(WorldPacket& recvPacket); + void HandleSellItemOpcode(WorldPackets::Item::SellItem& packet); + void HandleBuyItemInSlotOpcode(WorldPackets::Item::BuyItemInSlot& packet); + void HandleBuyItemOpcode(WorldPackets::Item::BuyItem& packet); + void HandleListInventoryOpcode(WorldPackets::Item::ListInventory& packet); + void HandleAutoStoreBagItemOpcode(WorldPackets::Item::AutoStoreBagItem& packet); + void HandleReadItem(WorldPackets::Item::ReadItem& packet); + void HandleAutoEquipItemSlotOpcode(WorldPackets::Item::AutoEquipItemSlot& packet); + void HandleSwapItem(WorldPackets::Item::SwapItem& packet); + void HandleBuybackItem(WorldPackets::Item::BuybackItem& packet); + void HandleWrapItemOpcode(WorldPackets::Item::WrapItem& packet); void HandleAttackSwingOpcode(WorldPacket& recvPacket); void HandleAttackStopOpcode(WorldPacket& recvPacket); @@ -1014,12 +1036,12 @@ public: // opcodes handlers void HandleRequestPetInfo(WorldPackets::Pet::RequestPetInfo& packet); // Socket gem - void HandleSocketOpcode(WorldPacket& recvData); + void HandleSocketOpcode(WorldPackets::Item::SocketGems& packet); - void HandleCancelTempEnchantmentOpcode(WorldPacket& recvData); + void HandleCancelTempEnchantmentOpcode(WorldPackets::Item::CancelTempEnchantment& packet); - void HandleItemRefundInfoRequest(WorldPacket& recvData); - void HandleItemRefund(WorldPacket& recvData); + void HandleItemRefundInfoRequest(WorldPackets::Item::ItemRefundInfo& packet); + void HandleItemRefund(WorldPackets::Item::ItemRefund& packet); void HandleChannelVoiceOnOpcode(WorldPacket& recvData); void HandleVoiceSessionEnableOpcode(WorldPacket& recvData); From 033a6e31ed616db5a5dea64956a9a55d3e64dc2b Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Mon, 1 Sep 2025 21:40:12 -0400 Subject: [PATCH 077/155] fix(Scripts/World): Swap Shard communication spell to direct cast instead of AoE. (#22764) --- src/server/scripts/World/scourge_invasion.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/scripts/World/scourge_invasion.cpp b/src/server/scripts/World/scourge_invasion.cpp index 982049624..c7d83f028 100644 --- a/src/server/scripts/World/scourge_invasion.cpp +++ b/src/server/scripts/World/scourge_invasion.cpp @@ -519,7 +519,8 @@ struct npc_necrotic_shard : public ScriptedAI // Buff Players. DoCastSelf(SPELL_SOUL_REVIVAL, true); // Sending the Death Bolt. - DoCastAOE(SPELL_COMMUNIQUE_CAMP_TO_RELAY_DEATH, true); + if (Creature* relay = GetClosestCreatureWithEntry(me, NPC_NECROPOLIS_RELAY, 200.0f)) + me->CastSpell(relay, SPELL_COMMUNIQUE_CAMP_TO_RELAY_DEATH, true); DespawnCultists(); // Despawn remaining Cultists (should never happen). DespawnEventDoodads(); sWorldState->Save(SAVE_ID_SCOURGE_INVASION); From 201a84fe851b9febeed7472f091b9e132a4dd079 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Tue, 2 Sep 2025 08:34:18 +0200 Subject: [PATCH 078/155] fix(DB/SAI) Add missing abilites for various npcs in The Shadow Vault area. (#22766) --- data/sql/updates/pending_db_world/Various_AI.sql | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Various_AI.sql diff --git a/data/sql/updates/pending_db_world/Various_AI.sql b/data/sql/updates/pending_db_world/Various_AI.sql new file mode 100644 index 000000000..04a68b7f6 --- /dev/null +++ b/data/sql/updates/pending_db_world/Various_AI.sql @@ -0,0 +1,13 @@ + +-- Morbid Carcass, Vault Geist, Rabid Cannibal, Death Knight Master +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (29719, 29720, 29722, 29738)); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` IN (29719, 29720, 29722, 29738)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(29719, 0, 0, 0, 0, 0, 100, 0, 8000, 12000, 8000, 12000, 0, 0, 11, 40504, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Morbid Carcass - In Combat - Cast \'Cleave\''), +(29719, 0, 1, 0, 9, 0, 100, 0, 8000, 12000, 8000, 12000, 8, 40, 11, 50335, 0, 0, 0, 0, 0, 5, 40, 0, 0, 0, 0, 0, 0, 0, 'Morbid Carcass - Within 8-40 Range - Cast \'Scourge Hook\''), +(29720, 0, 0, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 97, 20, 10, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Vault Geist - On Aggro - Jump To Pos'), +(29720, 0, 1, 0, 0, 0, 100, 0, 4000, 6000, 18000, 24000, 0, 0, 11, 36590, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Vault Geist - In Combat - Cast \'Rip\''), +(29738, 0, 0, 0, 0, 0, 100, 0, 0, 0, 20000, 24000, 0, 0, 11, 50688, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Death Knight Master - In Combat - Cast \'Plague Strike\''), +(29738, 0, 1, 0, 60, 0, 100, 0, 0, 0, 30000, 30000, 0, 0, 11, 50689, 32, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Death Knight Master - On Update - Cast \'Blood Presence\''), +(29722, 0, 0, 0, 0, 0, 100, 0, 2000, 4000, 18000, 22000, 0, 0, 11, 30639, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Rabid Cannibal - In Combat - Cast \'Carnivorous Bite\''); From 91c2e402329397f07976f1699c5626dc97292aef Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 2 Sep 2025 06:35:21 +0000 Subject: [PATCH 079/155] chore(DB): import pending files Referenced commit(s): 201a84fe851b9febeed7472f091b9e132a4dd079 --- .../Various_AI.sql => db_world/2025_09_02_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Various_AI.sql => db_world/2025_09_02_00.sql} (97%) diff --git a/data/sql/updates/pending_db_world/Various_AI.sql b/data/sql/updates/db_world/2025_09_02_00.sql similarity index 97% rename from data/sql/updates/pending_db_world/Various_AI.sql rename to data/sql/updates/db_world/2025_09_02_00.sql index 04a68b7f6..08ea0a4fe 100644 --- a/data/sql/updates/pending_db_world/Various_AI.sql +++ b/data/sql/updates/db_world/2025_09_02_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_08_30_03 -> 2025_09_02_00 -- Morbid Carcass, Vault Geist, Rabid Cannibal, Death Knight Master UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (29719, 29720, 29722, 29738)); From e26421d2bc7d5a2e44c8a40fe5e811402fac2c89 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Tue, 2 Sep 2025 17:42:04 -0400 Subject: [PATCH 080/155] fix(DB/Creature): Add spawn for Bubulo Acerbus. (#22768) --- data/sql/updates/pending_db_world/gnome-if-quartermaster.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/gnome-if-quartermaster.sql diff --git a/data/sql/updates/pending_db_world/gnome-if-quartermaster.sql b/data/sql/updates/pending_db_world/gnome-if-quartermaster.sql new file mode 100644 index 000000000..18dd6729f --- /dev/null +++ b/data/sql/updates/pending_db_world/gnome-if-quartermaster.sql @@ -0,0 +1,3 @@ +DELETE FROM `creature` WHERE `guid` = 1741 AND `id1` = 14724; +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES +(1741, 14724, 0, 0, 0, 0, 0, 1, 1, 0, -4823.649, -1299.3605, 501.95117, 0.959931075572967529, 300, 0, 0, 1220, 0, 0, 0, 0, 0, '', 45613, 1, NULL); From 4308fa20f23376ade768cf12cdf4bb38dac935a2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 2 Sep 2025 21:43:09 +0000 Subject: [PATCH 081/155] chore(DB): import pending files Referenced commit(s): e26421d2bc7d5a2e44c8a40fe5e811402fac2c89 --- .../gnome-if-quartermaster.sql => db_world/2025_09_02_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/gnome-if-quartermaster.sql => db_world/2025_09_02_01.sql} (93%) diff --git a/data/sql/updates/pending_db_world/gnome-if-quartermaster.sql b/data/sql/updates/db_world/2025_09_02_01.sql similarity index 93% rename from data/sql/updates/pending_db_world/gnome-if-quartermaster.sql rename to data/sql/updates/db_world/2025_09_02_01.sql index 18dd6729f..58a544d75 100644 --- a/data/sql/updates/pending_db_world/gnome-if-quartermaster.sql +++ b/data/sql/updates/db_world/2025_09_02_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_02_00 -> 2025_09_02_01 DELETE FROM `creature` WHERE `guid` = 1741 AND `id1` = 14724; INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `VerifiedBuild`, `CreateObject`, `Comment`) VALUES (1741, 14724, 0, 0, 0, 0, 0, 1, 1, 0, -4823.649, -1299.3605, 501.95117, 0.959931075572967529, 300, 0, 0, 1220, 0, 0, 0, 0, 0, '', 45613, 1, NULL); From f1cb5cc71bc9577ceadcddfd0855ab15dbf15751 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:15:40 -0300 Subject: [PATCH 082/155] =?UTF-8?q?fix(Core/SAI):=20Remove=20combat=20flag?= =?UTF-8?q?=20on=20evade=20for=20SAI-scripted=20charmed=20u=E2=80=A6=20(#2?= =?UTF-8?q?2775)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/AI/SmartScripts/SmartAI.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 4c2b252e2..f7e30d339 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -660,6 +660,7 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/) if (me->GetCharmerGUID().IsPlayer() || me->HasUnitFlag(UNIT_FLAG_POSSESSED)) { me->AttackStop(); + me->RemoveUnitFlag(UNIT_FLAG_IN_COMBAT); return; } From 66d43b794f4c612e3d2722acd88b8233d10cfd12 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 2 Sep 2025 20:15:52 -0300 Subject: [PATCH 083/155] fix(DB/SAI): Despawn Wooly Mammoth on dismount (#22776) --- data/sql/updates/pending_db_world/rev_1756853210914260300.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756853210914260300.sql diff --git a/data/sql/updates/pending_db_world/rev_1756853210914260300.sql b/data/sql/updates/pending_db_world/rev_1756853210914260300.sql new file mode 100644 index 000000000..a9c6d46ab --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756853210914260300.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28851) AND (`source_type` = 0) AND (`id` IN (2)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28851, 0, 2, 0, 28, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Enraged Mammoth - On Passenger Removed - Despawn In 3000 ms'); From 52f79d8dd7ca7df8b3f08ed5ec0a8c63b5e48931 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 2 Sep 2025 23:16:45 +0000 Subject: [PATCH 084/155] chore(DB): import pending files Referenced commit(s): f1cb5cc71bc9577ceadcddfd0855ab15dbf15751 --- .../rev_1756853210914260300.sql => db_world/2025_09_02_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756853210914260300.sql => db_world/2025_09_02_02.sql} (94%) diff --git a/data/sql/updates/pending_db_world/rev_1756853210914260300.sql b/data/sql/updates/db_world/2025_09_02_02.sql similarity index 94% rename from data/sql/updates/pending_db_world/rev_1756853210914260300.sql rename to data/sql/updates/db_world/2025_09_02_02.sql index a9c6d46ab..922f9cb8d 100644 --- a/data/sql/updates/pending_db_world/rev_1756853210914260300.sql +++ b/data/sql/updates/db_world/2025_09_02_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_02_01 -> 2025_09_02_02 -- DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28851) AND (`source_type` = 0) AND (`id` IN (2)); INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES From a575101751e968ce5f9120cdda832fbbbd02d610 Mon Sep 17 00:00:00 2001 From: Yehonal Date: Wed, 3 Sep 2025 03:31:09 +0200 Subject: [PATCH 085/155] feat(core): implement 31-bit safe petition_id for improved database integrity (#22774) --- .../rev_1756841669142726711.sql | 14 ++++ .../Implementation/CharacterDatabase.cpp | 10 +-- .../Implementation/CharacterDatabase.h | 4 +- src/server/game/Handlers/PetitionsHandler.cpp | 36 +++++---- src/server/game/Petitions/PetitionMgr.cpp | 77 +++++++++++++++++-- src/server/game/Petitions/PetitionMgr.h | 19 ++++- 6 files changed, 130 insertions(+), 30 deletions(-) create mode 100644 data/sql/updates/pending_db_characters/rev_1756841669142726711.sql diff --git a/data/sql/updates/pending_db_characters/rev_1756841669142726711.sql b/data/sql/updates/pending_db_characters/rev_1756841669142726711.sql new file mode 100644 index 000000000..42fb73711 --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1756841669142726711.sql @@ -0,0 +1,14 @@ +-- Add petition_id column to petition table +ALTER TABLE `petition` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`; +-- Populate petition_id based on petitionguid +UPDATE `petition` SET `petition_id` = CASE WHEN `petitionguid` <= 2147483647 THEN `petitionguid` ELSE `petitionguid` - 2147483648 END WHERE `petition_id` = 0; +-- Add index on petition_id +ALTER TABLE `petition` ADD INDEX `idx_petition_id` (`petition_id`); +-- Add petition_id column to petition_sign table +ALTER TABLE `petition_sign` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`; +-- Populate petition_id in petition_sign from petition table +UPDATE `petition_sign` AS `ps` JOIN `petition` AS `p` ON `p`.`petitionguid` = `ps`.`petitionguid` SET `ps`.`petition_id` = `p`.`petition_id` WHERE `ps`.`petition_id` = 0; +-- Add index on petition_id and playerguid in petition_sign +ALTER TABLE `petition_sign` ADD INDEX `idx_petition_id_player` (`petition_id`, `playerguid`); +-- Update enchantments in item_instance with petition_id prefix +UPDATE `item_instance` AS `ii` JOIN `petition` AS `p` ON `p`.`petitionguid` = `ii`.`guid` SET `ii`.`enchantments` = CONCAT(`p`.`petition_id`, SUBSTRING(`ii`.`enchantments`, LOCATE(' ', `ii`.`enchantments`))) WHERE `ii`.`enchantments` IS NOT NULL AND `ii`.`enchantments` <> ''; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index ec080b784..27a8b8940 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -353,8 +353,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_REM_AT_LOGIN_FLAG, "UPDATE characters set at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ALL_AT_LOGIN_FLAGS, "UPDATE characters SET at_login = at_login | ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_BUG_REPORT, "INSERT INTO bugreport (type, content) VALUES(?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petitionguid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petitionguid, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_PETITION_NAME, "UPDATE petition SET name = ? WHERE petition_id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PETITION_SIGNATURE, "INSERT INTO petition_sign (ownerguid, petition_id, playerguid, player_account) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ACCOUNT_ONLINE, "UPDATE characters SET online = 0 WHERE account = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_GROUP, "INSERT INTO `groups` (guid, leaderGuid, lootMethod, looterGuid, lootThreshold, icon1, icon2, icon3, icon4, icon5, icon6, icon7, icon8, groupType, difficulty, raidDifficulty, masterLooterGuid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_REP_GROUP_MEMBER, "REPLACE INTO group_member (guid, memberGuid, memberFlags, subgroup, roles) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC); @@ -456,9 +456,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_CHAR_GIFT, "INSERT INTO character_gifts (guid, item_guid, entry, flags) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INSTANCE_BY_INSTANCE, "DELETE FROM instance WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_MAIL_ITEM_BY_ID, "DELETE FROM mail_items WHERE mail_id = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_PETITION_BY_GUID, "DELETE FROM petition WHERE petitionguid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID, "DELETE FROM petition_sign WHERE petitionguid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (petition_id, ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PETITION_BY_ID, "DELETE FROM petition WHERE petition_id = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_PETITION_SIGNATURE_BY_ID, "DELETE FROM petition_sign WHERE petition_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_DECLINED_NAME, "DELETE FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_CHAR_DECLINED_NAME, "INSERT INTO character_declinedname (guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_RACE, "UPDATE characters SET race = ? WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index e7e05e473..b378134c3 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -381,8 +381,8 @@ enum CharacterDatabaseStatements : uint32 CHAR_DEL_INSTANCE_BY_INSTANCE, CHAR_DEL_MAIL_ITEM_BY_ID, CHAR_INS_PETITION, - CHAR_DEL_PETITION_BY_GUID, - CHAR_DEL_PETITION_SIGNATURE_BY_GUID, + CHAR_DEL_PETITION_BY_ID, + CHAR_DEL_PETITION_SIGNATURE_BY_ID, CHAR_DEL_CHAR_DECLINED_NAME, CHAR_INS_CHAR_DECLINED_NAME, CHAR_UPD_CHAR_RACE, diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index 796144a13..7ff2091d4 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -183,7 +183,9 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData) if (!charter) return; - charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, charter->GetGUID().GetCounter()); + // Use a 31-bit safe petition id instead of the raw item guid + uint32 petitionId = sPetitionMgr->GeneratePetitionId(); + charter->SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1, petitionId); // ITEM_FIELD_ENCHANTMENT_1_1 is guild/arenateam id // ITEM_FIELD_ENCHANTMENT_1_1+1 is current signatures count (showed on item) charter->SetState(ITEM_CHANGED, _player); @@ -211,16 +213,18 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket& recvData) // xinef: petition pointer is invalid from now on CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION); - stmt->SetData(0, _player->GetGUID().GetCounter()); - stmt->SetData(1, charter->GetGUID().GetCounter()); - stmt->SetData(2, name); - stmt->SetData(3, uint8(type)); + // petition_id, ownerguid, petitionguid(item guid), name, type + stmt->SetData(0, petitionId); + stmt->SetData(1, _player->GetGUID().GetCounter()); + stmt->SetData(2, charter->GetGUID().GetCounter()); + stmt->SetData(3, name); + stmt->SetData(4, uint8(type)); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); - // xinef: fill petition store - sPetitionMgr->AddPetition(charter->GetGUID(), _player->GetGUID(), name, uint8(type)); + // xinef: fill petition store (include petitionId) + sPetitionMgr->AddPetition(charter->GetGUID(), _player->GetGUID(), name, uint8(type), petitionId); } void WorldSession::HandlePetitionShowSignOpcode(WorldPacket& recvData) @@ -249,7 +253,7 @@ void WorldSession::HandlePetitionShowSignOpcode(WorldPacket& recvData) WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8 + 8 + 4 + 1 + signs * 12)); data << petitionguid; // petition guid data << _player->GetGUID(); // owner guid - data << uint32(petitionguid.GetCounter()); // guild guid + data << uint32(petition->petitionId); // guild/team id (31-bit safe) data << uint8(signs); // sign's count if (signs) @@ -286,7 +290,7 @@ void WorldSession::SendPetitionQueryOpcode(ObjectGuid petitionguid) uint8 type = petition->petitionType; WorldPacket data(SMSG_PETITION_QUERY_RESPONSE, (4 + 8 + petition->petitionName.size() + 1 + 1 + 4 * 12 + 2 + 10)); - data << uint32(petitionguid.GetCounter()); // guild/team guid (in Trinity always same as petition low guid + data << uint32(petition->petitionId); // guild/team id (was item low guid) data << petition->ownerGuid; // charter owner guid data << petition->petitionName; // name (guild/arena team) data << uint8(0); // some string @@ -373,7 +377,7 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket& recvData) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PETITION_NAME); stmt->SetData(0, newName); - stmt->SetData(1, petitionGuid.GetCounter()); + stmt->SetData(1, petition->petitionId); CharacterDatabase.Execute(stmt); @@ -497,7 +501,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket& recvData) CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PETITION_SIGNATURE); stmt->SetData(0, petition->ownerGuid.GetCounter()); - stmt->SetData(1, petitionGuid.GetCounter()); + stmt->SetData(1, petition->petitionId); stmt->SetData(2, playerGuid.GetCounter()); stmt->SetData(3, GetAccountId()); @@ -625,7 +629,7 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket& recvData) WorldPacket data(SMSG_PETITION_SHOW_SIGNATURES, (8 + 8 + 4 + signs + signs * 12)); data << petitionguid; // petition guid data << _player->GetGUID(); // owner guid - data << uint32(petitionguid.GetCounter()); // guild guid + data << uint32(petition->petitionId); // guild/team id (31-bit safe) data << uint8(signs); // sign's count if (signs) @@ -790,12 +794,12 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket& recvData) CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_GUID); - stmt->SetData(0, petitionGuid.GetCounter()); + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_BY_ID); + stmt->SetData(0, petition->petitionId); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_GUID); - stmt->SetData(0, petitionGuid.GetCounter()); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PETITION_SIGNATURE_BY_ID); + stmt->SetData(0, petition->petitionId); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); diff --git a/src/server/game/Petitions/PetitionMgr.cpp b/src/server/game/Petitions/PetitionMgr.cpp index a3c5c69ff..7e946923e 100644 --- a/src/server/game/Petitions/PetitionMgr.cpp +++ b/src/server/game/Petitions/PetitionMgr.cpp @@ -41,8 +41,9 @@ void PetitionMgr::LoadPetitions() { uint32 oldMSTime = getMSTime(); PetitionStore.clear(); + PetitionIdToItemGuid.clear(); - QueryResult result = CharacterDatabase.Query("SELECT ownerguid, petitionguid, name, type FROM petition"); + QueryResult result = CharacterDatabase.Query("SELECT ownerguid, petitionguid, name, type, petition_id FROM petition"); if (!result) { LOG_WARN("server.loading", ">> Loaded 0 Petitions!"); @@ -51,13 +52,24 @@ void PetitionMgr::LoadPetitions() } uint32 count = 0; + uint32 maxId = 0; do { Field* fields = result->Fetch(); - AddPetition(ObjectGuid::Create(fields[1].Get()), ObjectGuid::Create(fields[0].Get()), fields[2].Get(), fields[3].Get()); + uint32 itemLow = fields[1].Get(); + uint32 petitionId = fields[4].Get(); + ObjectGuid itemGuid = ObjectGuid::Create(itemLow); + ObjectGuid ownerGuid = ObjectGuid::Create(fields[0].Get()); + AddPetition(itemGuid, ownerGuid, fields[2].Get(), fields[3].Get(), petitionId); + PetitionIdToItemGuid[petitionId] = itemGuid; + if (petitionId > maxId) + maxId = petitionId; ++count; } while (result->NextRow()); + // initialize next id (keep within 31-bit safe range) + _nextPetitionId = std::min(std::max(maxId + 1, 1), 0x7FFFFFFFu); + LOG_INFO("server.loading", ">> Loaded {} Petitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); } @@ -67,7 +79,7 @@ void PetitionMgr::LoadSignatures() uint32 oldMSTime = getMSTime(); SignatureStore.clear(); - QueryResult result = CharacterDatabase.Query("SELECT petitionguid, playerguid, player_account FROM petition_sign"); + QueryResult result = CharacterDatabase.Query("SELECT petition_id, playerguid, player_account FROM petition_sign"); if (!result) { LOG_WARN("server.loading", ">> Loaded 0 Petition signs!"); @@ -79,7 +91,11 @@ void PetitionMgr::LoadSignatures() do { Field* fields = result->Fetch(); - AddSignature(ObjectGuid::Create(fields[0].Get()), fields[2].Get(), ObjectGuid::Create(fields[1].Get())); + uint32 petitionId = fields[0].Get(); + auto it = PetitionIdToItemGuid.find(petitionId); + if (it == PetitionIdToItemGuid.end()) + continue; // orphan signature, skip + AddSignature(it->second, fields[2].Get(), ObjectGuid::Create(fields[1].Get())); ++count; } while (result->NextRow()); @@ -87,10 +103,11 @@ void PetitionMgr::LoadSignatures() LOG_INFO("server.loading", " "); } -void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type) +void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type, uint32 petitionId) { Petition& p = PetitionStore[petitionGUID]; p.petitionGuid = petitionGUID; + p.petitionId = petitionId; p.ownerGuid = ownerGuid; p.petitionName = name; p.petitionType = type; @@ -102,7 +119,12 @@ void PetitionMgr::AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std void PetitionMgr::RemovePetition(ObjectGuid petitionGUID) { - PetitionStore.erase(petitionGUID); + auto it = PetitionStore.find(petitionGUID); + if (it != PetitionStore.end()) + { + PetitionIdToItemGuid.erase(it->second.petitionId); + PetitionStore.erase(it); + } // remove signatures SignatureStore.erase(petitionGUID); @@ -143,6 +165,15 @@ Petition const* PetitionMgr::GetPetition(ObjectGuid petitionGUID) const return nullptr; } +Petition const* PetitionMgr::GetPetitionById(uint32 petitionId) const +{ + auto it = PetitionIdToItemGuid.find(petitionId); + if (it == PetitionIdToItemGuid.end()) + return nullptr; + + return GetPetition(it->second); +} + Petition const* PetitionMgr::GetPetitionByOwnerWithType(ObjectGuid ownerGuid, uint8 type) const { for (PetitionContainer::const_iterator itr = PetitionStore.begin(); itr != PetitionStore.end(); ++itr) @@ -189,3 +220,37 @@ void PetitionMgr::RemoveSignaturesByPlayerAndType(ObjectGuid playerGuid, uint8 t itr->second.signatureMap.erase(signItr); } } + +uint32 PetitionMgr::GeneratePetitionId() +{ + // ensure 31-bit range and avoid collisions with already loaded petitions + if (_nextPetitionId == 0 || _nextPetitionId >= 0x7FFFFFFF) + _nextPetitionId = 1; + + // find first free id + while (PetitionIdToItemGuid.count(_nextPetitionId)) + { + ++_nextPetitionId; + if (_nextPetitionId >= 0x7FFFFFFF) + _nextPetitionId = 1; + } + + uint32 id = _nextPetitionId++; + if (_nextPetitionId >= 0x7FFFFFFF) + _nextPetitionId = 1; + return id; +} + +uint32 PetitionMgr::GetPetitionIdByItemGuid(ObjectGuid petitionItemGuid) const +{ + Petition const* p = GetPetition(petitionItemGuid); + return p ? p->petitionId : 0; +} + +ObjectGuid PetitionMgr::GetItemGuidByPetitionId(uint32 petitionId) const +{ + auto it = PetitionIdToItemGuid.find(petitionId); + if (it == PetitionIdToItemGuid.end()) + return ObjectGuid::Empty; + return it->second; +} diff --git a/src/server/game/Petitions/PetitionMgr.h b/src/server/game/Petitions/PetitionMgr.h index 97491fb3a..cd7d47ead 100644 --- a/src/server/game/Petitions/PetitionMgr.h +++ b/src/server/game/Petitions/PetitionMgr.h @@ -20,6 +20,7 @@ #include "ObjectGuid.h" +#include #include #define CHARTER_DISPLAY_ID 16161 @@ -37,14 +38,21 @@ typedef std::map SignatureMap; struct Petition { + // Item GUID of the charter item (used to find the item in inventory) ObjectGuid petitionGuid; + // New 31-bit safe petition identifier used in packets/DB relations + uint32 petitionId; + // Owner character GUID ObjectGuid ownerGuid; + // Petition type (guild / arena) uint8 petitionType; + // Name associated with the petition (guild/arena name) std::string petitionName; }; struct Signatures { + // Keep keying by item-guid for backward compatibility in code paths ObjectGuid petitionGuid; SignatureMap signatureMap; }; @@ -65,10 +73,11 @@ public: void LoadSignatures(); // Petitions - void AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type); + void AddPetition(ObjectGuid petitionGUID, ObjectGuid ownerGuid, std::string const& name, uint8 type, uint32 petitionId); void RemovePetition(ObjectGuid petitionGUID); void RemovePetitionByOwnerAndType(ObjectGuid ownerGuid, uint8 type); Petition const* GetPetition(ObjectGuid petitionGUID) const; + Petition const* GetPetitionById(uint32 petitionId) const; Petition const* GetPetitionByOwnerWithType(ObjectGuid ownerGuid, uint8 type) const; PetitionContainer* GetPetitionStore() { return &PetitionStore; } @@ -79,9 +88,17 @@ public: Signatures const* GetSignature(ObjectGuid petitionGUID) const; SignatureContainer* GetSignatureStore() { return &SignatureStore; } + uint32 GeneratePetitionId(); + uint32 GetPetitionIdByItemGuid(ObjectGuid petitionItemGuid) const; + ObjectGuid GetItemGuidByPetitionId(uint32 petitionId) const; + protected: PetitionContainer PetitionStore; SignatureContainer SignatureStore; + // Mapping id -> item-guid to support DB-id lookups + std::unordered_map PetitionIdToItemGuid; + // Next petition id (kept < 2^31) + uint32 _nextPetitionId = 1; }; #define sPetitionMgr PetitionMgr::instance() From b225e0004296062c1012cf212f81c8191d850018 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Sep 2025 01:35:02 +0000 Subject: [PATCH 086/155] chore(DB): import pending files Referenced commit(s): a575101751e968ce5f9120cdda832fbbbd02d610 --- .../2025_09_03_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_characters/rev_1756841669142726711.sql => db_characters/2025_09_03_00.sql} (96%) diff --git a/data/sql/updates/pending_db_characters/rev_1756841669142726711.sql b/data/sql/updates/db_characters/2025_09_03_00.sql similarity index 96% rename from data/sql/updates/pending_db_characters/rev_1756841669142726711.sql rename to data/sql/updates/db_characters/2025_09_03_00.sql index 42fb73711..c9ae6307d 100644 --- a/data/sql/updates/pending_db_characters/rev_1756841669142726711.sql +++ b/data/sql/updates/db_characters/2025_09_03_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_29_00 -> 2025_09_03_00 -- Add petition_id column to petition table ALTER TABLE `petition` ADD COLUMN `petition_id` INT UNSIGNED NOT NULL DEFAULT 0 AFTER `petitionguid`; -- Populate petition_id based on petitionguid From 0f805db8bac4d5c9cbd4fbd856e933a48379a19c Mon Sep 17 00:00:00 2001 From: sudlud Date: Wed, 3 Sep 2025 06:13:18 +0200 Subject: [PATCH 087/155] fix(DB/Gameobject): Sniffed Values for 'various doodad' spawns (#22773) --- .../rev_1756700338678545400.sql | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756700338678545400.sql diff --git a/data/sql/updates/pending_db_world/rev_1756700338678545400.sql b/data/sql/updates/pending_db_world/rev_1756700338678545400.sql new file mode 100644 index 000000000..9d8c33842 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756700338678545400.sql @@ -0,0 +1,150 @@ +-- Update gameobject 'various doodad' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (193692, 193766, 193765, 193764, 193763, 193762, 193761, 193760, 193759, 193758, 193757, 193756, 193755, 193754, 193753, 193752, 193751, 193750, 193749, 193748, 193747, 193746, 193745, 193744, 193743, 193742, 193741, 193740, 193739, 193738, 193737, 193736, 193735, 193734, 193733, 193732, 193731, 193730, 193729, 193728, 193727, 193726, 193725, 193724, 193723, 193722, 193721, 193720, 193719, 193718, 193717, 193716, 193715, 193714, 193713, 193712, 193711, 193710, 193709, 193708, 193707, 193706, 193705, 193704, 193703, 193702, 193701, 193700, 193699, 193698, 193697, 193696, 193693, 193691, 193678, 193677, 193676, 193674, 193669, 193668, 193666, 193665, 193664, 193663, 193659, 193658, 193657, 193662, 193660, 193661, 193654, 193652, 193651, 193649, 193650, 193671, 193670, 193645, 193644, 193643, 193642, 193637, 193632, 193631, 193630, 193653, 193636, 193635, 193634, 193633, 193681, 193680)) AND (`guid` IN (255513, 268696, 268697, 268698, 268699, 268700, 268701, 268702, 268703, 268704, 268705, 268706, 268707, 268708, 268709, 268710, 268711, 268712, 268713, 268714, 268715, 268716, 268717, 268718, 268719, 268720, 268721, 268722, 268723, 268724, 268725, 268726, 268727, 268728, 268729, 268730, 268731, 268732, 268733, 268734, 268735, 268736, 268737, 268738, 268739, 268740, 268741, 268742, 268743, 268744, 268745, 268746, 268747, 268748, 268749, 268750, 268751, 268752, 268753, 268754, 268755, 268756, 268757, 268758, 268759, 268760, 268761, 268762, 268763, 268764, 268765, 268766, 268772, 268773, 268774, 268775, 268779, 268780, 268781, 268782, 268783, 268784, 268785, 268786, 268790, 268791, 268792, 268793, 268794, 268795, 268799, 268800, 268807, 268808, 268809, 268815, 268816, 268818, 268819, 268820, 268821, 268822, 268825, 268826, 268827, 268829, 268832, 268833, 268834, 268835, 268839, 268840)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(255513, 193692, 571, 0, 0, 1, 1, 7882.8623046875, 2043.8250732421875, 600.73297119140625, 4.665524959564208984, 0.187406539916992187, -0.31766891479492187, -0.64971733093261718, 0.66470491886138916, 120, 255, 1, "", 46368, NULL), +(268696, 193766, 571, 0, 0, 1, 1, 7637.41064453125, 2072.35791015625, 600.27178955078125, 1.248236894607543945, 0.51220560073852539, 0.357756614685058593, 0.46212005615234375, 0.629365265369415283, 120, 255, 1, "", 50664, NULL), +(268697, 193765, 571, 0, 0, 1, 1, 7635.556640625, 2046.7196044921875, 601.6680908203125, 1.269469738006591796, -0.03150653839111328, 0.077538490295410156, 0.589531898498535156, 0.803397297859191894, 120, 255, 1, "", 50664, NULL), +(268698, 193764, 571, 0, 0, 1, 1, 7625.869140625, 2060.052978515625, 604.26983642578125, 0.078540004789829254, 0, 0, 0.039259910583496093, 0.999229073524475097, 120, 255, 1, "", 50664, NULL), +(268699, 193763, 571, 0, 0, 1, 1, 7625.7724609375, 2060.06494140625, 600.8868408203125, 0.063891783356666564, 0, 0, 0.031940460205078125, 0.999489784240722656, 120, 255, 1, "", 50664, NULL), +(268700, 193762, 571, 0, 0, 1, 1, 7625.6640625, 2060.03564453125, 604.1954345703125, 3.22885894775390625, 0, 0, -0.99904823303222656, 0.043619260191917419, 120, 255, 1, "", 50664, NULL), +(268701, 193761, 571, 0, 0, 1, 1, 7629.734375, 2062.71484375, 600.2579345703125, 2.95832991600036621, 0, 0, 0.995804786682128906, 0.091503240168094635, 120, 255, 1, "", 50664, NULL), +(268702, 193760, 571, 0, 0, 1, 1, 7630.5419921875, 2062.549560546875, 600.24835205078125, 4.249876976013183593, 0, 0, -0.85035228729248046, 0.526213824748992919, 120, 255, 1, "", 50664, NULL), +(268703, 193759, 571, 0, 0, 1, 1, 7629.98193359375, 2061.771240234375, 600.24383544921875, 3.351046562194824218, 0, 0, -0.99452114105224609, 0.104535527527332305, 120, 255, 1, "", 50664, NULL), +(268704, 193758, 571, 0, 0, 1, 1, 7628.8798828125, 2060.107666015625, 600.49481201171875, 4.721115589141845703, 0, 0, -0.70401477813720703, 0.71018528938293457, 120, 255, 1, "", 50664, NULL), +(268705, 193757, 571, 0, 0, 1, 1, 7628.60400390625, 2060.201171875, 599.6319580078125, 4.616395950317382812, 0, 0, -0.74021816253662109, 0.672366797924041748, 120, 255, 1, "", 50664, NULL), +(268706, 193756, 571, 0, 0, 1, 1, 7628.60400390625, 2060.201171875, 598.53387451171875, 6.143558979034423828, 0, 0, -0.06975650787353515, 0.997564077377319335, 120, 255, 1, "", 50664, NULL), +(268707, 193755, 571, 0, 0, 1, 1, 7617.7353515625, 2050.1357421875, 600.6690673828125, 5.480334281921386718, 0, 0, -0.39073085784912109, 0.920504987239837646, 120, 255, 1, "", 50664, NULL), +(268708, 193754, 571, 0, 0, 1, 1, 7630.58837890625, 2060.197998046875, 600.16461181640625, 0.017452461645007133, 0, 0, 0.008726119995117187, 0.999961912631988525, 120, 255, 1, "", 50664, NULL), +(268709, 193753, 571, 0, 0, 1, 1, 7615.00048828125, 2053.274658203125, 601.31890869140625, 4.834563255310058593, -0.07124805450439453, -0.11837959289550781, -0.65368366241455078, 0.744048118591308593, 120, 255, 1, "", 50664, NULL), +(268710, 193752, 571, 0, 0, 1, 1, 7614.63818359375, 2053.234130859375, 601.3212890625, 4.1538848876953125, 0, 0, -0.8746194839477539, 0.484810054302215576, 120, 255, 1, "", 50664, NULL), +(268711, 193751, 571, 0, 0, 1, 1, 7614.73583984375, 2052.607177734375, 601.3494873046875, 5.52397012710571289, 0.025708198547363281, 0.1858978271484375, -0.35620498657226562, 0.915368258953094482, 120, 255, 1, "", 50664, NULL), +(268712, 193750, 571, 0, 0, 1, 1, 7614.7763671875, 2052.47705078125, 600.12860107421875, 0.759216904640197753, 0, 0, 0.370556831359863281, 0.928809821605682373, 120, 255, 1, "", 50664, NULL), +(268713, 193749, 571, 0, 0, 1, 1, 7621.33642578125, 2045.822998046875, 600.0079345703125, 5.864306926727294921, 0, 0, -0.20791149139404296, 0.978147625923156738, 120, 255, 1, "", 50664, NULL), +(268714, 193748, 571, 0, 0, 1, 1, 7621.1689453125, 2048.1103515625, 600.03460693359375, 3.194002151489257812, 0, 0, -0.99965667724609375, 0.026201646775007247, 120, 255, 1, "", 50664, NULL), +(268715, 193747, 571, 0, 0, 1, 1, 7619.73583984375, 2046.9991455078125, 600.139892578125, 4.127707481384277343, 0, 0, -0.880889892578125, 0.473321229219436645, 120, 255, 1, "", 50664, NULL), +(268716, 193746, 571, 0, 0, 1, 1, 7611.34130859375, 2059.42138671875, 600.2303466796875, 0.680676698684692382, 0, 0, 0.333806037902832031, 0.942641794681549072, 120, 255, 1, "", 50664, NULL), +(268717, 193745, 571, 0, 0, 1, 1, 7611.443359375, 2061.458740234375, 600.2303466796875, 2.076939344406127929, 0, 0, 0.861628532409667968, 0.50753939151763916, 120, 255, 1, "", 50664, NULL), +(268718, 193744, 571, 0, 0, 1, 1, 7611.46630859375, 2060.24267578125, 601.098876953125, 1.474833250045776367, 0.740206718444824218, 0.672366142272949218, 0.004041671752929687, 0.001196039956994354, 120, 255, 1, "", 50664, NULL), +(268719, 193743, 571, 0, 0, 1, 1, 7629.513671875, 2043.40283203125, 600.21649169921875, 2.303830623626708984, 0, 0, 0.913544654846191406, 0.406738430261611938, 120, 255, 1, "", 50664, NULL), +(268720, 193742, 571, 0, 0, 1, 1, 7627.474609375, 2043.3980712890625, 600.215576171875, 3.70009779930114746, 0, 0, -0.96126174926757812, 0.275637149810791015, 120, 255, 1, "", 50664, NULL), +(268721, 193741, 571, 0, 0, 1, 1, 7628.4716796875, 2043.3489990234375, 601.08172607421875, 3.097992658615112304, -0.02180719375610351, -0.99975299835205078, -0.00331974029541015, 0.002718634903430938, 120, 255, 1, "", 50664, NULL), +(268722, 193740, 571, 0, 0, 1, 1, 7620.10205078125, 2074.37548828125, 601.4208984375, 6.012660503387451171, 0, 0, -0.13485050201416015, 0.990865945816040039, 120, 255, 1, "", 50664, NULL), +(268723, 193739, 571, 0, 0, 1, 1, 7621.29052734375, 2074.291748046875, 601.4190673828125, 0.157077088952064514, 0, 0, 0.078457832336425781, 0.996917426586151123, 120, 255, 1, "", 50664, NULL), +(268724, 193738, 571, 0, 0, 1, 1, 7621.4072265625, 2072.64208984375, 601.4334716796875, 1.954769015312194824, 0, 0, 0.829037666320800781, 0.559192776679992675, 120, 255, 1, "", 50664, NULL), +(268725, 193737, 571, 0, 0, 1, 1, 7620.9501953125, 2073.94580078125, 600.21722412109375, 5.253442287445068359, 0, 0, -0.49242305755615234, 0.870355963706970214, 120, 255, 1, "", 50664, NULL), +(268726, 193736, 571, 0, 0, 1, 1, 7618.22607421875, 2070.371337890625, 600.55718994140625, 3.892086982727050781, 0, 0, -0.93041706085205078, 0.366502493619918823, 120, 255, 1, "", 50664, NULL), +(268727, 193735, 571, 0, 0, 1, 1, 7613.96484375, 2068.400634765625, 600.26336669921875, 5.410520076751708984, 0.18164825439453125, -0.05159950256347656, -0.42017078399658203, 0.887579798698425292, 120, 255, 1, "", 50664, NULL), +(268728, 193734, 571, 0, 0, 1, 1, 7614.1796875, 2069.150146484375, 600.2694091796875, 2.277705669403076171, 0.4829254150390625, 0.523255348205566406, -0.48152923583984375, 0.510995566844940185, 120, 255, 1, "", 50664, NULL), +(268729, 193733, 571, 0, 0, 1, 1, 7614.11865234375, 2068.62841796875, 600.2442626953125, 2.862335443496704101, 0, 0, 0.990267753601074218, 0.139175355434417724, 120, 255, 1, "", 50664, NULL), +(268730, 193732, 571, 0, 0, 1, 1, 7613.57958984375, 2067.849365234375, 600.361083984375, 0.758936166763305664, 0.710488319396972656, 0.047131538391113281, -0.70103359222412109, 0.039202630519866943, 120, 255, 1, "", 50664, NULL), +(268731, 193731, 571, 0, 0, 1, 1, 7613.70166015625, 2066.873779296875, 600.338623046875, 5.733424663543701171, -0.62461566925048828, 0.353822708129882812, 0.599942207336425781, 0.353177070617675781, 120, 255, 1, "", 50664, NULL), +(268732, 193730, 571, 0, 0, 1, 1, 7613.63330078125, 2067.5244140625, 600.229736328125, 2.809975385665893554, 0, 0, 0.986285209655761718, 0.165049895644187927, 120, 255, 1, "", 50664, NULL), +(268733, 193729, 571, 0, 0, 1, 1, 7627.837890625, 2076.871337890625, 600.24664306640625, 5.471607208251953125, 0, 0, -0.39474391937255859, 0.918791174888610839, 120, 255, 1, "", 50664, NULL), +(268734, 193728, 571, 0, 0, 1, 1, 7628.87841796875, 2076.95263671875, 601.11578369140625, 6.265766620635986328, 0.999953269958496093, -0.00871753692626953, 0.00246429443359375, 0.00337409065105021, 120, 255, 1, "", 50664, NULL), +(268735, 193727, 571, 0, 0, 1, 1, 7629.87646484375, 2076.929931640625, 600.24664306640625, 0.584683895111083984, 0, 0, 0.288195610046386718, 0.957571566104888916, 120, 255, 1, "", 50664, NULL), +(268736, 193726, 571, 0, 0, 1, 1, 7644.1845703125, 2052.05908203125, 600.23858642578125, 2.085667610168457031, 0, 0, 0.863835334777832031, 0.503774285316467285, 120, 255, 1, "", 50664, NULL), +(268737, 193725, 571, 0, 0, 1, 1, 7644.37548828125, 2052.462158203125, 600.2384033203125, 4.598945140838623046, 0, 0, -0.74605655670166015, 0.665882587432861328, 120, 255, 1, "", 50664, NULL), +(268738, 193724, 571, 0, 0, 1, 1, 7644.04248046875, 2052.38671875, 600.23297119140625, 0.706856131553649902, 0.038586616516113281, -0.14088153839111328, 0.340669631958007812, 0.928766727447509765, 120, 255, 1, "", 50664, NULL), +(268739, 193723, 571, 0, 0, 1, 1, 7636.345703125, 2046.3046875, 601.35015869140625, 0.575957715511322021, -0.09489917755126953, 0.161904335021972656, 0.286963462829589843, 0.939379096031188964, 120, 255, 1, "", 50664, NULL), +(268740, 193722, 571, 0, 0, 1, 1, 7636.70751953125, 2047.2916259765625, 601.309326171875, 3.010666131973266601, 0, 0, 0.997858047485351562, 0.065416477620601654, 120, 255, 1, "", 50664, NULL), +(268741, 193721, 571, 0, 0, 1, 1, 7636.48095703125, 2046.31298828125, 600.129638671875, 2.094393253326416015, 0, 0, 0.866024971008300781, 0.50000077486038208, 120, 255, 1, "", 50664, NULL), +(268742, 193720, 571, 0, 0, 1, 1, 7639.40576171875, 2049.9814453125, 600.68109130859375, 0.750488698482513427, 0, 0, 0.366499900817871093, 0.930418074131011962, 120, 255, 1, "", 50664, NULL), +(268743, 193719, 571, 0, 0, 1, 1, 7642.1875, 2066.251953125, 601.51141357421875, 4.319693565368652343, 0.329238414764404296, -0.62578010559082031, -0.54365253448486328, 0.452154040336608886, 120, 255, 1, "", 50664, NULL), +(268744, 193718, 571, 0, 0, 1, 1, 7641.9541015625, 2051.512939453125, 600.240234375, 2.923415660858154296, 0, 0, 0.994055747985839843, 0.108872212469577789, 120, 255, 1, "", 50664, NULL), +(268745, 193717, 571, 0, 0, 1, 1, 7642.1845703125, 2051.5986328125, 600.46063232421875, 5.174901962280273437, 0, 0, -0.52621364593505859, 0.850352406501770019, 120, 255, 1, "", 50664, NULL), +(268746, 193716, 571, 0, 0, 1, 1, 7642.3935546875, 2051.588134765625, 600.239990234375, 3.63901376724243164, 0, 0, -0.96923065185546875, 0.246154293417930603, 120, 255, 1, "", 50664, NULL), +(268747, 193715, 571, 0, 0, 1, 1, 7642.1396484375, 2051.818115234375, 600.2618408203125, 5.349435329437255859, 0, 0, -0.45009803771972656, 0.892979145050048828, 120, 255, 1, "", 50664, NULL), +(268748, 193714, 571, 0, 0, 1, 1, 7642.29345703125, 2051.72412109375, 600.26397705078125, 4.860742568969726562, 0, 0, -0.65275955200195312, 0.757565200328826904, 120, 255, 1, "", 50664, NULL), +(268749, 193713, 571, 0, 0, 1, 1, 7642.54052734375, 2051.8837890625, 600.25, 1.844759345054626464, 0.050289154052734375, 0.201838493347167968, 0.769841194152832031, 0.603387713432312011, 120, 255, 1, "", 50664, NULL), +(268750, 193712, 571, 0, 0, 1, 1, 7642.7177734375, 2051.58984375, 600.26141357421875, 1.710421562194824218, 0, 0, 0.754709243774414062, 0.656059443950653076, 120, 255, 1, "", 50664, NULL), +(268751, 193711, 571, 0, 0, 1, 1, 7645.83544921875, 2061.108642578125, 600.2529296875, 3.848450660705566406, 0, 0, -0.93819141387939453, 0.346116840839385986, 120, 255, 1, "", 50664, NULL), +(268752, 193710, 571, 0, 0, 1, 1, 7645.78662109375, 2059.0693359375, 600.252685546875, 5.244716167449951171, 0, 0, -0.4962158203125, 0.86819922924041748, 120, 255, 1, "", 50664, NULL), +(268753, 193709, 571, 0, 0, 1, 1, 7645.86181640625, 2060.064697265625, 601.121826171875, 4.642610549926757812, 0.681998252868652343, -0.73134136199951171, -0.00068855285644531, 0.004207443445920944, 120, 255, 1, "", 50664, NULL), +(268754, 193708, 571, 0, 0, 1, 1, 7636.177734375, 2073.49462890625, 601.4434814453125, 3.420847892761230468, -0.21741914749145507, -0.08422660827636718, -0.96116828918457031, 0.1476154625415802, 120, 255, 1, "", 50664, NULL), +(268755, 193707, 571, 0, 0, 1, 1, 7639.47998046875, 2069.831787109375, 600.65679931640625, 2.347463846206665039, 0, 0, 0.922200202941894531, 0.386712819337844848, 120, 255, 1, "", 50664, NULL), +(268756, 193706, 571, 0, 0, 1, 1, 7642.2158203125, 2066.698486328125, 601.4727783203125, 5.715955257415771484, 0, 0, -0.27982807159423828, 0.960050106048583984, 120, 255, 1, "", 50664, NULL), +(268757, 193705, 571, 0, 0, 1, 1, 7641.5390625, 2066.837890625, 601.4727783203125, 0.401424884796142578, 0, 0, 0.199367523193359375, 0.979924798011779785, 120, 255, 1, "", 50664, NULL), +(268758, 193704, 571, 0, 0, 1, 1, 7642.36376953125, 2067.4638671875, 601.4591064453125, 3.395873546600341796, 0.056763648986816406, 0.014508247375488281, -0.99017143249511718, 0.126995846629142761, 120, 255, 1, "", 50664, NULL), +(268759, 193703, 571, 0, 0, 1, 1, 7642.8466796875, 2067.182861328125, 601.47882080078125, 2.1816558837890625, -0.18238639831542968, 0.044202804565429687, 0.875073432922363281, 0.446125298738479614, 120, 255, 1, "", 50664, NULL), +(268760, 193702, 571, 0, 0, 1, 1, 7643.08251953125, 2067.653564453125, 601.462158203125, 3.307400941848754882, -0.08287525177001953, 0.042493820190429687, -0.99254989624023437, 0.078553743660449981, 120, 255, 1, "", 50664, NULL), +(268761, 193701, 571, 0, 0, 1, 1, 7642.833984375, 2067.318359375, 600.256591796875, 3.70009779930114746, 0, 0, -0.96126174926757812, 0.275637149810791015, 120, 255, 1, "", 50664, NULL), +(268762, 193700, 571, 0, 0, 1, 1, 7638.7451171875, 2073.489501953125, 600.251220703125, 1.247907638549804687, 0, 0, 0.584248542785644531, 0.811574757099151611, 120, 255, 1, "", 50664, NULL), +(268763, 193699, 571, 0, 0, 1, 1, 7637.20556640625, 2073.30859375, 601.41949462890625, 4.347476005554199218, 0.06531381607055664, 0.67337799072265625, -0.33799266815185546, 0.654260754585266113, 120, 255, 1, "", 50664, NULL), +(268764, 193698, 571, 0, 0, 1, 1, 7636.033203125, 2073.449951171875, 600.2333984375, 4.354224681854248046, -0.00527000427246093, -0.02503776550292968, -0.82129764556884765, 0.569925904273986816, 120, 255, 1, "", 50664, NULL), +(268765, 193697, 571, 0, 0, 1, 1, 7635.75830078125, 2074.88037109375, 600.2703857421875, 3.333590030670166015, 0, 0, -0.99539566040039062, 0.095851235091686248, 120, 255, 1, "", 50664, NULL), +(268766, 193696, 571, 0, 0, 1, 1, 7637.640625, 2074.039306640625, 600.2724609375, 2.609261274337768554, 0, 0, 0.964786529541015625, 0.263034075498580932, 120, 255, 1, "", 50664, NULL), +(268772, 193693, 571, 0, 0, 1, 1, 7892.458984375, 2073.525146484375, 601.7738037109375, 5.521906852722167968, -0.67214488983154296, 0.21199798583984375, -0.31161308288574218, 0.637318909168243408, 120, 255, 1, "", 46368, NULL), +(268773, 193691, 571, 0, 0, 1, 1, 7891.21435546875, 2057.974853515625, 604.2218017578125, 3.124123096466064453, 0, 0, 0.99996185302734375, 0.008734640665352344, 120, 255, 1, "", 46368, NULL), +(268774, 193678, 571, 0, 0, 1, 1, 7891.45458984375, 2058.0087890625, 600.86529541015625, 0.011531894095242023, 0, 0, 0.005765914916992187, 0.999983370304107666, 120, 255, 1, "", 46368, NULL), +(268775, 193677, 571, 0, 0, 1, 1, 7891.65234375, 2058.012939453125, 604.251953125, 6.265733242034912109, 0, 0, -0.00872611999511718, 0.999961912631988525, 120, 255, 1, "", 46368, NULL), +(268779, 193676, 571, 0, 0, 1, 1, 7888.271484375, 2058.06640625, 600.46380615234375, 1.579522013664245605, 0, 0, 0.710185050964355468, 0.704015016555786132, 120, 255, 1, "", 46368, NULL), +(268780, 193674, 571, 0, 0, 1, 1, 7888.546875, 2057.972900390625, 598.50286865234375, 3.001946926116943359, 0, 0, 0.997563362121582031, 0.069766148924827575, 120, 255, 1, "", 46368, NULL), +(268781, 193669, 571, 0, 0, 1, 1, 7877.67138671875, 2048.341796875, 600.62579345703125, 5.489060401916503906, 0, 0, -0.38671112060546875, 0.922200918197631835, 120, 255, 1, "", 46368, NULL), +(268782, 193668, 571, 0, 0, 1, 1, 7886.5634765625, 2057.9765625, 600.13427734375, 3.159062385559082031, 0, 0, -0.99996185302734375, 0.008734640665352344, 120, 255, 1, "", 46368, NULL), +(268783, 193666, 571, 0, 0, 1, 1, 7874.21435546875, 2051.378173828125, 601.40032958984375, 5.759586811065673828, 0.030743122100830078, 0.030928611755371093, -0.25936603546142578, 0.964794039726257324, 120, 255, 1, "", 46368, NULL), +(268784, 193665, 571, 0, 0, 1, 1, 7874.3037109375, 2050.991455078125, 601.44683837890625, 5.32325601577758789, 0.044202804565429687, 0.182386398315429687, -0.4461221694946289, 0.875075042247772216, 120, 255, 1, "", 46368, NULL), +(268785, 193664, 571, 0, 0, 1, 1, 7874.00244140625, 2050.78515625, 601.4483642578125, 4.642575740814208984, -0.07862043380737304, -0.12896251678466796, -0.71886634826660156, 0.678541600704193115, 120, 255, 1, "", 46368, NULL), +(268786, 193663, 571, 0, 0, 1, 1, 7874.31689453125, 2050.85595703125, 600.2255859375, 0.558503925800323486, 0, 0, 0.275636672973632812, 0.961261868476867675, 120, 255, 1, "", 46368, NULL), +(268790, 193659, 571, 0, 0, 1, 1, 7871.31591796875, 2057.065185546875, 600.2220458984375, 0.706856250762939453, 0, 0, 0.346116065979003906, 0.938191711902618408, 120, 255, 1, "", 46368, NULL), +(268791, 193658, 571, 0, 0, 1, 1, 7871.3642578125, 2059.104248046875, 600.22412109375, 2.103119850158691406, 0, 0, 0.868198394775390625, 0.496217250823974609, 120, 255, 1, "", 46368, NULL), +(268792, 193657, 571, 0, 0, 1, 1, 7871.28857421875, 2058.10986328125, 601.09088134765625, 1.501015067100524902, 0.731341838836669921, 0.681998252868652343, 0.00405120849609375, 0.001035800902172923, 120, 255, 1, "", 46368, NULL), +(268793, 193662, 571, 0, 0, 1, 1, 7889.31298828125, 2041.3023681640625, 600.216796875, 2.33001255989074707, 0, 0, 0.918790817260742187, 0.394744753837585449, 120, 255, 1, "", 46368, NULL), +(268794, 193660, 571, 0, 0, 1, 1, 7887.27392578125, 2041.244140625, 600.21728515625, 3.72628021240234375, 0, 0, -0.95757102966308593, 0.288197338581085205, 120, 255, 1, "", 46368, NULL), +(268795, 193661, 571, 0, 0, 1, 1, 7888.27294921875, 2041.2213134765625, 601.0849609375, 3.12417149543762207, -0.00871896743774414, -0.99995326995849609, -0.00328731536865234, 0.002583741443231701, 120, 255, 1, "", 46368, NULL), +(268799, 193654, 571, 0, 0, 1, 1, 7880.66943359375, 2071.861328125, 600.09869384765625, 5.235987663269042968, 0, 0, -0.5, 0.866025388240814208, 120, 255, 1, "", 46368, NULL), +(268800, 193652, 571, 0, 0, 1, 1, 7877.7451171875, 2068.192626953125, 600.65087890625, 3.892086982727050781, 0, 0, -0.93041706085205078, 0.366502493619918823, 120, 255, 1, "", 46368, NULL), +(268807, 193651, 571, 0, 0, 1, 1, 7887.63720703125, 2074.77099609375, 600.18682861328125, 5.445429801940917968, 0, 0, -0.40673542022705078, 0.91354602575302124, 120, 255, 1, "", 46368, NULL), +(268808, 193649, 571, 0, 0, 1, 1, 7888.68017578125, 2074.82470703125, 601.05072021484375, 6.239586830139160156, 0.999753475189208984, -0.0218057632446289, 0.002421379089355468, 0.00341796875, 120, 255, 1, "", 46368, NULL), +(268809, 193650, 571, 0, 0, 1, 1, 7889.67724609375, 2074.776123046875, 600.18499755859375, 0.558503925800323486, 0, 0, 0.275636672973632812, 0.961261868476867675, 120, 255, 1, "", 46368, NULL), +(268815, 193671, 571, 0, 0, 1, 1, 7896.20068359375, 2044.22802734375, 600.18603515625, 2.111847877502441406, 0, 0, 0.870355606079101562, 0.492423713207244873, 120, 255, 1, "", 46368, NULL), +(268816, 193670, 571, 0, 0, 1, 1, 7898.9248046875, 2047.802734375, 600.52520751953125, 0.750488698482513427, 0, 0, 0.366499900817871093, 0.930418074131011962, 120, 255, 1, "", 46368, NULL), +(268818, 193645, 571, 0, 0, 1, 1, 7903.21923828125, 2050.816650390625, 600.10931396484375, 1.256635904312133789, 0, 0, 0.587784767150878906, 0.809017360210418701, 120, 255, 1, "", 46368, NULL), +(268819, 193644, 571, 0, 0, 1, 1, 7903.2822265625, 2050.578857421875, 600.28997802734375, 3.508116960525512695, 0, 0, -0.98325443267822265, 0.182238012552261352, 120, 255, 1, "", 46368, NULL), +(268820, 193643, 571, 0, 0, 1, 1, 7903.251953125, 2050.372314453125, 599.96185302734375, 1.972219824790954589, 0, 0, 0.83388519287109375, 0.55193793773651123, 120, 255, 1, "", 46368, NULL), +(268821, 193642, 571, 0, 0, 1, 1, 7903.5048828125, 2050.60205078125, 600.26007080078125, 3.682650327682495117, 0, 0, -0.96362972259521484, 0.26724100112915039, 120, 255, 1, "", 46368, NULL), +(268822, 193637, 571, 0, 0, 1, 1, 7902.48046875, 2048.71728515625, 600.2122802734375, 5.166173934936523437, 0, 0, -0.52991962432861328, 0.848047912120819091, 120, 255, 1, "", 46368, NULL), +(268825, 193632, 571, 0, 0, 1, 1, 7905.810546875, 2058.752685546875, 600.1993408203125, 3.822272777557373046, 0, 0, -0.94264125823974609, 0.333807557821273803, 120, 255, 1, "", 46368, NULL), +(268826, 193631, 571, 0, 0, 1, 1, 7905.70849609375, 2056.715576171875, 600.1993408203125, 5.218533515930175781, 0, 0, -0.5075387954711914, 0.861628890037536621, 120, 255, 1, "", 46368, NULL), +(268827, 193630, 571, 0, 0, 1, 1, 7905.68505859375, 2057.931640625, 601.06793212890625, 4.616430282592773437, 0.672366619110107421, -0.74020576477050781, -0.00074100494384765, 0.00424973014742136, 120, 255, 1, "", 46368, NULL), +(268829, 193653, 571, 0, 0, 1, 1, 7899.41552734375, 2068.0380859375, 600.639404296875, 2.338739633560180664, 0, 0, 0.920504570007324218, 0.3907318115234375, 120, 255, 1, "", 46368, NULL), +(268832, 193636, 571, 0, 0, 1, 1, 7902.55126953125, 2066.074951171875, 601.30126953125, 3.508113622665405273, -0.08671522140502929, 0.033976554870605468, -0.97968578338623046, 0.177600175142288208, 120, 255, 1, "", 46368, NULL), +(268833, 193635, 571, 0, 0, 1, 1, 7902.6494140625, 2066.373046875, 601.30133056640625, 4.389505863189697265, -0.07650041580200195, -0.06483268737792968, -0.80701732635498046, 0.581951439380645751, 120, 255, 1, "", 46368, NULL), +(268834, 193634, 571, 0, 0, 1, 1, 7902.3046875, 2066.493408203125, 601.3028564453125, 3.708826541900634765, 0, 0, -0.96004962921142578, 0.279829770326614379, 120, 255, 1, "", 46368, NULL), +(268835, 193633, 571, 0, 0, 1, 1, 7902.375, 2065.69677734375, 600.09716796875, 3.900813102722167968, 0, 0, -0.92880916595458984, 0.370558410882949829, 120, 255, 1, "", 46368, NULL), +(268839, 193681, 571, 0, 0, 1, 1, 7895.1708984375, 2072.284423828125, 600.2469482421875, 1.517560839653015136, 0, 0, 0.688036918640136718, 0.725675702095031738, 120, 255, 1, "", 46368, NULL), +(268840, 193680, 571, 0, 0, 1, 1, 7893.27880859375, 2073.20849609375, 600.249755859375, 0.280996710062026977, 0, 0, 0.140036582946777343, 0.990146338939666748, 120, 255, 1, "", 46368, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (193638, 193639, 193640, 193641, 193646, 193647, 193648, 193655, 193656, 193667, 193672, 193673, 193675, 193679, 193682, 193683, 193684, 193685, 193686, 193687, 193688, 193689, 193690, 193694, 193695)) AND (`guid` IN (2077, 2078, 2079, 2080, 2081, 2082, 2083, 2084, 2085, 2086, 2087, 2088, 2089, 2090, 2091, 2092, 2093, 2094, 2095, 2096, 2097, 2098, 2099, 2100, 2101)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(2077, 193638, 571, 0, 0, 1, 1, 7902.05078125, 2048.413330078125, 600.2374267578125, 4.581534385681152343, -0.28159475326538085, 0.654002189636230468, 0.270960807800292968, 0.647738933563232421, 120, 255, 1, "", 46368, NULL), +(2078, 193639, 571, 0, 0, 1, 1, 7903.22216796875, 2050.048828125, 599.98638916015625, 0.043632153421640396, 0, 0, 0.021814346313476562, 0.99976205825805664, 120, 255, 1, "", 46368, NULL), +(2079, 193640, 571, 0, 0, 1, 1, 7903.53271484375, 2050.19677734375, 600.09759521484375, 0.177969858050346374, 0.183217525482177734, 0.098484039306640625, 0.070977210998535156, 0.975548326969146728, 120, 255, 1, "", 46368, NULL), +(2080, 193641, 571, 0, 0, 1, 1, 7903.39697265625, 2050.458740234375, 600.12481689453125, 3.194002151489257812, 0, 0, -0.99965667724609375, 0.026201646775007247, 120, 255, 1, "", 46368, NULL), +(2081, 193646, 571, 0, 0, 1, 1, 7904.517578125, 2052.545166015625, 600.2119140625, 6.065019607543945312, 0, 0, -0.10886669158935546, 0.994056344032287597, 120, 255, 1, "", 46368, NULL), +(2082, 193647, 571, 0, 0, 1, 1, 7903.68505859375, 2052.069091796875, 600.21636962890625, 0.680676698684692382, 0, 0, 0.333806037902832031, 0.942641794681549072, 120, 255, 1, "", 46368, NULL), +(2083, 193648, 571, 0, 0, 1, 1, 7904.35107421875, 2051.583740234375, 600.22607421875, 5.672319889068603515, 0, 0, -0.3007059097290039, 0.953716933727264404, 120, 255, 1, "", 46368, NULL), +(2084, 193655, 571, 0, 0, 1, 1, 7880.44384765625, 2070.882568359375, 601.27783203125, 6.152286052703857421, 0, 0, -0.06540298461914062, 0.997858941555023193, 120, 255, 1, "", 46368, NULL), +(2085, 193656, 571, 0, 0, 1, 1, 7880.8056640625, 2071.86962890625, 601.318603515625, 3.717553138732910156, 0.161904335021972656, 0.094899177551269531, -0.93937873840332031, 0.286964625120162963, 120, 255, 1, "", 46368, NULL), +(2086, 193667, 571, 0, 0, 1, 1, 7874.93603515625, 2051.4755859375, 601.44091796875, 2.574358940124511718, 0, 0, 0.960049629211425781, 0.279829770326614379, 120, 255, 1, "", 46368, NULL), +(2087, 193672, 571, 0, 0, 1, 1, 7895.59130859375, 2044.85791015625, 601.402587890625, 4.127707481384277343, 0, 0, -0.880889892578125, 0.473321229219436645, 120, 255, 1, "", 46368, NULL), +(2088, 193673, 571, 0, 0, 1, 1, 7895.86083984375, 2043.8819580078125, 601.3876953125, 3.298687219619750976, 0, 0, -0.99691677093505859, 0.078466430306434631, 120, 255, 1, "", 46368, NULL), +(2089, 193675, 571, 0, 0, 1, 1, 7888.546875, 2057.972900390625, 599.6009521484375, 1.474801421165466308, 0, 0, 0.672366142272949218, 0.740218758583068847, 120, 255, 1, "", 46368, NULL), +(2090, 193679, 571, 0, 0, 1, 1, 7893.048828125, 2071.62255859375, 600.365478515625, 3.813161611557006835, -0.69073057174682617, -0.15002155303955078, -0.69123363494873046, 0.150269299745559692, 120, 255, 1, "", 46368, NULL), +(2091, 193682, 571, 0, 0, 1, 1, 7895.12890625, 2072.302978515625, 601.992919921875, 0.365644693374633789, 0, 0, 0.181805610656738281, 0.983334481716156005, 120, 255, 1, "", 46368, NULL), +(2092, 193683, 571, 0, 0, 1, 1, 7875.3193359375, 2053.161376953125, 600.2493896484375, 1.278458118438720703, 0.212913990020751953, -0.32244300842285156, 0.543003082275390625, 0.745550692081451416, 120, 255, 1, "", 46368, NULL), +(2093, 193684, 571, 0, 0, 1, 1, 7898.14697265625, 2066.417236328125, 600.31329345703125, 0.75534135103225708, 0, 0, 0.368756294250488281, 0.929526090621948242, 120, 255, 1, "", 46368, NULL), +(2094, 193685, 571, 0, 0, 1, 1, 7897.3955078125, 2049.253173828125, 600.3131103515625, 5.459003925323486328, 0, 0, -0.40052604675292968, 0.916285336017608642, 120, 255, 1, "", 46368, NULL), +(2095, 193686, 571, 0, 0, 1, 1, 7878.8671875, 2049.250244140625, 600.31298828125, 0.75534135103225708, 0, 0, 0.368756294250488281, 0.929526090621948242, 120, 255, 1, "", 46368, NULL), +(2096, 193687, 571, 0, 0, 1, 1, 7878.82861328125, 2066.62109375, 600.31317138671875, 5.432824611663818359, 0, 0, -0.41248512268066406, 0.910964369773864746, 120, 255, 1, "", 46368, NULL), +(2097, 193688, 571, 0, 0, 1, 1, 7881.25048828125, 2043.955078125, 600.2493896484375, 1.4564744234085083, 0, 0, 0.665555000305175781, 0.746348798274993896, 120, 255, 1, "", 46368, NULL), +(2098, 193689, 571, 0, 0, 1, 1, 7879.41748046875, 2044.993408203125, 600.2493896484375, 0.219910025596618652, 0, 0, 0.10973358154296875, 0.993961036205291748, 120, 255, 1, "", 46368, NULL), +(2099, 193690, 571, 0, 0, 1, 1, 7881.208984375, 2043.9761962890625, 600.2493896484375, 0.304556638002395629, 0, 0, 0.151690483093261718, 0.988428056240081787, 120, 255, 1, "", 46368, NULL), +(2100, 193694, 571, 0, 0, 1, 1, 7886.49609375, 2059.90869140625, 600.2626953125, 5.452062606811523437, 0, 0, -0.40370368957519531, 0.914889812469482421, 120, 255, 1, "", 46368, NULL), +(2101, 193695, 571, 0, 0, 1, 1, 7885.58935546875, 2059.190185546875, 600.76806640625, 2.337379932403564453, 0.248764514923095703, 0.376603126525878906, 0.836214065551757812, 0.311500102281570434, 120, 255, 1, "", 46368, NULL); + +-- remove duplicate spawns +DELETE FROM `gameobject` WHERE (`id` IN (193706, 193712, 193713, 193719, 193698, 193699, 193700, 193705, 193708, 193722, 193723, 193724, 193725, 193726, 193730, 193731, 193732, 193733, 193734, 193735, 193738, 193739, 193740, 193759, 193760, 193761, 193749, 193747, 193748, 193692)) AND (`guid` IN (268830, 268824, 268823, 268817, 268838, 268837, 268836, 268831, 268828, 268814, 268813, 268812, 268811, 268810, 268806, 268805, 268804, 268803, 268802, 268801, 268798, 268797, 268796, 268778, 268777, 268776, 268787, 268789, 268788, 256036, 257787, 258233, 260066, 260579, 261410, 261970, 262680, 263207, 263653, 265211, 266056, 267263, 267510, 267905, 268771)); +DELETE FROM `gameobject_addon` WHERE (`guid` IN (268830, 268824, 268823, 268817, 268838, 268837, 268836, 268831, 268828, 268814, 268813, 268812, 268811, 268810, 268806, 268805, 268804, 268803, 268802, 268801, 268798, 268797, 268796, 268778, 268777, 268776, 268787, 268789, 268788, 256036, 257787, 258233, 260066, 260579, 261410, 261970, 262680, 263207, 263653, 265211, 266056, 267263, 267510, 267905, 268771)); +DELETE FROM `game_event_gameobject` WHERE (`eventEntry` = 0) AND (`guid` IN (268830, 268824, 268823, 268817, 268838, 268837, 268836, 268831, 268828, 268814, 268813, 268812, 268811, 268810, 268806, 268805, 268804, 268803, 268802, 268801, 268798, 268797, 268796, 268778, 268777, 268776, 268787, 268789, 268788, 256036, 257787, 258233, 260066, 260579, 261410, 261970, 262680, 263207, 263653, 265211, 266056, 267263, 267510, 267905, 268771)); From 6381194d8a44c1ae3c7393457251d95cd7fea231 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Sep 2025 04:14:21 +0000 Subject: [PATCH 088/155] chore(DB): import pending files Referenced commit(s): 0f805db8bac4d5c9cbd4fbd856e933a48379a19c --- .../rev_1756700338678545400.sql => db_world/2025_09_03_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756700338678545400.sql => db_world/2025_09_03_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1756700338678545400.sql b/data/sql/updates/db_world/2025_09_03_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1756700338678545400.sql rename to data/sql/updates/db_world/2025_09_03_00.sql index 9d8c33842..0630c0561 100644 --- a/data/sql/updates/pending_db_world/rev_1756700338678545400.sql +++ b/data/sql/updates/db_world/2025_09_03_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_02_02 -> 2025_09_03_00 -- Update gameobject 'various doodad' with sniffed values -- updated spawns DELETE FROM `gameobject` WHERE (`id` IN (193692, 193766, 193765, 193764, 193763, 193762, 193761, 193760, 193759, 193758, 193757, 193756, 193755, 193754, 193753, 193752, 193751, 193750, 193749, 193748, 193747, 193746, 193745, 193744, 193743, 193742, 193741, 193740, 193739, 193738, 193737, 193736, 193735, 193734, 193733, 193732, 193731, 193730, 193729, 193728, 193727, 193726, 193725, 193724, 193723, 193722, 193721, 193720, 193719, 193718, 193717, 193716, 193715, 193714, 193713, 193712, 193711, 193710, 193709, 193708, 193707, 193706, 193705, 193704, 193703, 193702, 193701, 193700, 193699, 193698, 193697, 193696, 193693, 193691, 193678, 193677, 193676, 193674, 193669, 193668, 193666, 193665, 193664, 193663, 193659, 193658, 193657, 193662, 193660, 193661, 193654, 193652, 193651, 193649, 193650, 193671, 193670, 193645, 193644, 193643, 193642, 193637, 193632, 193631, 193630, 193653, 193636, 193635, 193634, 193633, 193681, 193680)) AND (`guid` IN (255513, 268696, 268697, 268698, 268699, 268700, 268701, 268702, 268703, 268704, 268705, 268706, 268707, 268708, 268709, 268710, 268711, 268712, 268713, 268714, 268715, 268716, 268717, 268718, 268719, 268720, 268721, 268722, 268723, 268724, 268725, 268726, 268727, 268728, 268729, 268730, 268731, 268732, 268733, 268734, 268735, 268736, 268737, 268738, 268739, 268740, 268741, 268742, 268743, 268744, 268745, 268746, 268747, 268748, 268749, 268750, 268751, 268752, 268753, 268754, 268755, 268756, 268757, 268758, 268759, 268760, 268761, 268762, 268763, 268764, 268765, 268766, 268772, 268773, 268774, 268775, 268779, 268780, 268781, 268782, 268783, 268784, 268785, 268786, 268790, 268791, 268792, 268793, 268794, 268795, 268799, 268800, 268807, 268808, 268809, 268815, 268816, 268818, 268819, 268820, 268821, 268822, 268825, 268826, 268827, 268829, 268832, 268833, 268834, 268835, 268839, 268840)); From 4d0281552a0c927f4e7628898e3665523b7f75e3 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 3 Sep 2025 08:13:06 -0300 Subject: [PATCH 089/155] =?UTF-8?q?fix(Scripts/HowlingFjord):=20Fix=20Falc?= =?UTF-8?q?on=20vs=20Hawk=20quest=20credit=20being=20gran=E2=80=A6=20(#227?= =?UTF-8?q?78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rev_1756862808168107100.sql | 11 +++++++ .../scripts/Northrend/zone_howling_fjord.cpp | 32 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756862808168107100.sql diff --git a/data/sql/updates/pending_db_world/rev_1756862808168107100.sql b/data/sql/updates/pending_db_world/rev_1756862808168107100.sql new file mode 100644 index 000000000..a758fe749 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756862808168107100.sql @@ -0,0 +1,11 @@ +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 17) AND (`SourceGroup` = 0) AND (`SourceEntry` = 44407); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17, 0, 44407, 0, 0, 31, 1, 3, 24747, 0, 0, 0, 0, '', 'Hawk Hunting must target Fjord Hawk'); + +UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 24747; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 24747) AND (`source_type` = 0); + +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_hawk_hunting'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(44407, 'spell_hawk_hunting'); diff --git a/src/server/scripts/Northrend/zone_howling_fjord.cpp b/src/server/scripts/Northrend/zone_howling_fjord.cpp index 2b0efc76f..c6405cc18 100644 --- a/src/server/scripts/Northrend/zone_howling_fjord.cpp +++ b/src/server/scripts/Northrend/zone_howling_fjord.cpp @@ -22,6 +22,7 @@ #include "ScriptedEscortAI.h" #include "ScriptedGossip.h" #include "SpellInfo.h" +#include "SpellScript.h" class npc_attracted_reef_bull : public CreatureScript { @@ -393,6 +394,36 @@ public: } }; +enum HawkHunting +{ + SPELL_HAWK_HUNTING_ITEM = 44408 +}; + +// 44407 - Spell hawk Hunting +class spell_hawk_hunting : public SpellScript +{ + PrepareSpellScript(spell_hawk_hunting); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_HAWK_HUNTING_ITEM }); + } + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + if (!GetCaster()) + return; + + GetCaster()->CastSpell(GetCaster(), SPELL_HAWK_HUNTING_ITEM, true); + GetHitUnit()->ToCreature()->DespawnOrUnsummon(); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_hawk_hunting::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + void AddSC_howling_fjord() { new npc_attracted_reef_bull(); @@ -400,4 +431,5 @@ void AddSC_howling_fjord() new npc_apothecary_hanes(); new npc_plaguehound_tracker(); new npc_razael_and_lyana(); + RegisterSpellScript(spell_hawk_hunting); } From 11e448a230e0e9c219d2ef713630688f32d0d284 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 3 Sep 2025 08:13:42 -0300 Subject: [PATCH 090/155] fix(Scripts/Spells): Fix group credit for Salvaging Life's Strength quest spell (#22779) --- src/server/scripts/Spells/spell_quest.cpp | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index b6dae13a2..37d438e50 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -1527,12 +1527,27 @@ class spell_q12805_lifeblood_dummy : public SpellScript void HandleScript(SpellEffIndex /*effIndex*/) { Player* caster = GetCaster()->ToPlayer(); - if (Creature* target = GetHitCreature()) + Creature* target = GetHitCreature(); + + if (!target) + return; + + if (Group* group = caster->GetGroup()) { - caster->KilledMonsterCredit(NPC_SHARD_KILL_CREDIT); - target->CastSpell(target, uint32(GetEffectValue()), true); - target->DespawnOrUnsummon(2000); + ObjectGuid targetGUID = target->GetGUID(); + group->DoForAllMembers([targetGUID](Player* player) + { + if (Creature* shard = ObjectAccessor::GetCreature(*player, targetGUID)) + if (player->IsAtGroupRewardDistance(shard)) + player->KilledMonsterCredit(NPC_SHARD_KILL_CREDIT); + + }); } + else + caster->KilledMonsterCredit(NPC_SHARD_KILL_CREDIT); + + target->CastSpell(target, uint32(GetEffectValue()), true); + target->DespawnOrUnsummon(2000); } void Register() override From d5d8256bc5f733477966f70d0802abdedde0cd2d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Sep 2025 11:14:11 +0000 Subject: [PATCH 091/155] chore(DB): import pending files Referenced commit(s): 4d0281552a0c927f4e7628898e3665523b7f75e3 --- .../rev_1756862808168107100.sql => db_world/2025_09_03_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756862808168107100.sql => db_world/2025_09_03_01.sql} (94%) diff --git a/data/sql/updates/pending_db_world/rev_1756862808168107100.sql b/data/sql/updates/db_world/2025_09_03_01.sql similarity index 94% rename from data/sql/updates/pending_db_world/rev_1756862808168107100.sql rename to data/sql/updates/db_world/2025_09_03_01.sql index a758fe749..692c4337e 100644 --- a/data/sql/updates/pending_db_world/rev_1756862808168107100.sql +++ b/data/sql/updates/db_world/2025_09_03_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_03_00 -> 2025_09_03_01 -- DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 17) AND (`SourceGroup` = 0) AND (`SourceEntry` = 44407); INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES From 599d206584f801adf3e42fede6bf0496605b60c3 Mon Sep 17 00:00:00 2001 From: Yehonal Date: Thu, 4 Sep 2025 00:03:55 +0200 Subject: [PATCH 092/155] feat(MenuSystem): Implement unified menu system for AzerothCore management (#22786) --- apps/bash_shared/menu_system.sh | 218 ++++++++++++++++++ apps/compiler/compiler.sh | 124 +++++----- apps/compiler/test/test_compiler.bats | 16 +- .../includes/modules-manager/module-main.sh | 7 + .../includes/modules-manager/modules.sh | 125 +++++++++- apps/installer/main.sh | 169 +++++++------- apps/installer/test/test_module_commands.bats | 3 + 7 files changed, 498 insertions(+), 164 deletions(-) create mode 100644 apps/bash_shared/menu_system.sh create mode 100644 apps/installer/includes/modules-manager/module-main.sh diff --git a/apps/bash_shared/menu_system.sh b/apps/bash_shared/menu_system.sh new file mode 100644 index 000000000..e7ae8b564 --- /dev/null +++ b/apps/bash_shared/menu_system.sh @@ -0,0 +1,218 @@ +#!/usr/bin/env bash + +# ============================================================================= +# AzerothCore Menu System Library +# ============================================================================= +# This library provides a unified menu system for AzerothCore scripts. +# It supports ordered menu definitions, short commands, numeric selection, +# and proper argument handling. +# +# Features: +# - Single source of truth for menu definitions +# - Automatic ID assignment (1, 2, 3...) +# - Short command aliases (c, i, q, etc.) +# - Interactive mode: numbers + long/short commands +# - Direct mode: only long/short commands (no numbers) +# - Proper argument forwarding +# +# Usage: +# source "path/to/menu_system.sh" +# menu_items=("command|short|description" ...) +# menu_run "Menu Title" callback_function "${menu_items[@]}" "$@" +# ============================================================================= + +# Global arrays for menu state (will be populated by menu_define) +declare -a _MENU_KEYS=() +declare -a _MENU_SHORTS=() +declare -a _MENU_OPTIONS=() + +# Parse menu items and populate global arrays +# Usage: menu_define array_elements... +function menu_define() { + # Clear previous state + _MENU_KEYS=() + _MENU_SHORTS=() + _MENU_OPTIONS=() + + # Parse each menu item: "key|short|description" + local item key short desc + for item in "$@"; do + IFS='|' read -r key short desc <<< "$item" + _MENU_KEYS+=("$key") + _MENU_SHORTS+=("$short") + _MENU_OPTIONS+=("$key ($short): $desc") + done +} + +# Display menu with numbered options +# Usage: menu_display "Menu Title" +function menu_display() { + local title="$1" + + echo "==== $title ====" + for idx in "${!_MENU_OPTIONS[@]}"; do + local num=$((idx + 1)) + printf "%2d) %s\n" "$num" "${_MENU_OPTIONS[$idx]}" + done + echo "" +} + +# Find menu index by user input (number, long command, or short command) +# Returns: index (0-based) or -1 if not found +# Usage: index=$(menu_find_index "user_input") +function menu_find_index() { + local user_input="$1" + + # Try numeric selection first + if [[ "$user_input" =~ ^[0-9]+$ ]]; then + local num=$((user_input - 1)) + if [[ $num -ge 0 && $num -lt ${#_MENU_KEYS[@]} ]]; then + echo "$num" + return 0 + fi + fi + + # Try long command name + local idx + for idx in "${!_MENU_KEYS[@]}"; do + if [[ "$user_input" == "${_MENU_KEYS[$idx]}" ]]; then + echo "$idx" + return 0 + fi + done + + # Try short command + for idx in "${!_MENU_SHORTS[@]}"; do + if [[ "$user_input" == "${_MENU_SHORTS[$idx]}" ]]; then + echo "$idx" + return 0 + fi + done + + echo "-1" + return 1 +} + +# Handle direct execution (command line arguments) +# Disables numeric selection to prevent confusion with command arguments +# Usage: menu_direct_execute callback_function "$@" +function menu_direct_execute() { + local callback="$1" + shift + local user_input="$1" + shift + + # Handle help requests directly + if [[ "$user_input" == "--help" || "$user_input" == "help" || "$user_input" == "-h" ]]; then + echo "Available commands:" + printf '%s\n' "${_MENU_OPTIONS[@]}" + return 0 + fi + + # Disable numeric selection in direct mode + if [[ "$user_input" =~ ^[0-9]+$ ]]; then + echo "Invalid option. Numeric selection is not allowed when passing arguments." + echo "Use command name or short alias instead." + return 1 + fi + + # Find command and execute + local idx + idx=$(menu_find_index "$user_input") + if [[ $idx -ge 0 ]]; then + "$callback" "${_MENU_KEYS[$idx]}" "$@" + return $? + else + echo "Invalid option. Use --help to see available commands." >&2 + return 1 + fi +} + +# Handle interactive menu selection +# Usage: menu_interactive callback_function "Menu Title" +function menu_interactive() { + local callback="$1" + local title="$2" + + while true; do + menu_display "$title" + read -r -p "Please enter your choice: " REPLY + + # Handle help request + if [[ "$REPLY" == "--help" || "$REPLY" == "help" || "$REPLY" == "h" ]]; then + echo "Available commands:" + printf '%s\n' "${_MENU_OPTIONS[@]}" + echo "" + continue + fi + + # Find and execute command + local idx + idx=$(menu_find_index "$REPLY") + if [[ $idx -ge 0 ]]; then + "$callback" "${_MENU_KEYS[$idx]}" + else + echo "Invalid option. Please try again or use 'help' for available commands." >&2 + echo "" + fi + done +} + +# Main menu runner function +# Usage: menu_run "Menu Title" callback_function menu_item1 menu_item2 ... "$@" +function menu_run() { + local title="$1" + local callback="$2" + shift 2 + + # Extract menu items (all arguments until we find command line args) + local menu_items=() + local found_args=false + + # Separate menu items from command line arguments + while [[ $# -gt 0 ]]; do + if [[ "$1" =~ \| ]]; then + # This looks like a menu item (contains pipe) + menu_items+=("$1") + shift + else + # This is a command line argument + found_args=true + break + fi + done + + # Define menu from collected items + menu_define "${menu_items[@]}" + + # Handle direct execution if arguments provided + if [[ $found_args == true ]]; then + menu_direct_execute "$callback" "$@" + return $? + fi + + # Run interactive menu + menu_interactive "$callback" "$title" +} + +# Utility function to show available commands (for --help) +# Usage: menu_show_help +function menu_show_help() { + echo "Available commands:" + printf '%s\n' "${_MENU_OPTIONS[@]}" +} + +# Utility function to get command key by index +# Usage: key=$(menu_get_key index) +function menu_get_key() { + local idx="$1" + if [[ $idx -ge 0 && $idx -lt ${#_MENU_KEYS[@]} ]]; then + echo "${_MENU_KEYS[$idx]}" + fi +} + +# Utility function to get all command keys +# Usage: keys=($(menu_get_all_keys)) +function menu_get_all_keys() { + printf '%s\n' "${_MENU_KEYS[@]}" +} diff --git a/apps/compiler/compiler.sh b/apps/compiler/compiler.sh index dcb94a6b6..aa845b699 100755 --- a/apps/compiler/compiler.sh +++ b/apps/compiler/compiler.sh @@ -5,72 +5,76 @@ set -e CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source "$CURRENT_PATH/includes/includes.sh" +source "$AC_PATH_APPS/bash_shared/menu_system.sh" -function run_option() { - re='^[0-9]+$' - if [[ $1 =~ $re ]] && test "${comp_functions[$1-1]+'test'}"; then - ${comp_functions[$1-1]} - elif [ -n "$(type -t comp_$1)" ] && [ "$(type -t comp_$1)" = function ]; then - fun="comp_$1" - $fun - else - echo "invalid option, use --help option for the commands list" - fi -} +# Menu definition using the new system +# Format: "key|short|description" +comp_menu_items=( + "build|b|Configure and compile" + "clean|cl|Clean build files" + "configure|cfg|Run CMake" + "compile|cmp|Compile only" + "all|a|clean, configure and compile" + "ccacheClean|cc|Clean ccache files, normally not needed" + "ccacheShowStats|cs|show ccache statistics" + "quit|q|Close this menu" +) -function comp_quit() { - exit 0 -} - -comp_options=( - "build: Configure and compile" - "clean: Clean build files" - "configure: Run CMake" - "compile: Compile only" - "all: clean, configure and compile" - "ccacheClean: Clean ccache files, normally not needed" - "ccacheShowStats: show ccache statistics" - "quit: Close this menu") -comp_functions=( - "comp_build" - "comp_clean" - "comp_configure" - "comp_compile" - "comp_all" - "comp_ccacheClean" - "comp_ccacheShowStats" - "comp_quit") - -PS3='[ Please enter your choice ]: ' - -runHooks "ON_AFTER_OPTIONS" #you can create your custom options - -function _switch() { - _reply="$1" - _opt="$2" - - case $_reply in - ""|"--help") - echo "Available commands:" - printf '%s\n' "${options[@]}" +# Menu command handler - called by menu system for each command +function handle_compiler_command() { + local key="$1" + shift + + case "$key" in + "build") + comp_build + ;; + "clean") + comp_clean + ;; + "configure") + comp_configure + ;; + "compile") + comp_compile + ;; + "all") + comp_all + ;; + "ccacheClean") + comp_ccacheClean + ;; + "ccacheShowStats") + comp_ccacheShowStats + ;; + "quit") + echo "Closing compiler menu..." + exit 0 ;; *) - run_option $_reply $_opt - ;; + echo "Invalid option. Use --help to see available commands." + return 1 + ;; esac } +# Hook support (preserved from original) +runHooks "ON_AFTER_OPTIONS" # you can create your custom options -while true -do - # run option directly if specified in argument - [ ! -z $1 ] && _switch $@ - [ ! -z $1 ] && exit 0 +# Legacy switch function (preserved for compatibility) +function _switch() { + local reply="$1" + local opt="$2" - select opt in "${comp_options[@]}" - do - echo "==== ACORE COMPILER ====" - _switch $REPLY - break; - done -done + case "$reply" in + ""|"--help") + menu_show_help + ;; + *) + run_option "$reply" "$opt" + ;; + esac +} + +# Run the menu system +menu_run "ACORE COMPILER" handle_compiler_command "${comp_menu_items[@]}" "$@" diff --git a/apps/compiler/test/test_compiler.bats b/apps/compiler/test/test_compiler.bats index 79152b68e..5cb8de44a 100755 --- a/apps/compiler/test/test_compiler.bats +++ b/apps/compiler/test/test_compiler.bats @@ -36,8 +36,8 @@ teardown() { run bash -c "echo '' | timeout 5s $COMPILER_SCRIPT 2>&1 || true" # The script might exit with timeout (124) or success (0), both are acceptable for this test [[ "$status" -eq 0 ]] || [[ "$status" -eq 124 ]] - # Check if output contains expected content - looking for menu options - [[ "$output" =~ "build:" ]] || [[ "$output" =~ "clean:" ]] || [[ "$output" =~ "Please enter your choice" ]] || [[ -z "$output" ]] + # Check if output contains expected content - looking for menu options (old or new format) + [[ "$output" =~ "build:" ]] || [[ "$output" =~ "clean:" ]] || [[ "$output" =~ "Please enter your choice" ]] || [[ "$output" =~ "build (b):" ]] || [[ "$output" =~ "ACORE COMPILER" ]] || [[ -z "$output" ]] } @test "compiler: should accept option numbers" { @@ -54,16 +54,16 @@ teardown() { @test "compiler: should handle invalid option gracefully" { run timeout 5s "$COMPILER_SCRIPT" invalidOption - [ "$status" -eq 0 ] - [[ "$output" =~ "invalid option" ]] + # Should exit with error code for invalid option + [ "$status" -eq 1 ] + # Output check is optional as error message might be buffered } @test "compiler: should handle invalid number gracefully" { - run bash -c "echo '999' | timeout 5s $COMPILER_SCRIPT 2>/dev/null || true" - # The script might exit with timeout (124) or success (0), both are acceptable + run bash -c "echo '999' | timeout 5s $COMPILER_SCRIPT 2>&1 || true" + # The script might exit with timeout (124) or success (0) for interactive mode [[ "$status" -eq 0 ]] || [[ "$status" -eq 124 ]] - # Check if output contains expected content, or if there's no output due to timeout, that's also acceptable - [[ "$output" =~ "invalid option" ]] || [[ "$output" =~ "Please enter your choice" ]] || [[ -z "$output" ]] + # In interactive mode, the script should continue asking for input or timeout } @test "compiler: should quit with quit option" { diff --git a/apps/installer/includes/modules-manager/module-main.sh b/apps/installer/includes/modules-manager/module-main.sh new file mode 100644 index 000000000..f4f458e09 --- /dev/null +++ b/apps/installer/includes/modules-manager/module-main.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd ) + +source "$CURRENT_PATH/modules.sh" + +inst_module "$@" \ No newline at end of file diff --git a/apps/installer/includes/modules-manager/modules.sh b/apps/installer/includes/modules-manager/modules.sh index 93e643306..761232964 100644 --- a/apps/installer/includes/modules-manager/modules.sh +++ b/apps/installer/includes/modules-manager/modules.sh @@ -13,16 +13,126 @@ # - Cross-format module recognition (URLs, SSH, simple names) # - Custom directory naming to prevent conflicts # - Intelligent duplicate prevention +# - Interactive menu system for easy management # # Usage: # source "path/to/modules.sh" # inst_module_install "mod-transmog:my-custom-dir@develop:abc123" +# inst_module # Interactive menu +# inst_module search "transmog" # Direct command # # ============================================================================= +CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd ) + +source "$CURRENT_PATH/../../../bash_shared/includes.sh" +source "$AC_PATH_APPS/bash_shared/menu_system.sh" + +# Module management menu definition +# Format: "key|short|description" +module_menu_items=( + "search|s|Search for available modules" + "install|i|Install one or more modules" + "update|u|Update installed modules" + "remove|r|Remove installed modules" + "list|l|List installed modules" + "help|h|Show detailed help" + "quit|q|Close this menu" +) + +# Menu command handler for module operations +function handle_module_command() { + local key="$1" + shift + + case "$key" in + "search") + inst_module_search "$@" + ;; + "install") + inst_module_install "$@" + ;; + "update") + inst_module_update "$@" + ;; + "remove") + inst_module_remove "$@" + ;; + "list") + inst_module_list "$@" + ;; + "help") + inst_module_help + ;; + "quit") + echo "Exiting module manager..." + exit 0 + ;; + *) + echo "Invalid option. Use 'help' to see available commands." + return 1 + ;; + esac +} + +# Show detailed module help +function inst_module_help() { + echo "AzerothCore Module Manager Help" + echo "===============================" + echo "" + echo "Usage:" + echo " ./acore.sh module # Interactive menu" + echo " ./acore.sh module search [terms...]" + echo " ./acore.sh module install [--all | modules...]" + echo " ./acore.sh module update [--all | modules...]" + echo " ./acore.sh module remove [modules...]" + echo " ./acore.sh module list # List installed modules" + echo "" + echo "Module Specification Syntax:" + echo " name # Simple name (e.g., mod-transmog)" + echo " owner/name # GitHub repository" + echo " name:branch # Specific branch" + echo " name:branch:commit # Specific commit" + echo " name:dirname@branch # Custom directory name" + echo " https://github.com/... # Full URL" + echo "" + echo "Examples:" + echo " ./acore.sh module install mod-transmog" + echo " ./acore.sh module install azerothcore/mod-transmog:develop" + echo " ./acore.sh module update --all" + echo " ./acore.sh module remove mod-transmog" + echo "" +} + +# List installed modules +function inst_module_list() { + echo "Installed Modules:" + echo "==================" + local count=0 + while read -r repo_ref branch commit; do + [[ -z "$repo_ref" ]] && continue + count=$((count + 1)) + echo " $count. $repo_ref ($branch)" + if [[ "$commit" != "-" ]]; then + echo " Commit: $commit" + fi + done < <(inst_mod_list_read) + + if [[ $count -eq 0 ]]; then + echo " No modules installed." + fi + echo "" +} # Dispatcher for the unified `module` command. # Usage: ./acore.sh module [args...] +# ./acore.sh module # Interactive menu function inst_module() { + # If no arguments provided, start interactive menu + if [[ $# -eq 0 ]]; then + menu_run "MODULE MANAGER" handle_module_command "${module_menu_items[@]}" + return $? + fi + # Normalize arguments into an array local tokens=() read -r -a tokens <<< "$*" @@ -31,12 +141,7 @@ function inst_module() { case "$cmd" in ""|"help"|"-h"|"--help") - echo "Usage:" - echo " ./acore.sh module search [terms...]" - echo " ./acore.sh module install [--all | modules...]" - echo " modules can be specified as: name[:branch[:commit]]" - echo " ./acore.sh module update [modules...]" - echo " ./acore.sh module remove [modules...]" + inst_module_help ;; "search"|"s") inst_module_search "${args[@]}" @@ -50,9 +155,13 @@ function inst_module() { "remove"|"r") inst_module_remove "${args[@]}" ;; + "list"|"l") + inst_module_list "${args[@]}" + ;; *) - echo "Unknown subcommand: $cmd" - echo "Try: ./acore.sh module help" + echo "Unknown module command: $cmd" + echo "Use 'help' to see available commands." + return 1 ;; esac } diff --git a/apps/installer/main.sh b/apps/installer/main.sh index 5911ccbf7..3bdd99b22 100644 --- a/apps/installer/main.sh +++ b/apps/installer/main.sh @@ -1,114 +1,107 @@ #!/usr/bin/env bash +# AzerothCore Dashboard Script +# +# This script provides an interactive menu system for AzerothCore management +# using the unified menu system library. +# +# Usage: +# ./acore.sh - Interactive mode with numeric and text selection +# ./acore.sh [args] - Direct command execution (only text commands, no numbers) +# +# Interactive Mode: +# - Select options by number (1, 2, 3...), command name (init, compiler, etc.), +# or short alias (i, c, etc.) +# - All selection methods work in interactive mode +# +# Direct Command Mode: +# - Only command names and short aliases are accepted (e.g., './acore.sh compiler build', './acore.sh c build') +# - Numeric selection is disabled to prevent confusion with command arguments +# - Examples: './acore.sh init', './acore.sh compiler clean', './acore.sh module install mod-name' +# +# Menu System: +# - Uses unified menu system from bash_shared/menu_system.sh +# - Single source of truth for menu definitions +# - Consistent behavior across all AzerothCore tools + CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - source "$CURRENT_PATH/includes/includes.sh" +source "$AC_PATH_APPS/bash_shared/menu_system.sh" -PS3='[Please enter your choice]: ' -options=( - "init (i): First Installation" - "install-deps (d): Configure OS dep" - "pull (u): Update Repository" - "reset (r): Reset & Clean Repository" - "compiler (c): Run compiler tool" - "module (m): Module manager (search/install/update/remove)" - "module-install (mi): Module Install by name [DEPRECATED]" - "module-update (mu): Module Update by name [DEPRECATED]" - "module-remove: (mr): Module Remove by name [DEPRECATED]" - "client-data: (gd): download client data from github repository (beta)" - "run-worldserver (rw): execute a simple restarter for worldserver" - "run-authserver (ra): execute a simple restarter for authserver" - "docker (dr): Run docker tools" - "version (v): Show AzerothCore version" - "service-manager (sm): Run service manager to run authserver and worldserver in background" - "quit (q): Exit from this menu" - ) +# Menu: single ordered source of truth (no functions in strings) +# Format: "key|short|description" +menu_items=( + "init|i|First Installation" + "install-deps|d|Configure OS dep" + "pull|u|Update Repository" + "reset|r|Reset & Clean Repository" + "compiler|c|Run compiler tool" + "module|m|Module manager (search/install/update/remove)" + "client-data|gd|download client data from github repository (beta)" + "run-worldserver|rw|execute a simple restarter for worldserver" + "run-authserver|ra|execute a simple restarter for authserver" + "docker|dr|Run docker tools" + "version|v|Show AzerothCore version" + "service-manager|sm|Run service manager to run authserver and worldserver in background" + "quit|q|Exit from this menu" +) -function _switch() { - _reply="$1" - _opt="$2" - case $_reply in - ""|"i"|"init") - inst_allInOne +# Menu command handler - called by menu system for each command +function handle_menu_command() { + local key="$1" + shift + + case "$key" in + "init") + inst_allInOne ;; - ""|"d"|"install-deps") - inst_configureOS + "install-deps") + inst_configureOS ;; - ""|"u"|"pull") - inst_updateRepo + "pull") + inst_updateRepo ;; - ""|"r"|"reset") - inst_resetRepo + "reset") + inst_resetRepo ;; - ""|"c"|"compiler") - bash "$AC_PATH_APPS/compiler/compiler.sh" $_opt + "compiler") + bash "$AC_PATH_APPS/compiler/compiler.sh" "$@" ;; - ""|"m"|"module") - # Unified module command: supports subcommands search|install|update|remove - inst_module "${@:2}" + "module") + bash "$AC_PATH_APPS/installer/includes/modules-manager/module-main.sh" "$@" ;; - ""|"ms"|"module-search") - echo "[DEPRECATED] Use: ./acore.sh module search " - inst_module_search "${@:2}" + "client-data") + inst_download_client_data ;; - ""|"mi"|"module-install") - echo "[DEPRECATED] Use: ./acore.sh module install " - inst_module_install "${@:2}" + "run-worldserver") + inst_simple_restarter worldserver ;; - ""|"mu"|"module-update") - echo "[DEPRECATED] Use: ./acore.sh module update " - inst_module_update "${@:2}" + "run-authserver") + inst_simple_restarter authserver ;; - ""|"mr"|"module-remove") - echo "[DEPRECATED] Use: ./acore.sh module remove " - inst_module_remove "${@:2}" + "docker") + DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "$@" + exit ;; - ""|"gd"|"client-data") - inst_download_client_data - ;; - ""|"rw"|"run-worldserver") - inst_simple_restarter worldserver - ;; - ""|"ra"|"run-authserver") - inst_simple_restarter authserver - ;; - ""|"dr"|"docker") - DOCKER=1 bash "$AC_PATH_ROOT/apps/docker/docker-cmd.sh" "${@:2}" - exit - ;; - ""|"v"|"version") - # denoRunFile "$AC_PATH_APPS/installer/main.ts" "version" + "version") printf "AzerothCore Rev. %s\n" "$ACORE_VERSION" - exit + exit ;; - ""|"sm"|"service-manager") - bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "${@:2}" - exit + "service-manager") + bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "$@" + exit ;; - ""|"q"|"quit") + "quit") echo "Goodbye!" - exit + exit ;; - ""|"--help") - echo "Available commands:" - printf '%s\n' "${options[@]}" + *) + echo "Invalid option. Use --help to see available commands." + return 1 ;; - *) echo "invalid option, use --help option for the commands list";; esac } -while true -do - # run option directly if specified in argument - [ ! -z $1 ] && _switch $@ # old method: "${options[$cmdopt-1]}" - [ ! -z $1 ] && exit 0 - - echo "==== ACORE DASHBOARD ====" - select opt in "${options[@]}" - do - _switch $REPLY - break - done - echo "opt: $opt" -done +# Run the menu system +menu_run "ACORE DASHBOARD" handle_menu_command "${menu_items[@]}" "$@" diff --git a/apps/installer/test/test_module_commands.bats b/apps/installer/test/test_module_commands.bats index 40c284941..30ee94e81 100755 --- a/apps/installer/test/test_module_commands.bats +++ b/apps/installer/test/test_module_commands.bats @@ -58,6 +58,9 @@ EOF # minimal stub EOF + # Copy the menu system needed by modules.sh + cp "$AC_TEST_ROOT/apps/bash_shared/menu_system.sh" "$TEST_DIR/apps/bash_shared/" + # Copy the real installer app into the test apps dir mkdir -p "$TEST_DIR/apps" cp -r "$(cd "$AC_TEST_ROOT/apps/installer" && pwd)" "$TEST_DIR/apps/installer" From 1e29c31b079cb56a76265a816efce5b620e6a175 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Wed, 3 Sep 2025 16:20:04 -0700 Subject: [PATCH 093/155] fix(Scripts/WorldState): defeated scourge zone invasions not restarting after a defeat (#22783) --- src/server/game/World/WorldState.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/server/game/World/WorldState.cpp b/src/server/game/World/WorldState.cpp index a1f71a2a5..106e8fd99 100644 --- a/src/server/game/World/WorldState.cpp +++ b/src/server/game/World/WorldState.cpp @@ -1072,8 +1072,8 @@ std::string WorldState::GetScourgeInvasionPrintout() { TimePoint tp = m_siData.m_timers[timerId]; std::string timerStr; - if (tp.time_since_epoch().count() == 0) - timerStr = "0 (not set)"; + if (tp == TimePoint()) + timerStr = "Not set"; else if (tp <= now) timerStr = "Elapsed"; else @@ -1420,10 +1420,15 @@ void ScourgeInvasionData::Reset() std::string ScourgeInvasionData::GetData() { std::string output = std::to_string(m_state) + " "; - for (auto& timer : m_timers) - output += std::to_string(timer.time_since_epoch().count()) + " "; + for (TimePoint& timer : m_timers) + { + if (timer == TimePoint()) + output += "0 "; + else + output += std::to_string(std::chrono::duration_cast(timer.time_since_epoch()).count()) + " "; + } output += std::to_string(m_battlesWon) + " " + std::to_string(m_lastAttackZone) + " "; - for (auto& remaining : m_remaining) + for (uint32& remaining : m_remaining) output += std::to_string(remaining) + " "; return output; } From f970746c2c5d0a4c65d847e9f7b2b4635ddcf19e Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Wed, 3 Sep 2025 16:20:30 -0700 Subject: [PATCH 094/155] =?UTF-8?q?fix(Scripts/WorldState):=20zone=20invas?= =?UTF-8?q?ion=20marks=20self=20as=20defeated=20when=20ti=E2=80=A6=20(#227?= =?UTF-8?q?87)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Killerwife --- src/server/scripts/World/scourge_invasion.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/scripts/World/scourge_invasion.cpp b/src/server/scripts/World/scourge_invasion.cpp index c7d83f028..44cb434be 100644 --- a/src/server/scripts/World/scourge_invasion.cpp +++ b/src/server/scripts/World/scourge_invasion.cpp @@ -92,6 +92,7 @@ struct npc_herald_of_the_lich_king : public ScriptedAI Talk(HERALD_OF_THE_LICH_KING_SAY_ATTACK_END); ChangeZoneEventStatus(false); UpdateWeather(false); + me->DespawnOrUnsummon(); } } From d73cbec424dd5655397a15289479af77272ea4c9 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Wed, 3 Sep 2025 16:20:53 -0700 Subject: [PATCH 095/155] fix(DB/SAI): low-level scourge invasion mobs are killed by guards (#22784) --- .../updates/pending_db_world/rev_1756924523176702951.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756924523176702951.sql diff --git a/data/sql/updates/pending_db_world/rev_1756924523176702951.sql b/data/sql/updates/pending_db_world/rev_1756924523176702951.sql new file mode 100644 index 000000000..8e20651b9 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756924523176702951.sql @@ -0,0 +1,8 @@ +-- +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` IN (16423, 16437, 16438, 16422); +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (16423, 16437, 16438, 16422); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16423, 0, 0, 0, 0, 0, 100, 0, 0, 0, 5000, 10000, 0, 0, 11, 28265, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Spectral Apparition - In Combat - Cast \'Scourge Strike\''), +(16437, 0, 0, 0, 0, 0, 100, 0, 0, 0, 5000, 10000, 0, 0, 11, 28265, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Spectral Spirit - In Combat - Cast \'Scourge Strike\''), +(16422, 0, 0, 0, 0, 0, 100, 0, 0, 0, 5000, 10000, 0, 0, 11, 28265, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Soldier - In Combat - Cast \'Scourge Strike\''), +(16438, 0, 0, 0, 0, 0, 100, 0, 0, 0, 5000, 10000, 0, 0, 11, 28265, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Skeletal Trooper - In Combat - Cast \'Scourge Strike\''); From 2fc5ee9635aac494fb3913e4991079fda6e8005b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 3 Sep 2025 23:21:04 +0000 Subject: [PATCH 096/155] chore(DB): import pending files Referenced commit(s): 1e29c31b079cb56a76265a816efce5b620e6a175 --- .../rev_1756924523176702951.sql => db_world/2025_09_03_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756924523176702951.sql => db_world/2025_09_03_02.sql} (96%) diff --git a/data/sql/updates/pending_db_world/rev_1756924523176702951.sql b/data/sql/updates/db_world/2025_09_03_02.sql similarity index 96% rename from data/sql/updates/pending_db_world/rev_1756924523176702951.sql rename to data/sql/updates/db_world/2025_09_03_02.sql index 8e20651b9..98cfb21bd 100644 --- a/data/sql/updates/pending_db_world/rev_1756924523176702951.sql +++ b/data/sql/updates/db_world/2025_09_03_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_03_01 -> 2025_09_03_02 -- UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` IN (16423, 16437, 16438, 16422); DELETE FROM `smart_scripts` WHERE `entryorguid` IN (16423, 16437, 16438, 16422); From fa9e34de881e3054c52bec43b468cfe42c9c4290 Mon Sep 17 00:00:00 2001 From: "@cgrahamseven" Date: Wed, 3 Sep 2025 18:25:18 -0500 Subject: [PATCH 097/155] fix(Scripts/Oculus): Fix oculus npc belgaristrasz gossip menu (#22765) Co-authored-by: cgrahamseven --- .../scripts/Northrend/Nexus/Oculus/oculus.cpp | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index 17ac54f3b..01cdb73fb 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -75,8 +75,6 @@ enum DrakeGiverTexts class npc_oculus_drakegiver : public CreatureScript { public: - std::unordered_mapopenedMenu; - npc_oculus_drakegiver() : CreatureScript("npc_oculus_drakegiver") { } CreatureAI* GetAI(Creature* creature) const override @@ -193,20 +191,8 @@ public: SendGossipMenuFor(player, GOSSIP_TEXTID_VERDISA1, creature->GetGUID()); break; case NPC_BELGARISTRASZ: - if (HAS_ESSENCE(player)) - { - openedMenu[player->GetGUID()] = true; - } - - if (!openedMenu[player->GetGUID()]) - { - AddGossipItemFor(player, 9708, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - SendGossipMenuFor(player, GOSSIP_TEXTID_DRAKES, creature->GetGUID()); - } - else - { - OnGossipSelect(player, creature, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - } + AddGossipItemFor(player, 9708, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); + SendGossipMenuFor(player, GOSSIP_TEXTID_DRAKES, creature->GetGUID()); break; case NPC_ETERNOS: AddGossipItemFor(player, 9574, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); @@ -277,7 +263,6 @@ public: switch (uiAction) { case GOSSIP_ACTION_INFO_DEF: - openedMenu[player->GetGUID()] = true; if (player->HasItemCount(ITEM_AMBER_ESSENCE)) { AddGossipItemFor(player, 9575, 1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); From af31aaa3741b37e5a2ac56745de953555dc1b655 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Wed, 3 Sep 2025 16:29:15 -0700 Subject: [PATCH 098/155] fix(Core/Maps): Move corpse expiration updates to map (#22780) --- src/server/game/Maps/Map.cpp | 15 +++++++++++++++ src/server/game/Maps/Map.h | 4 ++++ src/server/game/World/IWorld.h | 1 - src/server/game/World/World.cpp | 19 ------------------- src/server/game/World/World.h | 3 --- src/server/scripts/Commands/cs_server.cpp | 6 +++++- 6 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 9ad4cfb4e..8e2a0cacf 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -73,6 +73,8 @@ Map::Map(uint32 id, uint32 InstanceId, uint8 SpawnMode, Map* _parent) : //lets initialize visibility distance for map Map::InitVisibilityDistance(); + + _corpseUpdateTimer.SetInterval(20 * MINUTE * IN_MILLISECONDS); } // Hook called after map is created AND after added to map list @@ -494,6 +496,8 @@ void Map::Update(const uint32 t_diff, const uint32 s_diff, bool /*thread*/) HandleDelayedVisibility(); + UpdateExpiredCorpses(t_diff); + sScriptMgr->OnMapUpdate(this, t_diff); METRIC_VALUE("map_creatures", uint64(GetObjectsStore().Size()), @@ -1580,6 +1584,17 @@ void Map::SendInitSelf(Player* player) player->SendDirectMessage(&packet); } +void Map::UpdateExpiredCorpses(uint32 const diff) +{ + _corpseUpdateTimer.Update(diff); + if (!_corpseUpdateTimer.Passed()) + return; + + RemoveOldCorpses(); + + _corpseUpdateTimer.Reset(); +} + void Map::SendInitTransports(Player* player) { if (_transports.empty()) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index f43111633..2e5b0ed0a 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -443,6 +443,8 @@ public: void SendZoneDynamicInfo(Player* player); void SendInitSelf(Player* player); + void UpdateExpiredCorpses(uint32 const diff); + void PlayDirectSoundToMap(uint32 soundId, uint32 zoneId = 0); void SetZoneMusic(uint32 zoneId, uint32 musicId); void SetZoneWeather(uint32 zoneId, WeatherState weatherId, float weatherGrade); @@ -576,6 +578,8 @@ private: ZoneDynamicInfoMap _zoneDynamicInfo; uint32 _defaultLight; + IntervalTimer _corpseUpdateTimer; + template inline ObjectGuidGeneratorBase& GetGuidSequenceGenerator() { diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 1ec85b71c..bf1af8cf8 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -110,7 +110,6 @@ public: virtual void ResetEventSeasonalQuests(uint16 event_id) = 0; [[nodiscard]] virtual std::string const& GetRealmName() const = 0; virtual void SetRealmName(std::string name) = 0; - virtual void RemoveOldCorpses() = 0; }; #endif //AZEROTHCORE_IWORLD_H diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index c8f330abd..fd56951ed 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -888,8 +888,6 @@ void World::SetInitialWorldSettings() _timers[WUPDATE_UPTIME].SetInterval(getIntConfig(CONFIG_UPTIME_UPDATE)*MINUTE * IN_MILLISECONDS); //Update "uptime" table based on configuration entry in minutes. - _timers[WUPDATE_CORPSES].SetInterval(20 * MINUTE * IN_MILLISECONDS); - //erase corpses every 20 minutes _timers[WUPDATE_CLEANDB].SetInterval(getIntConfig(CONFIG_LOGDB_CLEARINTERVAL)*MINUTE * IN_MILLISECONDS); // clean logs table every 14 days by default _timers[WUPDATE_AUTOBROADCAST].SetInterval(getIntConfig(CONFIG_AUTOBROADCAST_INTERVAL)); @@ -1278,18 +1276,6 @@ void World::Update(uint32 diff) LoginDatabase.Execute(stmt); } - ///- Erase corpses once every 20 minutes - if (_timers[WUPDATE_CORPSES].Passed()) - { - METRIC_TIMER("world_update_time", METRIC_TAG("type", "Remove old corpses")); - _timers[WUPDATE_CORPSES].Reset(); - - sMapMgr->DoForAllMaps([](Map* map) - { - map->RemoveOldCorpses(); - }); - } - ///- Process Game events when necessary if (_timers[WUPDATE_EVENTS].Passed()) { @@ -1824,11 +1810,6 @@ void World::ProcessQueryCallbacks() _queryProcessor.ProcessReadyCallbacks(); } -void World::RemoveOldCorpses() -{ - _timers[WUPDATE_CORPSES].SetCurrent(_timers[WUPDATE_CORPSES].GetInterval()); -} - bool World::IsPvPRealm() const { return getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 2b8310efe..2f9169605 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -60,7 +60,6 @@ enum WorldTimers { WUPDATE_WEATHERS, WUPDATE_UPTIME, - WUPDATE_CORPSES, WUPDATE_EVENTS, WUPDATE_CLEANDB, WUPDATE_AUTOBROADCAST, @@ -241,8 +240,6 @@ public: [[nodiscard]] std::string const& GetRealmName() const override { return _realmName; } // pussywizard void SetRealmName(std::string name) override { _realmName = name; } // pussywizard - void RemoveOldCorpses() override; - protected: void _UpdateGameTime(); // callback for UpdateRealmCharacters diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index 0b0c31499..9878fced9 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -21,6 +21,7 @@ #include "GameTime.h" #include "GitRevision.h" #include "Log.h" +#include "MapMgr.h" #include "ModuleMgr.h" #include "MotdMgr.h" #include "MySQLThreading.h" @@ -101,7 +102,10 @@ public: // Triggering corpses expire check in world static bool HandleServerCorpsesCommand(ChatHandler* /*handler*/) { - sWorld->RemoveOldCorpses(); + sMapMgr->DoForAllMaps([](Map* map) + { + map->RemoveOldCorpses(); + }); return true; } From d69f61d7ab91f5beeeda0e2e79d9cc016a370b12 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Thu, 4 Sep 2025 06:19:25 -0300 Subject: [PATCH 099/155] fix(Core/SAI): Fix crash in SMART_TARGET_OWNER_OR_SUMMONER (#22789) --- src/server/game/AI/SmartScripts/SmartScript.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index e0a6f4fb8..c980f4f70 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -3834,19 +3834,20 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e, { targets.clear(); - if (owner->ToCreature()) + if (IsCreature(owner)) { if (Unit* base = ObjectAccessor::GetUnit(*owner, owner->ToCreature()->GetCharmerOrOwnerGUID())) - { targets.push_back(base); - } } - else + else if (IsGameObject(owner)) { if (Unit* base = ObjectAccessor::GetUnit(*owner, owner->ToGameObject()->GetOwnerGUID())) - { targets.push_back(base); - } + } + else if (IsPlayer(owner)) + { + if (Unit* base = owner->ToPlayer()->GetCharmerOrOwner()) + targets.push_back(base); } } } From 24c611a9c3fecd06c1196caf884d0f2ce6941fd1 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Thu, 4 Sep 2025 06:22:46 -0300 Subject: [PATCH 100/155] fix(DB/SAI): Despawn Frozen Phylactery on use (#22788) --- .../updates/pending_db_world/rev_1756947824261630300.sql | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756947824261630300.sql diff --git a/data/sql/updates/pending_db_world/rev_1756947824261630300.sql b/data/sql/updates/pending_db_world/rev_1756947824261630300.sql new file mode 100644 index 000000000..ed5149662 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756947824261630300.sql @@ -0,0 +1,6 @@ +-- +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 188141; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 1 AND `entryorguid` = 188141); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(188141, 1, 0, 0, 64, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Frozen Phylactery - On Gossip Hello - Despawn In 3000 ms'); From 300ed47bc48123067cde95b3456dff3accc72b45 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 4 Sep 2025 09:23:46 +0000 Subject: [PATCH 101/155] chore(DB): import pending files Referenced commit(s): 24c611a9c3fecd06c1196caf884d0f2ce6941fd1 --- .../rev_1756947824261630300.sql => db_world/2025_09_04_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756947824261630300.sql => db_world/2025_09_04_00.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1756947824261630300.sql b/data/sql/updates/db_world/2025_09_04_00.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1756947824261630300.sql rename to data/sql/updates/db_world/2025_09_04_00.sql index ed5149662..fc9d4a9a2 100644 --- a/data/sql/updates/pending_db_world/rev_1756947824261630300.sql +++ b/data/sql/updates/db_world/2025_09_04_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_03_02 -> 2025_09_04_00 -- UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 188141; From c9e1782d7a902b24439e2d55f4ae9ccf0c7c82af Mon Sep 17 00:00:00 2001 From: Yehonal Date: Thu, 4 Sep 2025 11:47:44 +0200 Subject: [PATCH 102/155] Refactor menu command handling for direct execution (#22791) --- .github/workflows/dashboard-ci.yml | 18 +++ apps/bash_shared/menu_system.sh | 119 ++++++++++++------ apps/compiler/compiler.sh | 19 +-- .../includes/modules-manager/modules.sh | 11 +- apps/installer/main.sh | 2 +- 5 files changed, 114 insertions(+), 55 deletions(-) diff --git a/.github/workflows/dashboard-ci.yml b/.github/workflows/dashboard-ci.yml index fbdd88124..8b22541fb 100644 --- a/.github/workflows/dashboard-ci.yml +++ b/.github/workflows/dashboard-ci.yml @@ -80,12 +80,30 @@ jobs: # Configure dashboard sed -i 's/MTHREADS=.*/MTHREADS="4"/' conf/config.sh + - name: Test module commands + run: | + ./acore.sh module install mod-autobalance + ./acore.sh module install mod-duel-reset + + ./acore.sh module list + + ./acore.sh module install --all + ./acore.sh module update mod-autobalance + ./acore.sh module update --all + - name: Run complete installation (deps, compile, database, client-data) run: | # This runs: install-deps, compile, database setup, client-data download ./acore.sh init timeout-minutes: 120 + - name: Test module removal + run: | + ./acore.sh module remove mod-autobalance + ./acore.sh module list + ./acore.sh module remove mod-duel-reset + ./acore.sh module list + - name: Test authserver dry-run run: | cd env/dist/bin diff --git a/apps/bash_shared/menu_system.sh b/apps/bash_shared/menu_system.sh index e7ae8b564..7477ad9b4 100644 --- a/apps/bash_shared/menu_system.sh +++ b/apps/bash_shared/menu_system.sh @@ -102,13 +102,6 @@ function menu_direct_execute() { local user_input="$1" shift - # Handle help requests directly - if [[ "$user_input" == "--help" || "$user_input" == "help" || "$user_input" == "-h" ]]; then - echo "Available commands:" - printf '%s\n' "${_MENU_OPTIONS[@]}" - return 0 - fi - # Disable numeric selection in direct mode if [[ "$user_input" =~ ^[0-9]+$ ]]; then echo "Invalid option. Numeric selection is not allowed when passing arguments." @@ -118,11 +111,25 @@ function menu_direct_execute() { # Find command and execute local idx - idx=$(menu_find_index "$user_input") + # try-catch + { + idx=$(menu_find_index "$user_input") + } || + { + idx=-1 + } + if [[ $idx -ge 0 ]]; then "$callback" "${_MENU_KEYS[$idx]}" "$@" return $? else + # Handle help requests directly + if [[ "$user_input" == "--help" || "$user_input" == "help" || "$user_input" == "-h" ]]; then + echo "Available commands:" + printf '%s\n' "${_MENU_OPTIONS[@]}" + return 0 + fi + echo "Invalid option. Use --help to see available commands." >&2 return 1 fi @@ -138,20 +145,32 @@ function menu_interactive() { menu_display "$title" read -r -p "Please enter your choice: " REPLY - # Handle help request - if [[ "$REPLY" == "--help" || "$REPLY" == "help" || "$REPLY" == "h" ]]; then - echo "Available commands:" - printf '%s\n' "${_MENU_OPTIONS[@]}" - echo "" - continue - fi + # Parse input to separate command from arguments + local input_parts=() + read -r -a input_parts <<< "$REPLY" + local user_command="${input_parts[0]}" + local user_args=("${input_parts[@]:1}") # Find and execute command local idx - idx=$(menu_find_index "$REPLY") + idx=$(menu_find_index "$user_command") if [[ $idx -ge 0 ]]; then - "$callback" "${_MENU_KEYS[$idx]}" + # Pass the command key and any additional arguments + "$callback" "${_MENU_KEYS[$idx]}" "${user_args[@]}" + local exit_code=$? + # Exit loop if callback returns 0 (e.g., quit command) + if [[ $exit_code -eq 0 && "${_MENU_KEYS[$idx]}" == "quit" ]]; then + break + fi else + # Handle help request + if [[ "$REPLY" == "--help" || "$REPLY" == "help" || "$REPLY" == "h" ]]; then + echo "Available commands:" + printf '%s\n' "${_MENU_OPTIONS[@]}" + echo "" + continue + fi + echo "Invalid option. Please try again or use 'help' for available commands." >&2 echo "" fi @@ -159,35 +178,65 @@ function menu_interactive() { } # Main menu runner function -# Usage: menu_run "Menu Title" callback_function menu_item1 menu_item2 ... "$@" +# Usage: menu_run "Menu Title" callback_function "$@" +# The menu items array should be defined globally before calling this function function menu_run() { local title="$1" local callback="$2" shift 2 - # Extract menu items (all arguments until we find command line args) - local menu_items=() - local found_args=false + # Define menu from globally available menu items array + # This expects the calling script to have set up the menu items - # Separate menu items from command line arguments - while [[ $# -gt 0 ]]; do - if [[ "$1" =~ \| ]]; then - # This looks like a menu item (contains pipe) - menu_items+=("$1") - shift - else - # This is a command line argument - found_args=true - break - fi + # Handle direct execution if arguments provided + if [[ $# -gt 0 ]]; then + menu_direct_execute "$callback" "$@" + return $? + fi + + # Run interactive menu + menu_interactive "$callback" "$title" +} + +# Alternative menu runner that accepts menu items directly +# Usage: menu_run_with_items "Menu Title" callback_function -- "${menu_items_array[@]}" -- "$@" +function menu_run_with_items() { + local title="$1" + local callback="$2" + shift 2 + + # Parse parameters: menu items are between first and second "--" + local menu_items=() + local script_args=() + + # Skip first "--" + if [[ "$1" == "--" ]]; then + shift + else + echo "Error: menu_run_with_items requires -- separator before menu items" >&2 + return 1 + fi + + # Collect menu items until second "--" + while [[ $# -gt 0 && "$1" != "--" ]]; do + menu_items+=("$1") + shift done - # Define menu from collected items + # Skip second "--" if present + if [[ "$1" == "--" ]]; then + shift + fi + + # Remaining args are script arguments + script_args=("$@") + + # Define menu from provided array menu_define "${menu_items[@]}" # Handle direct execution if arguments provided - if [[ $found_args == true ]]; then - menu_direct_execute "$callback" "$@" + if [[ ${#script_args[@]} -gt 0 ]]; then + menu_direct_execute "$callback" "${script_args[@]}" return $? fi diff --git a/apps/compiler/compiler.sh b/apps/compiler/compiler.sh index aa845b699..e27b35c2d 100755 --- a/apps/compiler/compiler.sh +++ b/apps/compiler/compiler.sh @@ -49,7 +49,7 @@ function handle_compiler_command() { ;; "quit") echo "Closing compiler menu..." - exit 0 + return 0 ;; *) echo "Invalid option. Use --help to see available commands." @@ -61,20 +61,5 @@ function handle_compiler_command() { # Hook support (preserved from original) runHooks "ON_AFTER_OPTIONS" # you can create your custom options -# Legacy switch function (preserved for compatibility) -function _switch() { - local reply="$1" - local opt="$2" - - case "$reply" in - ""|"--help") - menu_show_help - ;; - *) - run_option "$reply" "$opt" - ;; - esac -} - # Run the menu system -menu_run "ACORE COMPILER" handle_compiler_command "${comp_menu_items[@]}" "$@" +menu_run_with_items "ACORE COMPILER" handle_compiler_command -- "${comp_menu_items[@]}" -- "$@" diff --git a/apps/installer/includes/modules-manager/modules.sh b/apps/installer/includes/modules-manager/modules.sh index 761232964..88cadf01e 100644 --- a/apps/installer/includes/modules-manager/modules.sh +++ b/apps/installer/includes/modules-manager/modules.sh @@ -25,6 +25,7 @@ CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit ; pwd ) source "$CURRENT_PATH/../../../bash_shared/includes.sh" +source "$CURRENT_PATH/../includes.sh" source "$AC_PATH_APPS/bash_shared/menu_system.sh" # Module management menu definition @@ -65,7 +66,7 @@ function handle_module_command() { ;; "quit") echo "Exiting module manager..." - exit 0 + return 0 ;; *) echo "Invalid option. Use 'help' to see available commands." @@ -129,7 +130,7 @@ function inst_module_list() { function inst_module() { # If no arguments provided, start interactive menu if [[ $# -eq 0 ]]; then - menu_run "MODULE MANAGER" handle_module_command "${module_menu_items[@]}" + menu_run_with_items "MODULE MANAGER" handle_module_command -- "${module_menu_items[@]}" -- return $? fi @@ -727,6 +728,12 @@ function inst_module_install { # Update one or more modules function inst_module_update { + # Handle help request + if [[ "$1" == "--help" || "$1" == "-h" ]]; then + inst_module_help + return 0 + fi + # Support multiple modules and the --all flag; prompt if none specified. local args=("$@") local use_all=false diff --git a/apps/installer/main.sh b/apps/installer/main.sh index 3bdd99b22..b1cba33e8 100644 --- a/apps/installer/main.sh +++ b/apps/installer/main.sh @@ -104,4 +104,4 @@ function handle_menu_command() { } # Run the menu system -menu_run "ACORE DASHBOARD" handle_menu_command "${menu_items[@]}" "$@" +menu_run_with_items "ACORE DASHBOARD" handle_menu_command -- "${menu_items[@]}" -- "$@" From 1278ccff1264d0c19cf8bb7b30572e4fefea31c0 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:19:43 -0400 Subject: [PATCH 103/155] fix(DB/Creature): Remove incorrect other gender display from Vurtok Axebreaker. (#22782) --- data/sql/updates/pending_db_world/vurtok-axebreaker.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 data/sql/updates/pending_db_world/vurtok-axebreaker.sql diff --git a/data/sql/updates/pending_db_world/vurtok-axebreaker.sql b/data/sql/updates/pending_db_world/vurtok-axebreaker.sql new file mode 100644 index 000000000..401c76fdb --- /dev/null +++ b/data/sql/updates/pending_db_world/vurtok-axebreaker.sql @@ -0,0 +1 @@ +UPDATE `creature_model_info` SET `DisplayID_Other_Gender` = 0 WHERE `DisplayID` IN (16292, 16294); From 079e003caff0ae86a1899b80204225c841577514 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 4 Sep 2025 18:20:43 +0000 Subject: [PATCH 104/155] chore(DB): import pending files Referenced commit(s): 1278ccff1264d0c19cf8bb7b30572e4fefea31c0 --- .../vurtok-axebreaker.sql => db_world/2025_09_04_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/vurtok-axebreaker.sql => db_world/2025_09_04_01.sql} (69%) diff --git a/data/sql/updates/pending_db_world/vurtok-axebreaker.sql b/data/sql/updates/db_world/2025_09_04_01.sql similarity index 69% rename from data/sql/updates/pending_db_world/vurtok-axebreaker.sql rename to data/sql/updates/db_world/2025_09_04_01.sql index 401c76fdb..9a885fd6d 100644 --- a/data/sql/updates/pending_db_world/vurtok-axebreaker.sql +++ b/data/sql/updates/db_world/2025_09_04_01.sql @@ -1 +1,2 @@ +-- DB update 2025_09_04_00 -> 2025_09_04_01 UPDATE `creature_model_info` SET `DisplayID_Other_Gender` = 0 WHERE `DisplayID` IN (16292, 16294); From 091ff23130e189c2a82330deb5af7a09dd23ad50 Mon Sep 17 00:00:00 2001 From: sudlud Date: Thu, 4 Sep 2025 22:06:29 +0200 Subject: [PATCH 105/155] fix(DB/Gameobject): Sniffed Values for 'Gravestone' spawns (#22796) --- .../rev_1757011554068939700.sql | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757011554068939700.sql diff --git a/data/sql/updates/pending_db_world/rev_1757011554068939700.sql b/data/sql/updates/pending_db_world/rev_1757011554068939700.sql new file mode 100644 index 000000000..d7bfbb34c --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757011554068939700.sql @@ -0,0 +1,17 @@ +-- Update gameobject 'Gravestone' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (192256)) AND (`guid` IN (76993)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(76993, 192256, 571, 0, 0, 1, 1, 9025.6845703125, -1178.6163330078125, 1058.107666015625, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 46368, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (192257, 192258, 192260, 192265, 192380)) AND (`guid` BETWEEN 265 AND 269); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(265, 192257, 571, 0, 0, 1, 1, 8094.673828125, -995.29864501953125, 936.18206787109375, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 53788, NULL), +(266, 192258, 571, 0, 0, 1, 1, 7832.95556640625, -2018.984375, 1224.683349609375, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 52237, NULL), +(267, 192260, 571, 0, 0, 1, 1, 7463.06689453125, -3326.37841796875, 897.74884033203125, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 47720, NULL), +(268, 192265, 571, 0, 0, 1, 1, 6942.8603515625, -552.515625, 914.403564453125, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 50664, NULL), +(269, 192380, 571, 0, 0, 1, 1, 6431.64404296875, -1186.592041015625, 446.2081298828125, 3.141592741012573242, 0, 0, -1, 0, 120, 255, 1, "", 47720, NULL); + +-- remaining spawns (no sniffed values available) +-- (`guid` IN (77187)) From 42f603a94d53535c56f7fc012ddf89f7203c9594 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 4 Sep 2025 20:07:32 +0000 Subject: [PATCH 106/155] chore(DB): import pending files Referenced commit(s): 091ff23130e189c2a82330deb5af7a09dd23ad50 --- .../rev_1757011554068939700.sql => db_world/2025_09_04_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757011554068939700.sql => db_world/2025_09_04_02.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1757011554068939700.sql b/data/sql/updates/db_world/2025_09_04_02.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1757011554068939700.sql rename to data/sql/updates/db_world/2025_09_04_02.sql index d7bfbb34c..91873ddd1 100644 --- a/data/sql/updates/pending_db_world/rev_1757011554068939700.sql +++ b/data/sql/updates/db_world/2025_09_04_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_04_01 -> 2025_09_04_02 -- Update gameobject 'Gravestone' with sniffed values -- updated spawns DELETE FROM `gameobject` WHERE (`id` IN (192256)) AND (`guid` IN (76993)); From 3f25bee746bf48d59fa31c520dbe8871b7978838 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Thu, 4 Sep 2025 15:35:23 -0700 Subject: [PATCH 107/155] fix(DB/SmartAI): Quest Draconis Gastritis can't be completed (#22805) --- .../rev_1757021490795481832.sql | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757021490795481832.sql diff --git a/data/sql/updates/pending_db_world/rev_1757021490795481832.sql b/data/sql/updates/pending_db_world/rev_1757021490795481832.sql new file mode 100644 index 000000000..ade694550 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757021490795481832.sql @@ -0,0 +1,22 @@ +-- +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24170); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(24170, 0, 0, 3, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Summoned - Store Targetlist'), +(24170, 0, 1, 4, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 33, 24170, 0, 0, 0, 0, 0, 12, 1, 0, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Died - Quest Credit \'null\''), +(24170, 0, 2, 0, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Summoned - Set Visibility Off'), +(24170, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 50, 186598, 45, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Summoned - Summon Gameobject \'Tillinghast\'s Plagued Meat\''), +(24170, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 1, 0, 0, 0, 20, 186598, 10, 0, 0, 0, 0, 0, 0, 'Draconis Gastritis Bunny - On Just Died - Despawn Instant'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23689); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(23689, 0, 1, 4, 65, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 36809, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - On Follow Complete - Cast \'Overpowering Sickness\''), +(23689, 0, 3, 5, 1, 0, 100, 512, 10000, 10000, 10000, 10000, 0, 0, 29, 0, 0, 24170, 0, 0, 0, 19, 24170, 75, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Out of Combat - Start Follow Closest Creature \'Draconis Gastritis Bunny\''), +(23689, 0, 4, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 51, 0, 0, 0, 0, 0, 0, 19, 24170, 10, 0, 0, 0, 0, 0, 0, 'Proto-Drake - On Follow Complete - Kill Target'), +(23689, 0, 5, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Out of Combat - Set Event Phase 1'), +(23689, 0, 6, 0, 1, 1, 100, 512, 45000, 45000, 45000, 45000, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Out of Combat - Despawn Instant (Phase 1)'), +-- update comments with Keira +(23689, 0, 8, 0, 8, 0, 100, 0, 40969, 0, 120000, 120000, 0, 0, 69, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - On Spellhit \'Malister`s Frost Wand\' - Move To Invoker'), +(23689, 0, 9, 0, 9, 0, 100, 513, 0, 0, 0, 0, 0, 20, 101, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Within 0-20 Range - Set Home Position (No Repeat)'), +(23689, 0, 10, 0, 9, 0, 100, 0, 0, 0, 2000, 3500, 0, 5, 11, 51219, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Within 0-5 Range - Cast \'Flame Breath\''), +(23689, 0, 11, 0, 0, 0, 100, 0, 3000, 9000, 30000, 45000, 0, 0, 11, 42362, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - In Combat - Cast \'Flames of Birth\''), +(23689, 0, 12, 0, 9, 0, 100, 0, 0, 0, 10000, 15000, 0, 20, 11, 41572, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Proto-Drake - Within 0-20 Range - Cast \'Wing Buffet\''); From 741f69052e095b393021972879919c60c1f7b301 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 4 Sep 2025 22:36:56 +0000 Subject: [PATCH 108/155] chore(DB): import pending files Referenced commit(s): 3f25bee746bf48d59fa31c520dbe8871b7978838 --- .../rev_1757021490795481832.sql => db_world/2025_09_04_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757021490795481832.sql => db_world/2025_09_04_03.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1757021490795481832.sql b/data/sql/updates/db_world/2025_09_04_03.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1757021490795481832.sql rename to data/sql/updates/db_world/2025_09_04_03.sql index ade694550..07265fb35 100644 --- a/data/sql/updates/pending_db_world/rev_1757021490795481832.sql +++ b/data/sql/updates/db_world/2025_09_04_03.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_04_02 -> 2025_09_04_03 -- DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24170); INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES From 0205a9674f578cf22e2318511738e7178849e567 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Thu, 4 Sep 2025 21:06:26 -0300 Subject: [PATCH 109/155] =?UTF-8?q?fix(DB/SAI):=20Fix=20Leave=20no=20one?= =?UTF-8?q?=20Behind=20not=20granting=20credit=20if=20crusaders=E2=80=A6?= =?UTF-8?q?=20(#22790)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rev_1756957969387621700.sql | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1756957969387621700.sql diff --git a/data/sql/updates/pending_db_world/rev_1756957969387621700.sql b/data/sql/updates/pending_db_world/rev_1756957969387621700.sql new file mode 100644 index 000000000..9eae6fcb8 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1756957969387621700.sql @@ -0,0 +1,63 @@ +-- Prevent removal on evade +DELETE FROM `spell_custom_attr` WHERE `spell_id` IN (50665, 50681, 50695); +INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES +(50665, 2048), +(50681, 2048), +(50695, 2048); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28148); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28148, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Reset - Set Event Phase 1'), +(28148, 0, 1, 2, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50695, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Just Summoned - Cast \'Bleeding Out\''), +(28148, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Just Summoned - Start Follow Invoker'), +(28148, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Just Summoned - Remove FlagStandstate Sit Down'), +(28148, 0, 4, 0, 23, 1, 100, 513, 50695, 0, 0, 0, 0, 0, 80, 2814800, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Aura \'Bleeding Out\' - Run Script (Phase 1) (No Repeat)'), +(28148, 0, 5, 6, 40, 0, 100, 513, 4, 0, 0, 0, 0, 0, 90, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Point 4 of Path Any Reached - Set Flag Standstate Sit Down (No Repeat)'), +(28148, 0, 6, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 20000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Point 4 of Path Any Reached - Despawn In 20000 ms (No Repeat)'), +(28148, 0, 7, 8, 8, 1, 100, 512, 50669, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Set Event Phase 2 (Phase 1)'), +(28148, 0, 8, 9, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50698, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Cast \'Kill Credit Jospehine 01\' (Phase 1)'), +(28148, 0, 9, 10, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50711, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Cast \'Strip Aura Josephine 01\' (Phase 1)'), +(28148, 0, 10, 11, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50699, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Cross Cast \'Josephine Kill Credit\' (Phase 1)'), +(28148, 0, 11, 12, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50712, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Cross Cast \'Strip Aura Josephine\' (Phase 1)'), +(28148, 0, 12, 13, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Stop Follow (Phase 1)'), +(28148, 0, 13, 14, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Say Line 0 (Phase 1)'), +(28148, 0, 14, 15, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 0, 28148, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Start Waypoint Path 28148 (Phase 1)'), +(28148, 0, 15, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Josephine - On Spellhit \'Quest Credit\' - Remove Npc Flags Gossip (Phase 1)'); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28142) AND (`source_type` = 0); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28142, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Reset - Set Event Phase 1'), +(28142, 0, 1, 2, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50681, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Just Summoned - Cast \'Bleeding Out\''), +(28142, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Just Summoned - Start Follow Invoker'), +(28142, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Just Summoned - Remove FlagStandstate Sit Down'), +(28142, 0, 4, 0, 23, 1, 100, 513, 50681, 0, 0, 0, 0, 0, 80, 2814200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Aura \'Bleeding Out\' - Run Script (Phase 1) (No Repeat)'), +(28142, 0, 5, 6, 40, 0, 100, 513, 5, 0, 0, 0, 0, 0, 90, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Point 5 of Path Any Reached - Set Flag Standstate Sit Down (No Repeat)'), +(28142, 0, 6, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 20000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Point 5 of Path Any Reached - Despawn In 20000 ms (No Repeat)'), +(28142, 0, 7, 8, 8, 1, 100, 512, 50669, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Set Event Phase 2 (Phase 1)'), +(28142, 0, 8, 9, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50683, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Cast \'Kill Credit Lamoof 01\' (Phase 1)'), +(28142, 0, 9, 10, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50723, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Cast \'Strip Aura Lamoof 01\' (Phase 1)'), +(28142, 0, 10, 11, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50684, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Cross Cast \'Lamoof Kill Credit\' (Phase 1)'), +(28142, 0, 11, 12, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50722, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Cross Cast \'Strip Aura Lamoof\' (Phase 1)'), +(28142, 0, 12, 13, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Stop Follow (Phase 1)'), +(28142, 0, 13, 14, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Say Line 0 (Phase 1)'), +(28142, 0, 14, 15, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 0, 28142, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Start Waypoint Path 28142 (Phase 1)'), +(28142, 0, 15, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Lamoof - On Spellhit \'Quest Credit\' - Remove Npc Flags Gossip (Phase 1)'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28136); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28136, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Reset - Set Event Phase 1'), +(28136, 0, 1, 2, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50665, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Just Summoned - Cast \'Bleeding Out\''), +(28136, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Just Summoned - Start Follow Invoker'), +(28136, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Just Summoned - Remove FlagStandstate Sit Down'), +(28136, 0, 4, 0, 23, 1, 100, 513, 50665, 0, 0, 0, 0, 0, 80, 2813600, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Aura \'Bleeding Out\' - Run Script (Phase 1) (No Repeat)'), +(28136, 0, 5, 6, 40, 0, 100, 513, 5, 0, 0, 0, 0, 0, 90, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Point 5 of Path Any Reached - Set Flag Standstate Sit Down (No Repeat)'), +(28136, 0, 6, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 20000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Point 5 of Path Any Reached - Despawn In 20000 ms (No Repeat)'), +(28136, 0, 7, 8, 8, 1, 100, 512, 50669, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Set Event Phase 2 (Phase 1)'), +(28136, 0, 8, 9, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50671, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Cast \'Kill Credit Jonathan 01\' (Phase 1)'), +(28136, 0, 9, 10, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 50709, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Cast \'Strip Aura Jonathan 01\' (Phase 1)'), +(28136, 0, 10, 11, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50680, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Cross Cast \'Jonathan Kill Credit\' (Phase 1)'), +(28136, 0, 11, 12, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 86, 50710, 2, 23, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Cross Cast \'Strip Aura Jonanthan\' (Phase 1)'), +(28136, 0, 12, 13, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Stop Follow (Phase 1)'), +(28136, 0, 13, 14, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Say Line 0 (Phase 1)'), +(28136, 0, 14, 15, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 0, 28136, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Start Waypoint Path 28136 (Phase 1)'), +(28136, 0, 15, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crusader Jonathan - On Spellhit \'Quest Credit\' - Remove Npc Flags Gossip (Phase 1)'); From 9320f9e1e6df5456670efaf6abae87c990486b39 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 5 Sep 2025 00:07:30 +0000 Subject: [PATCH 110/155] chore(DB): import pending files Referenced commit(s): 0205a9674f578cf22e2318511738e7178849e567 --- .../rev_1756957969387621700.sql => db_world/2025_09_05_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1756957969387621700.sql => db_world/2025_09_05_00.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1756957969387621700.sql b/data/sql/updates/db_world/2025_09_05_00.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1756957969387621700.sql rename to data/sql/updates/db_world/2025_09_05_00.sql index 9eae6fcb8..d5bb66322 100644 --- a/data/sql/updates/pending_db_world/rev_1756957969387621700.sql +++ b/data/sql/updates/db_world/2025_09_05_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_04_03 -> 2025_09_05_00 -- Prevent removal on evade DELETE FROM `spell_custom_attr` WHERE `spell_id` IN (50665, 50681, 50695); INSERT INTO `spell_custom_attr` (`spell_id`, `attributes`) VALUES From f7aa69e389590fb3a611a7dbf627ac8aa58fc498 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Fri, 5 Sep 2025 09:12:59 -0400 Subject: [PATCH 111/155] fix(DB/Creature): Correct spells for Orc Warlock chess piece. (#22770) --- data/sql/updates/pending_db_world/kara-chess-warlock.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/kara-chess-warlock.sql diff --git a/data/sql/updates/pending_db_world/kara-chess-warlock.sql b/data/sql/updates/pending_db_world/kara-chess-warlock.sql new file mode 100644 index 000000000..ccf508662 --- /dev/null +++ b/data/sql/updates/pending_db_world/kara-chess-warlock.sql @@ -0,0 +1,4 @@ +DELETE FROM `creature_template_spell` WHERE `CreatureID` = 21750 AND `Index` IN (2, 3); +INSERT INTO `creature_template_spell` (`CreatureID`, `Index`, `Spell`, `VerifiedBuild`) VALUES +(21750, 2, 37463, 0), +(21750, 3, 37469, 0); From 0210e565da50d94104eb128a80eb0edf4a5c60c0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 5 Sep 2025 13:15:32 +0000 Subject: [PATCH 112/155] chore(DB): import pending files Referenced commit(s): f7aa69e389590fb3a611a7dbf627ac8aa58fc498 --- .../kara-chess-warlock.sql => db_world/2025_09_05_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/kara-chess-warlock.sql => db_world/2025_09_05_01.sql} (83%) diff --git a/data/sql/updates/pending_db_world/kara-chess-warlock.sql b/data/sql/updates/db_world/2025_09_05_01.sql similarity index 83% rename from data/sql/updates/pending_db_world/kara-chess-warlock.sql rename to data/sql/updates/db_world/2025_09_05_01.sql index ccf508662..0e87f6295 100644 --- a/data/sql/updates/pending_db_world/kara-chess-warlock.sql +++ b/data/sql/updates/db_world/2025_09_05_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_05_00 -> 2025_09_05_01 DELETE FROM `creature_template_spell` WHERE `CreatureID` = 21750 AND `Index` IN (2, 3); INSERT INTO `creature_template_spell` (`CreatureID`, `Index`, `Spell`, `VerifiedBuild`) VALUES (21750, 2, 37463, 0), From fdc81f3be016a754f7708829a455d3f4f5376ce7 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sat, 6 Sep 2025 00:44:07 -0700 Subject: [PATCH 113/155] fix(Scripts/Spells): Shadow of Doom death sometimes deals no damage (#22813) --- src/server/game/Spells/SpellInfoCorrections.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 4ba8f62c6..e5360809f 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -5143,6 +5143,14 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->DurationEntry = sSpellDurationStore.LookupEntry(21); // -1 }); + ApplySpellFix({ + 28032, // Zap Crystal + 28056, // Zap Crystal Corpse + }, [](SpellInfo* spellInfo) + { + spellInfo->AttributesEx3 |= SPELL_ATTR3_ALWAYS_HIT; + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i]; From 82454c2261c8f72b50feb00e761c7e0424624c1e Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sat, 6 Sep 2025 00:44:15 -0700 Subject: [PATCH 114/155] fix(DB/Creature): Necrotic Shard is sometimes regenerating health (#22812) --- data/sql/updates/pending_db_world/rev_1757124004480055823.sql | 2 ++ src/server/scripts/World/scourge_invasion.cpp | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 data/sql/updates/pending_db_world/rev_1757124004480055823.sql diff --git a/data/sql/updates/pending_db_world/rev_1757124004480055823.sql b/data/sql/updates/pending_db_world/rev_1757124004480055823.sql new file mode 100644 index 000000000..6da883a59 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757124004480055823.sql @@ -0,0 +1,2 @@ +-- +UPDATE `creature_template` SET `RegenHealth` = 0 WHERE (`entry` IN (16136, 16172)); diff --git a/src/server/scripts/World/scourge_invasion.cpp b/src/server/scripts/World/scourge_invasion.cpp index 44cb434be..dc61a79ce 100644 --- a/src/server/scripts/World/scourge_invasion.cpp +++ b/src/server/scripts/World/scourge_invasion.cpp @@ -362,7 +362,6 @@ struct npc_necrotic_shard : public ScriptedAI { scheduler.Schedule(5s, [this](TaskContext context) // Spawn Cultists every 60 minutes. { - me->SetFullHealth(); DespawnShadowsOfDoom(); // Despawn all remaining Shadows before respawning the Cultists? SummonCultists(); context.Repeat(1h); From 2ac43d17229e8abbffffe211604702acc6766b64 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sat, 6 Sep 2025 00:45:00 -0700 Subject: [PATCH 115/155] fix(DB/Event): missing Ironforge Argent Quartermaster (#22810) Co-authored-by: Orozxy --- .../updates/pending_db_world/rev_1757111466618260938.sql | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757111466618260938.sql diff --git a/data/sql/updates/pending_db_world/rev_1757111466618260938.sql b/data/sql/updates/pending_db_world/rev_1757111466618260938.sql new file mode 100644 index 000000000..7de57ec4f --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757111466618260938.sql @@ -0,0 +1,8 @@ +-- +SET @CGUID:=12891; +DELETE FROM `creature` WHERE (`id1` = 16786) AND (`guid` = (@CGUID)); +INSERT INTO `creature` (`guid`, `id1`, `id2`, `id3`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `wander_distance`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`, `ScriptName`, `Comment`, `VerifiedBuild`) VALUES +(@CGUID, 16786, 0, 0, 0, 0, 0, 1, 1, 0, -4926.95, -981.718, 501.55, 2.0071299076080322, 120, 0, 0, 1, 0, 0, 0, 0, 0, '', '', 0); + +DELETE FROM `game_event_creature` WHERE `guid` = @CGUID; +INSERT INTO `game_event_creature` (`eventEntry`, `guid`) VALUES(17, @CGUID); From d2aa2fc5ee8bdc279202775c0c09a66df2c62ecd Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 6 Sep 2025 07:45:10 +0000 Subject: [PATCH 116/155] chore(DB): import pending files Referenced commit(s): fdc81f3be016a754f7708829a455d3f4f5376ce7 --- .../rev_1757111466618260938.sql => db_world/2025_09_06_00.sql} | 1 + .../rev_1757124004480055823.sql => db_world/2025_09_06_01.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/rev_1757111466618260938.sql => db_world/2025_09_06_00.sql} (94%) rename data/sql/updates/{pending_db_world/rev_1757124004480055823.sql => db_world/2025_09_06_01.sql} (66%) diff --git a/data/sql/updates/pending_db_world/rev_1757111466618260938.sql b/data/sql/updates/db_world/2025_09_06_00.sql similarity index 94% rename from data/sql/updates/pending_db_world/rev_1757111466618260938.sql rename to data/sql/updates/db_world/2025_09_06_00.sql index 7de57ec4f..04d9dde00 100644 --- a/data/sql/updates/pending_db_world/rev_1757111466618260938.sql +++ b/data/sql/updates/db_world/2025_09_06_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_05_01 -> 2025_09_06_00 -- SET @CGUID:=12891; DELETE FROM `creature` WHERE (`id1` = 16786) AND (`guid` = (@CGUID)); diff --git a/data/sql/updates/pending_db_world/rev_1757124004480055823.sql b/data/sql/updates/db_world/2025_09_06_01.sql similarity index 66% rename from data/sql/updates/pending_db_world/rev_1757124004480055823.sql rename to data/sql/updates/db_world/2025_09_06_01.sql index 6da883a59..6830e36d2 100644 --- a/data/sql/updates/pending_db_world/rev_1757124004480055823.sql +++ b/data/sql/updates/db_world/2025_09_06_01.sql @@ -1,2 +1,3 @@ +-- DB update 2025_09_06_00 -> 2025_09_06_01 -- UPDATE `creature_template` SET `RegenHealth` = 0 WHERE (`entry` IN (16136, 16172)); From 18723e298e7953571d51731fdcc9c3ed27fd725f Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sat, 6 Sep 2025 00:48:23 -0700 Subject: [PATCH 117/155] fix(DB/SAI): Scourge Invasion mini bosses various issues (#22809) Co-authored-by: Ryan Turner --- .../rev_1757106899882085716.sql | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757106899882085716.sql diff --git a/data/sql/updates/pending_db_world/rev_1757106899882085716.sql b/data/sql/updates/pending_db_world/rev_1757106899882085716.sql new file mode 100644 index 000000000..dd94e2ae8 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757106899882085716.sql @@ -0,0 +1,79 @@ +-- +-- Changes Scorn's spawn from Event 17 (Scourge Invasion) to 120 (Scourge Invasion - Boss in instance activation) +UPDATE `game_event_creature` SET `eventEntry` = 120 WHERE `guid` = 248652; + +-- Adds "FORCE_GOSSIP" for Sever +UPDATE `creature_template` SET `type_flags` = 134217728 WHERE `entry` = 14682; +-- Adds "FORCE_GOSSIP" and changes from "Warrior" to "Paladin" for Balzaphon, Lady Falther'ess and Scorn +UPDATE `creature_template` SET `unit_class` = 2, `type_flags` = 134217728 WHERE `entry` IN (14684, 14686, 14690, 14693); +-- Makes Balzaphon and Revanchion immune to Charge +UPDATE `creature_template` SET `mechanic_immune_mask` = (`mechanic_immune_mask` | 2048) WHERE `entry` IN (14684, 14690); +-- Scorn immune to root +UPDATE `creature_template` SET `mechanic_immune_mask` = (`mechanic_immune_mask` | 64) WHERE `entry` IN (14693); + +-- Adds SAI to Sever, Balzaphon, Lady Falther'ess, Revanchion and Lord Blackwood +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` IN (14682, 14684, 14686, 14695); + +-- Adds Spirit Particles (purple) to Lord Blackwood +UPDATE `creature_template_addon` SET `auras` = '28126' WHERE `entry` = 14695; + +-- Adds Spirit Particles (purple) and Frost Armor to Revanchion +UPDATE `creature_template_addon` SET `auras` = '28126 12556' WHERE `entry` = 14690 ; + +-- Adds SAI to Holding Pen (157819) +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 157819; + +-- Adds Spirit Particles (purple) to Sever, Balzaphon, Revanchion and Scorn +DELETE FROM `creature_template_addon` WHERE `entry` IN (14684, 14686, 14690, 14693, 14682); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(14682, 0, 0, 0, 1, 0, 0, '28126'), -- Sever +(14684, 0, 0, 0, 1, 0, 0, '28126'), -- Balzaphon +(14690, 0, 0, 0, 1, 0, 0, '28126'), -- Revanchion +(14693, 0, 0, 0, 1, 0, 0, '28126'); -- Scorn + +-- Adds Server and Lady Falther'ess texts. +DELETE FROM `creature_text` WHERE `CreatureID` IN (14682, 14686); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(14682, 0, 0, '%s goes into a frenzy!', 16, 0, 100, 0, 0, 0, 1191, 0, '[Sever] Sever goes into a frenzy! / %s goes into a frenzy!'), +(14686, 0, 0, 'Thank you for becoming my next victim!', 14, 0, 100, 0, 0, 0, 12429, 0, '[Lady Falther\'ess] Thank you for becoming my next victim!'); + +-- SmartGameObjectAI +-- 157819 (Holding Pen), when the "holding pen" is opened, saves data variable and sends to lady father'ess so it can be used after as a initatior of encounter with the player. +DELETE FROM `smart_scripts` WHERE `source_type` = 1 AND `entryorguid` = 157819; +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(157819, 1, 0, 1, 70, 0, 100, 0, 2, 0, 0, 0, 0, 0, 64, 1, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Holding Pen - On Gameobject State Changed - Store Targetlist to Lady Falther\'ess'), +(157819, 1, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 100, 1, 0, 0, 0, 0, 0, 19, 14686, 0, 0, 0, 0, 0, 0, 0, 'Holding Pen - On Gameobject State Changed - Send Target 1 to Lady Falther\'ess'), +(157819, 1, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 45, 1, 1, 0, 0, 0, 0, 19, 14686, 0, 0, 0, 0, 0, 0, 0, 'Holding Pen - On Gameobject State Changed - Set Data 1 1 to Lady Falther\'ess'); + +-- SmartAI +-- Adds SAI logic to Sever, Balzaphon, Lady Falther'ess, Revanchion and Lord Blackwood +DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `entryorguid` IN (14682, 14684, 14686, 14690, 14695); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +-- Sever +(14682, 0, 0, 0, 0, 0, 100, 0, 12000, 31000, 8000, 30000, 0, 0, 11, 17745, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Sever - In Combat - Cast \'Diseased Spit\''), +(14682, 0, 1, 2, 2, 0, 100, 0, 1, 50, 0, 0, 0, 0, 11, 8269, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sever - Between 1-50% Health - Cast \'Frenzy\''), +(14682, 0, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Sever - Between 1-50% Health - Say Line 0 "Sever goes into a frenzy!"'), +(14682, 0, 3, 0, 101, 0, 100, 0, 2, 10, 12500, 10000, 15000, 0, 11, 16508, 0, 0, 0, 0, 0, 17, 0, 10, 5, 0, 0, 0, 0, 0, 'Sever - On 2 or More Players in Range - Cast \'Intimidating Roar\''), +-- Balzaphon +(14684, 0, 0, 0, 0, 0, 100, 0, 2000, 7000, 2000, 5000, 0, 0, 11, 16799, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Balzaphon - In Combat - Cast \'Frostbolt\''), +(14684, 0, 1, 0, 0, 0, 100, 0, 6000, 12000, 7000, 15000, 0, 0, 11, 15244, 0, 0, 0, 0, 0, 5, 10, 0, 2, 0, 0, 0, 0, 0, 'Balzaphon - In Combat - Cast \'Cone of Cold\''), +(14684, 0, 2, 0, 0, 0, 100, 0, 10000, 20000, 12000, 20000, 0, 0, 11, 8398, 0, 0, 0, 0, 0, 5, 20, 0, 2, 0, 0, 0, 0, 0, 'Balzaphon - In Combat - Cast \'Frostbolt Volley\''), +-- Lady Falther'ess +(14686, 0, 0, 1, 37, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 28533, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Initialize - Cast \'Transform\' (Salma Saldean)'), +(14686, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 2, 35, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Initialize - Set Faction 35 (Friendly)'), +(14686, 0, 2, 3, 38, 0, 100, 0, 1, 1, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Demorph'), +(14686, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Say Line 0 - Thank you for becoming my next victim! '), +(14686, 0, 4, 5, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 2, 21, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Set Faction 21 (Undead, Scourge)'), +(14686, 0, 5, 6, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 21, 30, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Start Attacking (Closest Player within 30 yards)'), +(14686, 0, 6, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 75, 28126, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On Data Set 1 1 - Add Aura \'Spirit Particles (purple)\''), +(14686, 0, 7, 0, 0, 0, 100, 0, 2500, 8000, 10000, 18000, 0, 0, 11, 22743, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Lady Falther\'ess - In Combat - Cast \'Ribbon of Souls\''), +(14686, 0, 8, 0, 0, 0, 100, 0, 17500, 20000, 19000, 22000, 0, 0, 11, 17105, 0, 0, 0, 0, 0, 5, 30, 1, 2, 17105, 0, 0, 0, 0, 'Lady Falther\'ess - In Combat - Cast \'Banshee Curse\''), +(14686, 0, 9, 0, 101, 0, 100, 0, 2, 10, 7500, 5000, 6000, 0, 11, 16838, 0, 0, 5, 0, 0, 17, 0, 5, 5, 0, 0, 0, 0, 0, 'Lady Falther\'ess - On 2 or More Players in Range - Cast \'Banshee Shriek\''), +-- Revanchion +(14690, 0, 0, 0, 0, 0, 100, 0, 10000, 15000, 12500, 14000, 0, 0, 11, 15245, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Revanchion - In Combat - Cast \'Shadow Bolt Volley\''), +(14690, 0, 1, 0, 0, 0, 100, 0, 13000, 16000, 14000, 18000, 0, 0, 11, 14907, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Revanchion - In Combat - Cast \'Frost Nova\''), +-- Lord Blackwood +(14695, 0, 0, 0, 0, 0, 100, 0, 8000, 16000, 20000, 20000, 0, 0, 11, 7964, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Blackwood - In Combat - Cast \'Smoke Bomb\''), +(14695, 0, 1, 0, 105, 0, 100, 0, 10000, 12000, 10000, 12000, 0, 5, 11, 11972, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Blackwood - On Hostile Casting in Range - Cast \'Shield Bash\''), +(14695, 0, 2, 0, 110, 0, 100, 0, 2000, 20000, 20000, 20000, 0, 1, 11, 20733, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Blackwood - On Melee Range Target - Cast \'Black Arrow\''), +(14695, 0, 3, 0, 110, 0, 100, 0, 0, 0, 2400, 2400, 0, 1, 11, 16496, 64, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Blackwood - On Melee Range Target - Cast \'Shoot\''); From 725b475dd4522b34b2182c9721671f1a49ca1c80 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 6 Sep 2025 07:49:23 +0000 Subject: [PATCH 118/155] chore(DB): import pending files Referenced commit(s): 18723e298e7953571d51731fdcc9c3ed27fd725f --- .../rev_1757106899882085716.sql => db_world/2025_09_06_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757106899882085716.sql => db_world/2025_09_06_02.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1757106899882085716.sql b/data/sql/updates/db_world/2025_09_06_02.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1757106899882085716.sql rename to data/sql/updates/db_world/2025_09_06_02.sql index dd94e2ae8..372e87753 100644 --- a/data/sql/updates/pending_db_world/rev_1757106899882085716.sql +++ b/data/sql/updates/db_world/2025_09_06_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_06_01 -> 2025_09_06_02 -- -- Changes Scorn's spawn from Event 17 (Scourge Invasion) to 120 (Scourge Invasion - Boss in instance activation) UPDATE `game_event_creature` SET `eventEntry` = 120 WHERE `guid` = 248652; From d3a6c09b3164d5ad0f05924bdad30f98416e6503 Mon Sep 17 00:00:00 2001 From: Yehonal Date: Sat, 6 Sep 2025 11:22:22 +0200 Subject: [PATCH 119/155] feat(config): add support for excluding modules during installation and updates (#22793) --- .../includes/modules-manager/README.md | 145 ++++++- .../includes/modules-manager/modules.sh | 308 +++++++++++--- apps/installer/test/test_module_commands.bats | 398 ++++++++++++++++++ conf/dist/config.sh | 10 + 4 files changed, 785 insertions(+), 76 deletions(-) diff --git a/apps/installer/includes/modules-manager/README.md b/apps/installer/includes/modules-manager/README.md index f478035be..93496a91a 100644 --- a/apps/installer/includes/modules-manager/README.md +++ b/apps/installer/includes/modules-manager/README.md @@ -9,6 +9,10 @@ This directory contains the module management system for AzerothCore, providing - **Custom Directory Naming**: Prevent conflicts with custom directory names - **Duplicate Prevention**: Smart detection and prevention of duplicate installations - **Multi-Host Support**: GitHub, GitLab, and other Git hosts +- **Module Exclusion**: Support for excluding modules via environment variable +- **Interactive Menu System**: Easy-to-use menu interface for module management +- **Colored Output**: Enhanced terminal output with color support (respects NO_COLOR) +- **Flat Directory Structure**: Uses flat module installation (no owner subfolders) ## 📁 File Structure @@ -100,10 +104,33 @@ repo[:dirname][@branch[:commit]] # Search with multiple terms ./acore.sh module search auction house -# Show all available modules +# Search with input prompt ./acore.sh module search ``` +### Listing Installed Modules + +```bash +# List all installed modules +./acore.sh module list +``` + +### Interactive Menu + +```bash +# Start interactive menu system +./acore.sh module + +# Menu options: +# s - Search for available modules +# i - Install one or more modules +# u - Update installed modules +# r - Remove installed modules +# l - List installed modules +# h - Show detailed help +# q - Close this menu +``` + ## 🔍 Cross-Format Recognition The system intelligently recognizes the same module across different specification formats: @@ -129,13 +156,55 @@ The system prevents common conflicts: ```bash # If 'mod-transmog' directory already exists: $ ./acore.sh module install mod-transmog:mod-transmog -Error: Directory 'mod-transmog' already exists. Possible solutions: 1. Use a different directory name: mod-transmog:my-custom-name 2. Remove the existing directory first 3. Use the update command if this is the same module ``` +### Duplicate Module Prevention +The system uses intelligent owner/name matching to prevent installing the same module multiple times, even when specified in different formats. + +## 🚫 Module Exclusion + +You can exclude modules from installation using the `MODULES_EXCLUDE_LIST` environment variable: + +```bash +# Exclude specific modules (space-separated) +export MODULES_EXCLUDE_LIST="mod-test-module azerothcore/mod-dev-only" +./acore.sh module install --all # Will skip excluded modules + +# Supports cross-format matching +export MODULES_EXCLUDE_LIST="https://github.com/azerothcore/mod-transmog.git" +./acore.sh module install mod-transmog # Will be skipped as excluded +``` + +The exclusion system: +- Uses the same cross-format recognition as other module operations +- Works with all installation methods (`install`, `install --all`) +- Provides clear feedback when modules are skipped +- Supports URLs, owner/name format, and simple names + +## 🎨 Color Support + +The module manager provides enhanced terminal output with colors: + +- **Info**: Cyan text for informational messages +- **Success**: Green text for successful operations +- **Warning**: Yellow text for warnings +- **Error**: Red text for errors +- **Headers**: Bold cyan text for section headers + +Color support is automatically disabled when: +- Output is not to a terminal (piped/redirected) +- `NO_COLOR` environment variable is set +- Terminal doesn't support colors + +You can force color output with: +```bash +export FORCE_COLOR=1 +``` + ## 🔄 Integration ### Including in Scripts @@ -165,24 +234,78 @@ azerothcore/mod-transmog master abc123def456 https://github.com/custom/mod-custom.git develop def456abc789 mod-eluna:custom-eluna-dir main 789abc123def ``` + +The list maintains: +- **Alphabetical ordering** by normalized owner/name for consistency +- **Original format preservation** of how modules were specified +- **Automatic deduplication** across different specification formats +- **Custom directory tracking** when specified + ## 🔧 Configuration ### Environment Variables -- `MODULES_LIST_FILE`: Override default modules list path -- `J_PATH_MODULES`: Modules installation directory -- `AC_PATH_ROOT`: AzerothCore root path + +| Variable | Description | Default | +|----------|-------------|---------| +| `MODULES_LIST_FILE` | Override default modules list path | `$AC_PATH_ROOT/conf/modules.list` | +| `MODULES_EXCLUDE_LIST` | Space-separated list of modules to exclude | - | +| `J_PATH_MODULES` | Modules installation directory | `$AC_PATH_ROOT/modules` | +| `AC_PATH_ROOT` | AzerothCore root path | - | +| `NO_COLOR` | Disable colored output | - | +| `FORCE_COLOR` | Force colored output even when not TTY | - | ### Default Paths -- Modules list: `$AC_PATH_ROOT/conf/modules.list` +- **Modules list**: `$AC_PATH_ROOT/conf/modules.list` +- **Installation directory**: `$J_PATH_MODULES` (flat structure, no owner subfolders) + +## 🏗️ Architecture + +### Core Functions + +| Function | Purpose | +|----------|---------| +| `inst_module()` | Main dispatcher and interactive menu | +| `inst_parse_module_spec()` | Parse advanced module syntax | +| `inst_extract_owner_name()` | Normalize modules for cross-format recognition | +| `inst_mod_list_*()` | Module list management (read/write/update) | +| `inst_module_*()` | Module operations (install/update/remove/search) | + +### Key Features + +- **Flat Directory Structure**: All modules install directly under `modules/` without owner subdirectories +- **Smart Conflict Detection**: Prevents directory name conflicts with helpful suggestions +- **Cross-Platform Compatibility**: Works on Linux, macOS, and Windows (Git Bash) +- **Version Compatibility**: Checks `acore-module.json` for AzerothCore version compatibility +- **Git Integration**: Uses Joiner system for Git repository management + +### Debug Mode + +For debugging module operations, you can examine the generated commands: +```bash +# Check what Joiner commands would be executed +tail -f /tmp/joiner_called.txt # In test environments +``` ## 🤝 Contributing When modifying the module manager: -1. Maintain backwards compatibility -2. Update tests in `test_module_commands.bats` -3. Update this documentation -4. Test cross-format recognition thoroughly -5. Ensure helpful error messages +1. **Maintain backwards compatibility** with existing module list format +2. **Update tests** in `test_module_commands.bats` for new functionality +3. **Update this documentation** for any new features or changes +4. **Test cross-format recognition** thoroughly across all supported formats +5. **Ensure helpful error messages** for common user mistakes +6. **Test exclusion functionality** with various module specification formats +7. **Verify color output** works correctly in different terminal environments +### Testing Guidelines +```bash +# Run all module-related tests +cd apps/installer +bats test/test_module_commands.bats + +# Test with different environments +NO_COLOR=1 ./acore.sh module list +FORCE_COLOR=1 ./acore.sh module help +``` diff --git a/apps/installer/includes/modules-manager/modules.sh b/apps/installer/includes/modules-manager/modules.sh index 88cadf01e..787d07677 100644 --- a/apps/installer/includes/modules-manager/modules.sh +++ b/apps/installer/includes/modules-manager/modules.sh @@ -28,6 +28,49 @@ source "$CURRENT_PATH/../../../bash_shared/includes.sh" source "$CURRENT_PATH/../includes.sh" source "$AC_PATH_APPS/bash_shared/menu_system.sh" +# ----------------------------------------------------------------------------- +# Color support (disabled when not a TTY or NO_COLOR is set) +# ----------------------------------------------------------------------------- +if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then + if command -v tput >/dev/null 2>&1; then + _ac_cols=$(tput colors 2>/dev/null || echo 0) + else + _ac_cols=0 + fi +else + _ac_cols=0 +fi + +if [ "${FORCE_COLOR:-}" != "" ] || [ "${_ac_cols}" -ge 8 ]; then + C_RESET='\033[0m' + C_BOLD='\033[1m' + C_DIM='\033[2m' + C_RED='\033[31m' + C_GREEN='\033[32m' + C_YELLOW='\033[33m' + C_BLUE='\033[34m' + C_MAGENTA='\033[35m' + C_CYAN='\033[36m' +else + C_RESET='' + C_BOLD='' + C_DIM='' + C_RED='' + C_GREEN='' + C_YELLOW='' + C_BLUE='' + C_MAGENTA='' + C_CYAN='' +fi + +# Simple helpers for consistent colored output +function print_info() { printf "%b\n" "${C_CYAN}$*${C_RESET}"; } +function print_warn() { printf "%b\n" "${C_YELLOW}$*${C_RESET}"; } +function print_error() { printf "%b\n" "${C_RED}$*${C_RESET}"; } +function print_success() { printf "%b\n" "${C_GREEN}$*${C_RESET}"; } +function print_skip() { printf "%b\n" "${C_BLUE}$*${C_RESET}"; } +function print_header() { printf "%b\n" "${C_BOLD}${C_CYAN}$*${C_RESET}"; } + # Module management menu definition # Format: "key|short|description" module_menu_items=( @@ -65,11 +108,11 @@ function handle_module_command() { inst_module_help ;; "quit") - echo "Exiting module manager..." + print_info "Exiting module manager..." return 0 ;; *) - echo "Invalid option. Use 'help' to see available commands." + print_error "Invalid option. Use 'help' to see available commands." return 1 ;; esac @@ -77,7 +120,7 @@ function handle_module_command() { # Show detailed module help function inst_module_help() { - echo "AzerothCore Module Manager Help" + print_header "AzerothCore Module Manager Help" echo "===============================" echo "" echo "Usage:" @@ -106,20 +149,20 @@ function inst_module_help() { # List installed modules function inst_module_list() { - echo "Installed Modules:" + print_header "Installed Modules" echo "==================" local count=0 while read -r repo_ref branch commit; do [[ -z "$repo_ref" ]] && continue count=$((count + 1)) - echo " $count. $repo_ref ($branch)" + printf " %s. %b (%s)%b\n" "$count" "${C_GREEN}${repo_ref}" "${branch}" "${C_RESET}" if [[ "$commit" != "-" ]]; then - echo " Commit: $commit" + printf " %bCommit:%b %s\n" "${C_DIM}" "${C_RESET}" "$commit" fi done < <(inst_mod_list_read) if [[ $count -eq 0 ]]; then - echo " No modules installed." + print_warn " No modules installed." fi echo "" } @@ -160,8 +203,7 @@ function inst_module() { inst_module_list "${args[@]}" ;; *) - echo "Unknown module command: $cmd" - echo "Use 'help' to see available commands." + print_error "Unknown module command: $cmd. Use 'help' to see available commands." return 1 ;; esac @@ -188,20 +230,72 @@ function inst_parse_module_spec() { # Parse the new syntax: repo[:dirname][@branch[:commit]] - # First, extract custom directory name if present (format: repo:dirname@branch) + # First, check if this is a URL (contains :// or starts with git@) + local is_url=0 + if [[ "$spec" =~ :// ]] || [[ "$spec" =~ ^git@ ]]; then + is_url=1 + fi + + # Parse directory and branch differently for URLs vs simple names local repo_with_branch="$spec" - if [[ "$spec" =~ ^([^@:]+):([^@:]+)(@.*)?$ ]]; then - repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" - dirname="${BASH_REMATCH[2]}" + if [[ $is_url -eq 1 ]]; then + # For URLs, look for :dirname pattern, but be careful about ports + # Strategy: only match :dirname if it's clearly after the repository path + + # Look for :dirname patterns at the end, but not if it looks like a port + if [[ "$spec" =~ ^(.*\.git):([^@/:]+)(@.*)?$ ]]; then + # Repo ending with .git:dirname + repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" + dirname="${BASH_REMATCH[2]}" + elif [[ "$spec" =~ ^(.*://[^/]+/[^:]*[^0-9]):([^@/:]+)(@.*)?$ ]]; then + # URL with path ending in non-digit:dirname (avoid matching ports) + repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" + dirname="${BASH_REMATCH[2]}" + fi + # If no custom dirname found, repo_with_branch remains the original spec + else + # For simple names, use the original logic + if [[ "$spec" =~ ^([^@:]+):([^@:]+)(@.*)?$ ]]; then + repo_with_branch="${BASH_REMATCH[1]}${BASH_REMATCH[3]}" + dirname="${BASH_REMATCH[2]}" + fi fi # Now parse branch and commit from the repo part - if [[ "$repo_with_branch" =~ ^([^@]+)@([^:]+)(:(.+))?$ ]]; then - repo_part="${BASH_REMATCH[1]}" - branch="${BASH_REMATCH[2]}" - commit="${BASH_REMATCH[4]:-}" + # Be careful not to confuse URL @ with branch @ + if [[ "$repo_with_branch" =~ :// ]]; then + # For URLs, look for @ after the authority part + if [[ "$repo_with_branch" =~ ^[^/]*//[^/]+/.*@([^:]+)(:(.+))?$ ]]; then + # @ found in path part - treat as branch + repo_part="${repo_with_branch%@*}" + branch="${BASH_REMATCH[1]}" + commit="${BASH_REMATCH[3]:-}" + elif [[ "$repo_with_branch" =~ ^([^@]*@[^/]+/.*)@([^:]+)(:(.+))?$ ]]; then + # @ found after URL authority @ - treat as branch + repo_part="${BASH_REMATCH[1]}" + branch="${BASH_REMATCH[2]}" + commit="${BASH_REMATCH[4]:-}" + else + repo_part="$repo_with_branch" + fi + elif [[ "$repo_with_branch" =~ ^git@ ]]; then + # Git SSH format - look for @ after the initial git@host: part + if [[ "$repo_with_branch" =~ ^git@[^:]+:.*@([^:]+)(:(.+))?$ ]]; then + repo_part="${repo_with_branch%@*}" + branch="${BASH_REMATCH[1]}" + commit="${BASH_REMATCH[3]:-}" + else + repo_part="$repo_with_branch" + fi else - repo_part="$repo_with_branch" + # Non-URL format - use original logic + if [[ "$repo_with_branch" =~ ^([^@]+)@([^:]+)(:(.+))?$ ]]; then + repo_part="${BASH_REMATCH[1]}" + branch="${BASH_REMATCH[2]}" + commit="${BASH_REMATCH[4]:-}" + else + repo_part="$repo_with_branch" + fi fi # Normalize repo reference and extract owner/name. @@ -210,10 +304,39 @@ function inst_parse_module_spec() { # If repo_ref is a URL, extract owner/name from path when possible if [[ "$repo_ref" =~ :// ]] || [[ "$repo_ref" =~ ^git@ ]]; then - # Extract owner/name (last two path components) - owner_repo=$(echo "$repo_ref" | sed -E 's#(git@[^:]+:|https?://[^/]+/|ssh://[^/]+/)?(.*?)(\.git)?$#\2#') - owner="$(echo "$owner_repo" | awk -F'/' '{print $(NF-1)}')" - name="$(echo "$owner_repo" | awk -F'/' '{print $NF}' | sed -E 's/\.git$//')" + # Handle various URL formats + local path_part="" + if [[ "$repo_ref" =~ ^https?://[^/]+:?[0-9]*/(.+)$ ]]; then + # HTTPS URL (with or without port) + path_part="${BASH_REMATCH[1]}" + elif [[ "$repo_ref" =~ ^ssh://.*@[^/]+:?[0-9]*/(.+)$ ]]; then + # SSH URL with user@host:port/path format + path_part="${BASH_REMATCH[1]}" + elif [[ "$repo_ref" =~ ^ssh://[^@/]+:?[0-9]*/(.+)$ ]]; then + # SSH URL with host:port/path format (no user@) + path_part="${BASH_REMATCH[1]}" + elif [[ "$repo_ref" =~ ^git@[^:]+:(.+)$ ]]; then + # Git SSH format (git@host:path) + path_part="${BASH_REMATCH[1]}" + fi + + # Extract owner/name from path + if [[ -n "$path_part" ]]; then + # Remove .git suffix and any :dirname suffix + path_part="${path_part%.git}" + path_part="${path_part%:*}" + + if [[ "$path_part" == *"/"* ]]; then + owner="$(echo "$path_part" | awk -F'/' '{print $(NF-1)}')" + name="$(echo "$path_part" | awk -F'/' '{print $NF}')" + else + owner="unknown" + name="$path_part" + fi + else + owner="unknown" + name="unknown" + fi else owner_repo="$repo_ref" if [[ "$owner_repo" == *"/"* ]]; then @@ -266,21 +389,28 @@ function inst_extract_owner_name { base_ref="${repo_ref%%:*}" fi - if [[ "$base_ref" =~ ^https?://github\.com/([^/]+)/([^/]+)(\.git)?(/.*)?$ ]]; then - # HTTPS URL format - check this first before owner/name pattern + # Handle various URL formats with possible ports + if [[ "$base_ref" =~ ^https?://[^/]+:?[0-9]*/([^/]+)/([^/?]+) ]]; then + # HTTPS URL format (with or without port) - matches github.com, gitlab.com, custom hosts + local owner="${BASH_REMATCH[1]}" local name="${BASH_REMATCH[2]}" - name="${name%.git}" # Remove .git suffix if present - echo "${BASH_REMATCH[1]}/$name" - elif [[ "$base_ref" =~ ^https?://gitlab\.com/([^/]+)/([^/]+)(\.git)?(/.*)?$ ]]; then - # GitLab URL format + name="${name%:*}" # Remove any :dirname suffix first + name="${name%.git}" # Then remove .git suffix if present + echo "$owner/$name" + elif [[ "$base_ref" =~ ^ssh://[^/]+:?[0-9]*/([^/]+)/([^/?]+) ]]; then + # SSH URL format (with or without port) + local owner="${BASH_REMATCH[1]}" local name="${BASH_REMATCH[2]}" - name="${name%.git}" # Remove .git suffix if present - echo "${BASH_REMATCH[1]}/$name" - elif [[ "$base_ref" =~ ^git@github\.com:([^/]+)/([^/]+)(\.git)?$ ]]; then - # SSH URL format + name="${name%:*}" # Remove any :dirname suffix first + name="${name%.git}" # Then remove .git suffix if present + echo "$owner/$name" + elif [[ "$base_ref" =~ ^git@[^:]+:([^/]+)/([^/?]+) ]]; then + # Git SSH format (git@host:owner/repo) + local owner="${BASH_REMATCH[1]}" local name="${BASH_REMATCH[2]}" - name="${name%.git}" # Remove .git suffix if present - echo "${BASH_REMATCH[1]}/$name" + name="${name%:*}" # Remove any :dirname suffix first + name="${name%.git}" # Then remove .git suffix if present + echo "$owner/$name" elif [[ "$base_ref" =~ ^[^/]+/[^/]+$ ]]; then # Format: owner/name (check after URL patterns) echo "$base_ref" @@ -330,6 +460,40 @@ function inst_mod_list_read() { done < "$file" } +# Check whether a module spec matches the exclusion list. +# - Reads space/newline separated items from env var MODULES_EXCLUDE_LIST +# - Supports cross-format matching via inst_extract_owner_name +# Returns 0 if excluded, 1 otherwise. +function inst_mod_is_excluded() { + local spec="$1" + local target_owner_name + target_owner_name=$(inst_extract_owner_name "$spec") + + # No exclusions configured + if [[ -z "${MODULES_EXCLUDE_LIST:-}" ]]; then + return 1 + fi + + # Split on default IFS (space, tab, newline) + local items=() + # Use mapfile to split MODULES_EXCLUDE_LIST on newlines; fallback to space if no newlines + if [[ "${MODULES_EXCLUDE_LIST}" == *$'\n'* ]]; then + mapfile -t items <<< "${MODULES_EXCLUDE_LIST}" + else + read -r -a items <<< "${MODULES_EXCLUDE_LIST}" + fi + + local it it_owner + for it in "${items[@]}"; do + [[ -z "$it" ]] && continue + it_owner=$(inst_extract_owner_name "$it") + if [[ "$it_owner" == "$target_owner_name" ]]; then + return 0 + fi + done + return 1 +} + # Add or update an entry in the list: repo_ref branch commit # Removes any existing entries with the same owner/name to avoid duplicates function inst_mod_list_upsert() { @@ -448,12 +612,12 @@ function inst_check_module_conflict { local repo_ref="$2" if [ -d "$J_PATH_MODULES/$dirname" ]; then - echo "Error: Directory '$dirname' already exists." - echo "Possible solutions:" - echo " 1. Use a different directory name: $repo_ref:my-custom-name" - echo " 2. Remove the existing directory first" - echo " 3. Use the update command if this is the same module" - return 1 + print_error "Error: Directory '$dirname' already exists." + print_warn "Possible solutions:" + echo " 1. Use a different directory name: $repo_ref:my-custom-name" + echo " 2. Remove the existing directory first" + echo " 3. Use the update command if this is the same module" + return 1 fi return 0 } @@ -511,7 +675,7 @@ function inst_module_search { local CATALOG_URL="https://www.azerothcore.org/data/catalogue.json" - echo "Searching ${terms[*]}..." + print_header "Searching ${terms[*]}..." echo "" # Build candidate list from catalogue (full_name = owner/repo) @@ -567,9 +731,9 @@ function inst_module_search { read v b < <(inst_getVersionBranch "https://raw.githubusercontent.com/${mod_full}/master/acore-module.json") if [[ "$b" != "none" ]]; then - echo "-> $mod (tested with AC version: $v)" + printf "%b -> %b (tested with AC version: %s)%b\n" "" "${C_GREEN}${mod}${C_RESET}" "$v" "" else - echo "-> $mod (NOTE: The module latest tested AC revision is Unknown)" + printf "%b -> %b %b(NOTE: The module latest tested AC revision is Unknown)%b\n" "" "${C_GREEN}${mod}${C_RESET}" "${C_YELLOW}" "${C_RESET}" fi done @@ -590,7 +754,7 @@ function inst_module_install { local modules=("$@") - echo "Installing modules: ${modules[*]}" + print_header "Installing modules: ${modules[*]}" if $use_all; then # Install all modules from the list (respecting recorded branch and commit). @@ -602,14 +766,18 @@ function inst_module_install { local dup_error=0 while read -r repo_ref branch commit; do [ -z "$repo_ref" ] && continue + # Skip excluded modules when checking duplicates + if inst_mod_is_excluded "$repo_ref"; then + continue + fi parsed_output=$(inst_parse_module_spec "$repo_ref") IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" # dirname defaults to repo name; flat install path uses dirname only if [[ -n "${_seen[$dirname]:-}" ]]; then - echo "Error: duplicate module target directory '$dirname' detected in modules.list:" + print_error "Error: duplicate module target directory '$dirname' detected in modules.list:" echo " - ${_first[$dirname]}" echo " - ${repo_ref}" - echo "Use a custom folder name to disambiguate, e.g.: ${repo_ref}:$dirname-alt" + print_warn "Use a custom folder name to disambiguate, e.g.: ${repo_ref}:$dirname-alt" dup_error=1 else _seen[$dirname]=1 @@ -623,11 +791,16 @@ function inst_module_install { # Second pass: install in flat modules directory (no owner subfolders) while read -r repo_ref branch commit; do [ -z "$repo_ref" ] && continue + # Skip excluded entries during installation + if inst_mod_is_excluded "$repo_ref"; then + print_warn "[$repo_ref] Excluded by MODULES_EXCLUDE_LIST (skipping)." + continue + fi parsed_output=$(inst_parse_module_spec "$repo_ref") IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" if [ -d "$J_PATH_MODULES/$dirname" ]; then - echo "[$repo_ref] Already installed (skipping)." + print_skip "[$repo_ref] Already installed (skipping)." continue fi @@ -642,9 +815,9 @@ function inst_module_install { local curCommit curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") inst_mod_list_upsert "$repo_ref" "$branch" "$curCommit" - echo "[$repo_ref] Installed." + print_success "[$repo_ref] Installed." else - echo "[$repo_ref] Install failed." + print_error "[$repo_ref] Install failed." exit 1; fi done < <(inst_mod_list_read) @@ -663,7 +836,7 @@ function inst_module_install { # Check if module is already installed (by owner/name matching) existing_repo_ref=$(inst_mod_is_installed "$spec" || true) if [ -n "$existing_repo_ref" ]; then - echo "[$spec] Already installed as [$existing_repo_ref] (skipping)." + print_skip "[$spec] Already installed as [$existing_repo_ref] (skipping)." continue fi @@ -689,14 +862,14 @@ function inst_module_install { fi if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then def="$(inst_get_default_branch "$repo_ref")" - echo "Warning: $repo_ref has no compatible acore-module.json; installing from branch '$def' (latest commit)." + print_warn "Warning: $repo_ref has no compatible acore-module.json; installing from branch '$def' (latest commit)." b="$def" fi fi # Use flat directory structure with custom directory name if [ -d "$J_PATH_MODULES/$dirname" ]; then - echo "[$repo_ref] Already installed (skipping)." + print_skip "[$repo_ref] Already installed (skipping)." curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") inst_mod_list_upsert "$repo_ref" "$b" "$curCommit" continue @@ -709,14 +882,14 @@ function inst_module_install { if git -C "$J_PATH_MODULES/$dirname" rev-parse --verify "$override_commit" >/dev/null 2>&1; then git -C "$J_PATH_MODULES/$dirname" checkout --quiet "$override_commit" else - echo "[$repo_ref] Warning: provided commit '$override_commit' not found; staying on branch '$b' HEAD." + print_warn "[$repo_ref] provided commit '$override_commit' not found; staying on branch '$b' HEAD." fi fi curCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") inst_mod_list_upsert "$repo_ref" "$b" "$curCommit" - echo "[$repo_ref] Installed in '$dirname'. Please re-run compiling and db assembly." + print_success "[$repo_ref] Installed in '$dirname'. Please re-run compiling and db assembly." else - echo "[$repo_ref] Install failed or module not found" + print_error "[$repo_ref] Install failed or module not found" exit 1; fi done @@ -748,21 +921,26 @@ function inst_module_update { local line repo_ref branch commit newCommit owner modname url dirname while read -r repo_ref branch commit; do [ -z "$repo_ref" ] && continue + # Skip excluded modules during update --all + if inst_mod_is_excluded "$repo_ref"; then + print_warn "[$repo_ref] Excluded by MODULES_EXCLUDE_LIST (skipping)." + continue + fi parsed_output=$(inst_parse_module_spec "$repo_ref") IFS=' ' read -r _ owner modname _ _ url dirname <<< "$parsed_output" dirname="${dirname:-$modname}" if [ ! -d "$J_PATH_MODULES/$dirname/" ]; then - echo "[$repo_ref] Not installed locally, skipping." + print_skip "[$repo_ref] Not installed locally, skipping." continue fi if Joiner:upd_repo "$url" "$dirname" "$branch" ""; then newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") inst_mod_list_upsert "$repo_ref" "$branch" "$newCommit" - echo "[$repo_ref] Updated to latest commit on '$branch'." + print_success "[$repo_ref] Updated to latest commit on '$branch'." else - echo "[$repo_ref] Cannot update" + print_error "[$repo_ref] Cannot update" fi done < <(inst_mod_list_read) else @@ -793,11 +971,11 @@ function inst_module_update { fi if [[ "$v" == "none" || "$v" == "not-defined" || "$b" == "none" ]]; then if branch=$(git -C "$J_PATH_MODULES/$dirname" rev-parse --abbrev-ref HEAD 2>/dev/null); then - echo "Warning: $repo_ref has no compatible acore-module.json; updating current branch '$branch'." + print_warn "Warning: $repo_ref has no compatible acore-module.json; updating current branch '$branch'." b="$branch" else def="$(inst_get_default_branch "$repo_ref")" - echo "Warning: $repo_ref has no compatible acore-module.json and no git branch detected; updating default branch '$def'." + print_warn "Warning: $repo_ref has no compatible acore-module.json and no git branch detected; updating default branch '$def'." b="$def" fi fi @@ -806,12 +984,12 @@ function inst_module_update { if Joiner:upd_repo "$url" "$dirname" "$b" ""; then newCommit=$(git -C "$J_PATH_MODULES/$dirname" rev-parse HEAD 2>/dev/null || echo "") inst_mod_list_upsert "$repo_ref" "$b" "$newCommit" - echo "[$repo_ref] Done, please re-run compiling and db assembly" + print_success "[$repo_ref] Done, please re-run compiling and db assembly" else - echo "[$repo_ref] Cannot update" + print_error "[$repo_ref] Cannot update" fi else - echo "[$repo_ref] Cannot update! Path doesn't exist ($J_PATH_MODULES/$dirname/)" + print_error "[$repo_ref] Cannot update! Path doesn't exist ($J_PATH_MODULES/$dirname/)" fi done fi @@ -840,9 +1018,9 @@ function inst_module_remove { dirname="${dirname:-$modname}" if Joiner:remove "$dirname" ""; then inst_mod_list_remove "$repo_ref" - echo "[$repo_ref] Done, please re-run compiling" + print_success "[$repo_ref] Done, please re-run compiling" else - echo "[$repo_ref] Cannot remove" + print_error "[$repo_ref] Cannot remove" fi done diff --git a/apps/installer/test/test_module_commands.bats b/apps/installer/test/test_module_commands.bats index 30ee94e81..1223a80a6 100755 --- a/apps/installer/test/test_module_commands.bats +++ b/apps/installer/test/test_module_commands.bats @@ -146,6 +146,27 @@ teardown() { [ "$output" = "myorg/mymodule" ] } +@test "inst_extract_owner_name should handle URLs with ports correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test HTTPS URL with port + run inst_extract_owner_name "https://example.com:8080/user/repo.git" + [ "$output" = "user/repo" ] + + # Test SSH URL with port + run inst_extract_owner_name "ssh://git@example.com:2222/owner/module" + [ "$output" = "owner/module" ] + + # Test URL with port and custom directory (should ignore the directory part) + run inst_extract_owner_name "https://gitlab.internal:9443/team/project.git:custom-dir" + [ "$output" = "team/project" ] + + # Test complex URL with port (should extract owner/name correctly) + run inst_extract_owner_name "https://git.company.com:8443/department/awesome-module.git" + [ "$output" = "department/awesome-module" ] +} + @test "duplicate module entries should be prevented across different formats" { cd "$TEST_DIR" source "$TEST_DIR/apps/installer/includes/includes.sh" @@ -189,6 +210,30 @@ teardown() { [ "$status" -ne 0 ] } +@test "module installed via URL with port should be recognized correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Install via URL with port + inst_mod_list_upsert "https://gitlab.internal:9443/myorg/my-module.git" "master" "abc123" + + # Should be detected as installed using normalized owner/name + run inst_mod_is_installed "myorg/my-module" + [ "$status" -eq 0 ] + + # Should be detected when checking with different URL format + run inst_mod_is_installed "ssh://git@gitlab.internal:9443/myorg/my-module" + [ "$status" -eq 0 ] + + # Should be detected when checking with custom directory syntax + run inst_mod_is_installed "myorg/my-module:custom-dir" + [ "$status" -eq 0 ] + + # Different module should not be detected + run inst_mod_is_installed "myorg/different-module" + [ "$status" -ne 0 ] +} + @test "cross-format module removal should work" { cd "$TEST_DIR" source "$TEST_DIR/apps/installer/includes/includes.sh" @@ -355,3 +400,356 @@ EOF [ "$owner_name_format" = "azerothcore/mod-transmog" ] [ "$simple_format" = "azerothcore/mod-transmog" ] } + +# Tests for module exclusion functionality + +@test "module exclusion should work with MODULES_EXCLUDE_LIST" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test exclusion with simple name + export MODULES_EXCLUDE_LIST="mod-test-module" + run inst_mod_is_excluded "mod-test-module" + [ "$status" -eq 0 ] + + # Test exclusion with owner/name format + export MODULES_EXCLUDE_LIST="azerothcore/mod-test" + run inst_mod_is_excluded "mod-test" + [ "$status" -eq 0 ] + + # Test exclusion with space-separated list + export MODULES_EXCLUDE_LIST="mod-one mod-two mod-three" + run inst_mod_is_excluded "mod-two" + [ "$status" -eq 0 ] + + # Test exclusion with newline-separated list + export MODULES_EXCLUDE_LIST=" +mod-alpha +mod-beta +mod-gamma +" + run inst_mod_is_excluded "mod-beta" + [ "$status" -eq 0 ] + + # Test exclusion with URL format + export MODULES_EXCLUDE_LIST="https://github.com/azerothcore/mod-transmog.git" + run inst_mod_is_excluded "mod-transmog" + [ "$status" -eq 0 ] + + # Test non-excluded module + export MODULES_EXCLUDE_LIST="mod-other" + run inst_mod_is_excluded "mod-transmog" + [ "$status" -eq 1 ] + + # Test empty exclusion list + unset MODULES_EXCLUDE_LIST + run inst_mod_is_excluded "mod-transmog" + [ "$status" -eq 1 ] +} + +@test "install --all should skip excluded modules" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Setup modules list with excluded module + mkdir -p "$TEST_DIR/conf" + cat > "$TEST_DIR/conf/modules.list" << 'EOF' +azerothcore/mod-transmog master abc123 +azerothcore/mod-excluded master def456 +EOF + + # Set exclusion list + export MODULES_EXCLUDE_LIST="mod-excluded" + + # Mock the install process to capture output + run bash -c "source '$TEST_DIR/apps/installer/includes/includes.sh' && inst_module_install --all 2>&1" + + # Should show that excluded module was skipped + [[ "$output" == *"azerothcore/mod-excluded"* && "$output" == *"Excluded by MODULES_EXCLUDE_LIST"* && "$output" == *"skipping"* ]] +} + +@test "exclusion should work with multiple formats in same list" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test multiple exclusion formats + export MODULES_EXCLUDE_LIST="mod-test https://github.com/azerothcore/mod-transmog.git custom/mod-other" + + run inst_mod_is_excluded "mod-test" + [ "$status" -eq 0 ] + + run inst_mod_is_excluded "azerothcore/mod-transmog" + [ "$status" -eq 0 ] + + run inst_mod_is_excluded "custom/mod-other" + [ "$status" -eq 0 ] + + run inst_mod_is_excluded "mod-allowed" + [ "$status" -eq 1 ] +} + +# Tests for color support functionality + +@test "color functions should work correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test that print functions exist and work + run print_info "test message" + [ "$status" -eq 0 ] + + run print_warn "test warning" + [ "$status" -eq 0 ] + + run print_error "test error" + [ "$status" -eq 0 ] + + run print_success "test success" + [ "$status" -eq 0 ] + + run print_skip "test skip" + [ "$status" -eq 0 ] + + run print_header "test header" + [ "$status" -eq 0 ] +} + +@test "color support should respect NO_COLOR environment variable" { + cd "$TEST_DIR" + + # Test with NO_COLOR set + export NO_COLOR=1 + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Colors should be empty when NO_COLOR is set + [ -z "$C_RED" ] + [ -z "$C_GREEN" ] + [ -z "$C_RESET" ] +} + +# Tests for interactive menu system + +@test "module help should display comprehensive help" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + run inst_module_help + [ "$status" -eq 0 ] + + # Should contain key sections + [[ "$output" =~ "Module Manager Help" ]] + [[ "$output" =~ "Usage:" ]] + [[ "$output" =~ "Module Specification Syntax:" ]] + [[ "$output" =~ "Examples:" ]] +} + +@test "module list should show installed modules correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Setup modules list + mkdir -p "$TEST_DIR/conf" + cat > "$TEST_DIR/conf/modules.list" << 'EOF' +azerothcore/mod-transmog master abc123 +custom/mod-test develop def456 +EOF + + run inst_module_list + [ "$status" -eq 0 ] + + # Should show both modules + [[ "$output" =~ "mod-transmog" ]] + [[ "$output" =~ "custom/mod-test" ]] + [[ "$output" =~ "master" ]] + [[ "$output" =~ "develop" ]] +} + +@test "module list should handle empty list gracefully" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Ensure empty modules list + mkdir -p "$TEST_DIR/conf" + touch "$TEST_DIR/conf/modules.list" + + run inst_module_list + [ "$status" -eq 0 ] + [[ "$output" =~ "No modules installed" ]] +} + +# Tests for advanced parsing edge cases + +@test "parsing should handle complex URL formats" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test GitLab URL with custom directory and branch + run inst_parse_module_spec "https://gitlab.com/myorg/mymodule.git:custom-dir@develop:abc123" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://gitlab.com/myorg/mymodule.git" ] + [ "$owner" = "myorg" ] + [ "$name" = "mymodule" ] + [ "$branch" = "develop" ] + [ "$commit" = "abc123" ] + [ "$dirname" = "custom-dir" ] +} + +@test "parsing should handle URLs with ports correctly (fix for port/dirname confusion)" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test HTTPS URL with port - should NOT treat port as dirname + run inst_parse_module_spec "https://example.com:8080/user/repo.git" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://example.com:8080/user/repo.git" ] + [ "$owner" = "user" ] + [ "$name" = "repo" ] + [ "$branch" = "-" ] + [ "$commit" = "-" ] + [ "$url" = "https://example.com:8080/user/repo.git" ] + [ "$dirname" = "repo" ] # Should default to repo name, NOT port number +} + +@test "parsing should handle URLs with ports and custom directory correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test URL with port AND custom directory - should parse custom directory correctly + run inst_parse_module_spec "https://example.com:8080/user/repo.git:custom-dir" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://example.com:8080/user/repo.git" ] + [ "$owner" = "user" ] + [ "$name" = "repo" ] + [ "$branch" = "-" ] + [ "$commit" = "-" ] + [ "$url" = "https://example.com:8080/user/repo.git" ] + [ "$dirname" = "custom-dir" ] # Should be custom-dir, not port number +} + +@test "parsing should handle SSH URLs with ports correctly" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test SSH URL with port + run inst_parse_module_spec "ssh://git@example.com:2222/user/repo" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "ssh://git@example.com:2222/user/repo" ] + [ "$owner" = "user" ] + [ "$name" = "repo" ] + [ "$dirname" = "repo" ] # Should be repo name, not port number +} + +@test "parsing should handle SSH URLs with ports and custom directory" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test SSH URL with port and custom directory + run inst_parse_module_spec "ssh://git@example.com:2222/user/repo:my-custom-dir@develop" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "ssh://git@example.com:2222/user/repo" ] + [ "$owner" = "user" ] + [ "$name" = "repo" ] + [ "$branch" = "develop" ] + [ "$dirname" = "my-custom-dir" ] +} + +@test "parsing should handle complex URLs with ports, custom dirs, and branches" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Test comprehensive URL with port, custom directory, branch, and commit + run inst_parse_module_spec "https://gitlab.example.com:9443/myorg/myrepo.git:custom-name@feature-branch:abc123def" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://gitlab.example.com:9443/myorg/myrepo.git" ] + [ "$owner" = "myorg" ] + [ "$name" = "myrepo" ] + [ "$branch" = "feature-branch" ] + [ "$commit" = "abc123def" ] + [ "$url" = "https://gitlab.example.com:9443/myorg/myrepo.git" ] + [ "$dirname" = "custom-name" ] +} + +@test "URL port parsing regression test - ensure ports are not confused with directory names" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # These are the problematic cases that the fix addresses + local test_cases=( + "https://example.com:8080/repo.git" + "https://gitlab.internal:9443/group/project.git" + "ssh://git@server.com:2222/owner/repo" + "https://git.company.com:8443/team/module.git" + ) + + for spec in "${test_cases[@]}"; do + run inst_parse_module_spec "$spec" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + + # Critical: dirname should NEVER be a port number + [[ ! "$dirname" =~ ^[0-9]+$ ]] || { + echo "FAIL: Port number '$dirname' incorrectly parsed as directory name for spec: $spec" + return 1 + } + + # dirname should be the repository name by default + local expected_name + if [[ "$spec" =~ /([^/]+)(\.git)?$ ]]; then + expected_name="${BASH_REMATCH[1]}" + expected_name="${expected_name%.git}" + fi + [ "$dirname" = "$expected_name" ] || { + echo "FAIL: Expected dirname '$expected_name' but got '$dirname' for spec: $spec" + return 1 + } + done +} + +@test "parsing should handle URL with custom directory but no branch" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + run inst_parse_module_spec "https://github.com/owner/repo.git:my-dir" + [ "$status" -eq 0 ] + IFS=' ' read -r repo_ref owner name branch commit url dirname <<< "$output" + [ "$repo_ref" = "https://github.com/owner/repo.git" ] + [ "$dirname" = "my-dir" ] + [ "$branch" = "-" ] +} + +@test "modules list should maintain alphabetical order" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + # Add modules in random order + inst_mod_list_upsert "zeta/mod-z" "master" "abc" + inst_mod_list_upsert "alpha/mod-a" "master" "def" + inst_mod_list_upsert "beta/mod-b" "master" "ghi" + + # Read the list and verify alphabetical order + local entries=() + while read -r repo_ref branch commit; do + [[ -z "$repo_ref" ]] && continue + entries+=("$repo_ref") + done < <(inst_mod_list_read) + + # Should be in alphabetical order by owner/name + [ "${entries[0]}" = "alpha/mod-a" ] + [ "${entries[1]}" = "beta/mod-b" ] + [ "${entries[2]}" = "zeta/mod-z" ] +} + +@test "module dispatcher should handle unknown commands gracefully" { + cd "$TEST_DIR" + source "$TEST_DIR/apps/installer/includes/includes.sh" + + run inst_module "unknown-command" + [ "$status" -eq 1 ] + [[ "$output" =~ "Unknown module command" ]] +} \ No newline at end of file diff --git a/conf/dist/config.sh b/conf/dist/config.sh index 2e105fd44..1f956ca70 100644 --- a/conf/dist/config.sh +++ b/conf/dist/config.sh @@ -162,4 +162,14 @@ export CPUPROFILESIGNAL=${CPUPROFILESIGNAL:-12} # Lines starting with '#' and empty lines are ignored. export MODULES_LIST_FILE=${MODULES_LIST_FILE:-"$AC_PATH_ROOT/conf/modules.list"} +# Space/newline separated list of modules to exclude when using +# 'module install --all' and 'module update --all'. Items can be specified +# as simple names (e.g., mod-transmog), owner/name, or full URLs. +# Example: +# export MODULES_EXCLUDE_LIST="azerothcore/mod-transmog azerothcore/mod-autobalance" +export MODULES_EXCLUDE_LIST="" + +NO_COLOR=${NO_COLOR:-} +FORCE_COLOR=${FORCE_COLOR:-} + From 119af326494995eba42bbb47b79bca08ffb614b3 Mon Sep 17 00:00:00 2001 From: Yehonal Date: Sat, 6 Sep 2025 15:10:42 +0200 Subject: [PATCH 120/155] feat(Scripts): Add health and console commands to service manager and corresponding tests (#22814) --- .github/workflows/dashboard-ci.yml | 28 ++ apps/startup-scripts/README.md | 26 +- apps/startup-scripts/src/service-manager.sh | 302 +++++++++++++++++- .../test/test_startup_scripts.bats | 124 +++++++ 4 files changed, 477 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dashboard-ci.yml b/.github/workflows/dashboard-ci.yml index 8b22541fb..5901d1944 100644 --- a/.github/workflows/dashboard-ci.yml +++ b/.github/workflows/dashboard-ci.yml @@ -95,6 +95,7 @@ jobs: run: | # This runs: install-deps, compile, database setup, client-data download ./acore.sh init + sudo npm install -g pm2 timeout-minutes: 120 - name: Test module removal @@ -115,3 +116,30 @@ jobs: cd env/dist/bin timeout 5m ./worldserver -dry-run continue-on-error: false + + + - name: Test worldserver with startup scripts + run: | + ./acore.sh sm create world worldserver --bin-path ./env/dist/bin --provider pm2 + ./acore.sh sm show-config worldserver + ./acore.sh sm start worldserver + ./acore.sh sm wait-uptime worldserver 10 300 + ./acore.sh sm send worldserver "account create tester password 3" + ./acore.sh sm send worldserver "account set gm tester 3" + ./acore.sh sm send worldserver "account set addon tester 1" + ./acore.sh sm wait-uptime worldserver 10 300 + ./acore.sh sm stop worldserver + ./acore.sh sm delete worldserver + timeout-minutes: 30 + continue-on-error: false + + - name: Test authserver with startup scripts + run: | + ./acore.sh sm create auth authserver --bin-path ./env/dist/bin --provider pm2 + ./acore.sh sm show-config authserver + ./acore.sh sm start authserver + ./acore.sh sm wait-uptime authserver 10 300 + ./acore.sh sm stop authserver + ./acore.sh sm delete authserver + timeout-minutes: 30 + continue-on-error: false diff --git a/apps/startup-scripts/README.md b/apps/startup-scripts/README.md index 176ec3ed6..0c7cb0940 100644 --- a/apps/startup-scripts/README.md +++ b/apps/startup-scripts/README.md @@ -305,6 +305,31 @@ Services support two restart policies: ./service-manager.sh delete auth ``` +#### Health and Console Commands + +Use these commands to programmatically check service health and interact with the console (used by CI workflows): + +```bash +# Check if service is currently running (exit 0 if running) +./service-manager.sh is-running world + +# Print current uptime in seconds (fails if not running) +./service-manager.sh uptime-seconds world + +# Wait until uptime >= 10s (optional timeout 240s) +./service-manager.sh wait-uptime world 10 240 + +# Send a console command (uses pm2 send or tmux/screen) +./service-manager.sh send world "server info" + +# Show provider, configs and run-engine settings +./service-manager.sh show-config world +``` + +Notes: +- For `send`, PM2 provider uses `pm2 send` with the process ID; systemd provider requires a session manager (tmux/screen). If no attachable session is configured, the command fails. +- `wait-uptime` fails with a non-zero exit code if the service does not reach the requested uptime within the timeout window. + #### Service Configuration ```bash # Update service settings @@ -624,4 +649,3 @@ sudo npm install -g pm2 ``` - diff --git a/apps/startup-scripts/src/service-manager.sh b/apps/startup-scripts/src/service-manager.sh index 34dc4c4d5..ccc4e8e35 100755 --- a/apps/startup-scripts/src/service-manager.sh +++ b/apps/startup-scripts/src/service-manager.sh @@ -279,6 +279,11 @@ function print_help() { echo " $base_name start|stop|restart|status " echo " $base_name logs [--follow]" echo " $base_name attach " + echo " $base_name is-running # exit 0 if running, 1 otherwise" + echo " $base_name uptime-seconds # print uptime in seconds (fails if not running)" + echo " $base_name wait-uptime [t] # wait until uptime >= seconds (timeout t, default 120)" + echo " $base_name send # send console command to service" + echo " $base_name show-config # print current service + run-engine config" echo " $base_name edit-config " echo "" echo "Providers:" @@ -735,7 +740,7 @@ EOF systemctl --user enable "$service_name.service" fi - echo -e "${GREEN}Systemd service '$service_name' created successfully${NC}" + echo -e "${GREEN}Systemd service '$service_name' created successfully with session manager '$session_manager'${NC}" # Add to registry add_service_to_registry "$service_name" "systemd" "service" "$command" "" "$systemd_type" "$restart_policy" "$session_manager" "$gdb_enabled" "" "$server_config" @@ -1473,6 +1478,253 @@ function attach_to_service() { fi } +######################################### +# Runtime helpers: status / send / show # +######################################### + +function service_is_running() { + local service_name="$1" + + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" >&2 + return 1 + fi + + local provider=$(echo "$service_info" | jq -r '.provider') + + if [ "$provider" = "pm2" ]; then + # pm2 jlist -> JSON array with .name and .pm2_env.status + if pm2 jlist | jq -e ".[] | select(.name==\"$service_name\" and .pm2_env.status==\"online\")" >/dev/null; then + return 0 + else + return 1 + fi + elif [ "$provider" = "systemd" ]; then + # Check user service first, then system + if systemctl --user is-active --quiet "$service_name.service" 2>/dev/null; then + return 0 + elif systemctl is-active --quiet "$service_name.service" 2>/dev/null; then + return 0 + else + return 1 + fi + else + return 1 + fi +} + +function service_send_command() { + local service_name="$1"; shift || true + local cmd_str="$*" + if [ -z "$service_name" ] || [ -z "$cmd_str" ]; then + echo -e "${RED}Error: send requires and ${NC}" >&2 + return 1 + fi + + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" >&2 + return 1 + fi + + local provider=$(echo "$service_info" | jq -r '.provider') + local config_file="$CONFIG_DIR/$service_name.conf" + + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" >&2 + return 1 + fi + + # Load run-engine config path + # shellcheck source=/dev/null + source "$config_file" + if [ -z "${RUN_ENGINE_CONFIG_FILE:-}" ] || [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then + echo -e "${RED}Error: Run-engine configuration file not found for $service_name${NC}" >&2 + return 1 + fi + + # shellcheck source=/dev/null + if ! source "$RUN_ENGINE_CONFIG_FILE"; then + echo -e "${RED}Error: Failed to source run-engine configuration file: $RUN_ENGINE_CONFIG_FILE${NC}" >&2 + return 1 + fi + + local session_manager="${SESSION_MANAGER:-auto}" + local session_name="${SESSION_NAME:-$service_name}" + + if [ "$provider" = "pm2" ]; then + # Use pm2 send (requires pm2 >= 5) + local pm2_id_json + pm2_id_json=$(pm2 id "$service_name" 2>/dev/null || true) + local numeric_id + numeric_id=$(echo "$pm2_id_json" | jq -r '.[0] // empty') + if [ -z "$numeric_id" ]; then + echo -e "${RED}Error: PM2 process '$service_name' not found${NC}" >&2 + return 1 + fi + echo -e "${YELLOW}Sending to PM2 process $service_name (ID: $numeric_id): $cmd_str${NC}" + pm2 send "$numeric_id" "$cmd_str" ENTER + return $? + fi + + # systemd provider: need a session manager to interact with the console + case "$session_manager" in + tmux|auto) + if command -v tmux >/dev/null 2>&1 && tmux has-session -t "$session_name" 2>/dev/null; then + echo -e "${YELLOW}Sending to tmux session $session_name: $cmd_str${NC}" + tmux send-keys -t "$session_name" "$cmd_str" C-m + return $? + elif [ "$session_manager" = "tmux" ]; then + echo -e "${RED}Error: tmux session '$session_name' not available${NC}" >&2 + return 1 + fi + ;;& + screen|auto) + if command -v screen >/dev/null 2>&1; then + echo -e "${YELLOW}Sending to screen session $session_name: $cmd_str${NC}" + screen -S "$session_name" -X stuff "$cmd_str\n" + return $? + elif [ "$session_manager" = "screen" ]; then + echo -e "${RED}Error: screen not installed${NC}" >&2 + return 1 + fi + ;; + none|*) + echo -e "${RED}Error: No session manager configured (SESSION_MANAGER=$session_manager). Cannot send command.${NC}" >&2 + return 1 + ;; + esac + + echo -e "${RED}Error: Unable to find usable session (tmux/screen) to send command.${NC}" >&2 + return 1 +} + +function show_config() { + local service_name="$1" + if [ -z "$service_name" ]; then + echo -e "${RED}Error: Service name required for show-config${NC}" + return 1 + fi + + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" + return 1 + fi + + local provider=$(echo "$service_info" | jq -r '.provider') + local cfg_file="$CONFIG_DIR/$service_name.conf" + echo -e "${BLUE}Service: $service_name${NC}" + echo "Provider: $provider" + echo "Config file: $cfg_file" + if [ -f "$cfg_file" ]; then + # shellcheck source=/dev/null + source "$cfg_file" + echo "RUN_ENGINE_CONFIG_FILE: ${RUN_ENGINE_CONFIG_FILE:-}" + if [ -n "${RUN_ENGINE_CONFIG_FILE:-}" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then + # shellcheck source=/dev/null + source "$RUN_ENGINE_CONFIG_FILE" + echo "Session manager: ${SESSION_MANAGER:-}" + echo "Session name: ${SESSION_NAME:-}" + echo "BINPATH: ${BINPATH:-}" + echo "SERVERBIN: ${SERVERBIN:-}" + echo "CONFIG: ${CONFIG:-}" + echo "RESTART_POLICY: ${RESTART_POLICY:-}" + fi + else + echo "Config file not found" + fi +} + +# Return uptime in seconds for a service (echo integer), non-zero exit if not running +function service_uptime_seconds() { + local service_name="$1" + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" >&2 + return 1 + fi + + local provider=$(echo "$service_info" | jq -r '.provider') + + if [ "$provider" = "pm2" ]; then + check_pm2 || return 1 + local info_json + info_json=$(pm2 jlist 2>/dev/null) + local pm_uptime_ms + pm_uptime_ms=$(echo "$info_json" | jq -r ".[] | select(.name==\"$service_name\").pm2_env.pm_uptime // empty") + local status + status=$(echo "$info_json" | jq -r ".[] | select(.name==\"$service_name\").pm2_env.status // empty") + if [ -z "$pm_uptime_ms" ] || [ "$status" != "online" ]; then + return 1 + fi + # Current time in ms (fallback to seconds*1000 if %N unsupported) + local now_ms + if date +%s%N >/dev/null 2>&1; then + now_ms=$(( $(date +%s%N) / 1000000 )) + else + now_ms=$(( $(date +%s) * 1000 )) + fi + local diff_ms=$(( now_ms - pm_uptime_ms )) + [ "$diff_ms" -lt 0 ] && diff_ms=0 + echo $(( diff_ms / 1000 )) + return 0 + elif [ "$provider" = "systemd" ]; then + check_systemd || return 1 + local systemd_type="--user" + [ -f "/etc/systemd/system/$service_name.service" ] && systemd_type="--system" + + # Get ActiveEnterTimestampMonotonic in usec and ActiveState + local prop + if [ "$systemd_type" = "--system" ]; then + prop=$(systemctl show "$service_name.service" --property=ActiveEnterTimestampMonotonic,ActiveState 2>/dev/null) + else + prop=$(systemctl --user show "$service_name.service" --property=ActiveEnterTimestampMonotonic,ActiveState 2>/dev/null) + fi + local state + state=$(echo "$prop" | awk -F= '/^ActiveState=/{print $2}') + [ "$state" != "active" ] && return 1 + local enter_us + enter_us=$(echo "$prop" | awk -F= '/^ActiveEnterTimestampMonotonic=/{print $2}') + # Current monotonic time in seconds since boot + local now_s + now_s=$(cut -d' ' -f1 /proc/uptime) + # Compute uptime = now_monotonic - enter_monotonic + # enter_us may be empty on some systems; fallback to 0 + enter_us=${enter_us:-0} + # Convert now_s to microseconds using awk for precision, then compute diff + local diff_s + diff_s=$(awk -v now="$now_s" -v enter="$enter_us" 'BEGIN{printf "%d", (now*1000000 - enter)/1000000}') + [ "$diff_s" -lt 0 ] && diff_s=0 + echo "$diff_s" + return 0 + fi + + return 1 +} + +# Wait until service has at least uptime. Optional timeout seconds (default 120) +function wait_service_uptime() { + local service_name="$1" + local min_seconds="$2" + local timeout="${3:-120}" + local waited=0 + + while [ "$waited" -le "$timeout" ]; do + if secs=$(service_uptime_seconds "$service_name" 2>/dev/null); then + if [ "$secs" -ge "$min_seconds" ]; then + echo -e "${GREEN}Service '$service_name' has reached ${secs}s uptime (required: ${min_seconds}s)${NC}" + return 0 + fi + fi + sleep 1 + waited=$((waited + 1)) + done + echo -e "${RED}Timeout: $service_name did not reach ${min_seconds}s uptime within ${timeout}s${NC}" >&2 + return 1 +} + function attach_pm2_process() { local service_name="$1" @@ -1629,7 +1881,7 @@ case "${1:-help}" in delete_service "$2" ;; list) - list_services "$2" + list_services "${2:-}" ;; restore) restore_missing_services @@ -1670,6 +1922,52 @@ case "${1:-help}" in fi attach_to_service "$2" ;; + uptime-seconds) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for uptime-seconds command${NC}" + print_help + exit 1 + fi + service_uptime_seconds "$2" + ;; + wait-uptime) + if [ $# -lt 3 ]; then + echo -e "${RED}Error: Usage: $0 wait-uptime [timeout]${NC}" + print_help + exit 1 + fi + wait_service_uptime "$2" "$3" "${4:-120}" + ;; + is-running) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for is-running command${NC}" + print_help + exit 1 + fi + if service_is_running "$2"; then + echo -e "${GREEN}Service '$2' is running${NC}" + exit 0 + else + echo -e "${YELLOW}Service '$2' is not running${NC}" + exit 1 + fi + ;; + send) + if [ $# -lt 3 ]; then + echo -e "${RED}Error: Not enough arguments for send command${NC}" + print_help + exit 1 + fi + service_send_command "$2" "${@:3}" + ;; + show-config) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for show-config command${NC}" + print_help + exit 1 + fi + show_config "$2" + ;; help|--help|-h) print_help ;; diff --git a/apps/startup-scripts/test/test_startup_scripts.bats b/apps/startup-scripts/test/test_startup_scripts.bats index 2bbca2ffd..119a8c80c 100644 --- a/apps/startup-scripts/test/test_startup_scripts.bats +++ b/apps/startup-scripts/test/test_startup_scripts.bats @@ -143,6 +143,130 @@ teardown() { [[ "$output" =~ "on-failure|always" ]] } +@test "service-manager: help lists health and console commands" { + run "$SCRIPT_DIR/service-manager.sh" help + [ "$status" -eq 0 ] + [[ "$output" =~ "is-running " ]] + [[ "$output" =~ "uptime-seconds " ]] + [[ "$output" =~ "wait-uptime " ]] + [[ "$output" =~ "send " ]] + [[ "$output" =~ "show-config " ]] +} + +@test "service-manager: pm2 uptime and wait-uptime work with mocked pm2" { + command -v jq >/dev/null 2>&1 || skip "jq not installed" + export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services" + mkdir -p "$AC_SERVICE_CONFIG_DIR" + # Create registry with pm2 provider service + cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" << 'EOF' +[ + {"name":"test-world","provider":"pm2","type":"service","bin_path":"/bin/worldserver","args":"","systemd_type":"--user","restart_policy":"always"} +] +EOF + # Create minimal service config and run-engine config files required by 'send' + echo "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf\"" > "$AC_SERVICE_CONFIG_DIR/test-world.conf" + cat > "$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf" << 'EOF' +export SESSION_MANAGER="none" +export SESSION_NAME="test-world" +EOF + # Mock pm2 + cat > "$TEST_DIR/bin/pm2" << 'EOF' +#!/usr/bin/env bash +case "$1" in + jlist) + # Produce a JSON with uptime ~20 seconds + if date +%s%N >/dev/null 2>&1; then + nowms=$(( $(date +%s%N) / 1000000 )) + else + nowms=$(( $(date +%s) * 1000 )) + fi + up=$(( nowms - 20000 )) + echo "[{\"name\":\"test-world\",\"pm2_env\":{\"status\":\"online\",\"pm_uptime\":$up}}]" + ;; + id) + echo "[1]" + ;; + attach|send|list|describe|logs) + exit 0 + ;; + *) + exit 0 + ;; +esac +EOF + chmod +x "$TEST_DIR/bin/pm2" + + run "$SCRIPT_DIR/service-manager.sh" uptime-seconds test-world + debug_on_failure + [ "$status" -eq 0 ] + # Output should be a number >= 10 + [[ "$output" =~ ^[0-9]+$ ]] + [ "$output" -ge 10 ] + + run "$SCRIPT_DIR/service-manager.sh" wait-uptime test-world 10 5 + debug_on_failure + [ "$status" -eq 0 ] +} + +@test "service-manager: send works under pm2 with mocked pm2" { + command -v jq >/dev/null 2>&1 || skip "jq not installed" + export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services" + mkdir -p "$AC_SERVICE_CONFIG_DIR" + # Create registry and config as in previous test + cat > "$AC_SERVICE_CONFIG_DIR/service_registry.json" << 'EOF' +[ + {"name":"test-world","provider":"pm2","type":"service","bin_path":"/bin/worldserver","args":"","systemd_type":"--user","restart_policy":"always"} +] +EOF + echo "RUN_ENGINE_CONFIG_FILE=\"$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf\"" > "$AC_SERVICE_CONFIG_DIR/test-world.conf" + cat > "$AC_SERVICE_CONFIG_DIR/test-world-run-engine.conf" << 'EOF' +export SESSION_MANAGER="none" +export SESSION_NAME="test-world" +EOF + # pm2 mock + cat > "$TEST_DIR/bin/pm2" << 'EOF' +#!/usr/bin/env bash +case "$1" in + jlist) + if date +%s%N >/dev/null 2>&1; then + nowms=$(( $(date +%s%N) / 1000000 )) + else + nowms=$(( $(date +%s) * 1000 )) + fi + up=$(( nowms - 15000 )) + echo "[{\"name\":\"test-world\",\"pm2_env\":{\"status\":\"online\",\"pm_uptime\":$up}}]" + ;; + id) + echo "[1]" + ;; + send) + # simulate success + exit 0 + ;; + attach|list|describe|logs) + exit 0 + ;; + *) + exit 0 + ;; +esac +EOF + chmod +x "$TEST_DIR/bin/pm2" + + run "$SCRIPT_DIR/service-manager.sh" send test-world "server info" + debug_on_failure + [ "$status" -eq 0 ] +} + +@test "service-manager: wait-uptime times out for unknown service" { + command -v jq >/dev/null 2>&1 || skip "jq not installed" + export AC_SERVICE_CONFIG_DIR="$TEST_DIR/services" + mkdir -p "$AC_SERVICE_CONFIG_DIR" + echo "[]" > "$AC_SERVICE_CONFIG_DIR/service_registry.json" + run "$SCRIPT_DIR/service-manager.sh" wait-uptime unknown 2 1 + [ "$status" -ne 0 ] +} + # ===== EXAMPLE SCRIPTS TESTS ===== @test "examples: restarter-world should show configuration error" { From 46f12f78e063a9a4684667269e9f032fc3a86a66 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 6 Sep 2025 10:21:00 -0300 Subject: [PATCH 121/155] fix(Scripts/AzjolNerub): Modernize Krikthir the Gatewatcher script (#22647) Co-authored-by: sudlud --- .../rev_1754697821248272400.sql | 29 +++ .../AzjolNerub/AzjolNerub/azjol_nerub.h | 7 +- .../AzjolNerub/AzjolNerub/boss_hadronox.cpp | 6 +- .../boss_krikthir_the_gatewatcher.cpp | 175 ++++++++---------- .../AzjolNerub/instance_azjol_nerub.cpp | 70 +++---- 5 files changed, 137 insertions(+), 150 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1754697821248272400.sql diff --git a/data/sql/updates/pending_db_world/rev_1754697821248272400.sql b/data/sql/updates/pending_db_world/rev_1754697821248272400.sql new file mode 100644 index 000000000..2380f2e04 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1754697821248272400.sql @@ -0,0 +1,29 @@ +-- Remove heroic casts as it is already handled by spelldifficulty_dbc and add on aggro engage +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28729); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28729, 0, 0, 0, 0, 0, 100, 0, 2000, 6000, 15000, 20000, 0, 0, 11, 52524, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - In Combat - Cast \'Blinding Webs\''), +(28729, 0, 2, 0, 0, 0, 100, 0, 6000, 15000, 20000, 25000, 0, 0, 11, 52086, 0, 0, 0, 0, 0, 5, 30, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - In Combat - Cast \'Web Wrap\''), +(28729, 0, 3, 0, 0, 0, 100, 0, 4000, 12000, 9000, 15000, 0, 0, 11, 52469, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - In Combat - Cast \'Infected Bite\''), +(28729, 0, 5, 0, 8, 0, 100, 0, 52343, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - On Spellhit \'Krik`Thir Subboss Aggro Trigger\' - Set In Combat With Zone'), +(28729, 0, 6, 0, 0, 0, 100, 1, 500, 500, 0, 0, 0, 0, 39, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - In Combat - Call For Help (No Repeat)'), +(28729, 0, 1, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 205, 0, 1, 0, 0, 0, 0, 0, 0, 'Watcher Narjil - On Aggro - Do Action ID 1'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28730); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28730, 0, 0, 0, 0, 0, 100, 0, 2000, 6000, 15000, 20000, 0, 0, 11, 52470, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - In Combat - Cast \'Enrage\''), +(28730, 0, 1, 0, 0, 0, 100, 0, 6000, 15000, 20000, 25000, 0, 0, 11, 52086, 0, 0, 0, 0, 0, 5, 30, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - In Combat - Cast \'Web Wrap\''), +(28730, 0, 2, 0, 0, 0, 100, 0, 4000, 12000, 9000, 15000, 0, 0, 11, 52469, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - In Combat - Cast \'Infected Bite\''), +(28730, 0, 4, 0, 8, 0, 100, 0, 52343, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - On Spellhit \'Krik`Thir Subboss Aggro Trigger\' - Set In Combat With Zone'), +(28730, 0, 5, 0, 0, 0, 100, 1, 500, 500, 0, 0, 0, 0, 39, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - In Combat - Call For Help (No Repeat)'), +(28730, 0, 3, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 205, 0, 1, 0, 0, 0, 0, 0, 0, 'Watcher Gashra - On Aggro - Do Action ID 1'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28731); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(28731, 0, 0, 0, 0, 0, 100, 0, 2000, 6000, 15000, 20000, 0, 0, 11, 52493, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - In Combat - Cast \'Poison Spray\''), +(28731, 0, 2, 0, 0, 0, 100, 0, 6000, 15000, 20000, 25000, 0, 0, 11, 52086, 0, 0, 0, 0, 0, 5, 30, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - In Combat - Cast \'Web Wrap\''), +(28731, 0, 3, 0, 0, 0, 100, 0, 4000, 12000, 9000, 15000, 0, 0, 11, 52469, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - In Combat - Cast \'Infected Bite\''), +(28731, 0, 5, 0, 8, 0, 100, 0, 52343, 0, 0, 0, 0, 0, 38, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - On Spellhit \'Krik`Thir Subboss Aggro Trigger\' - Set In Combat With Zone'), +(28731, 0, 6, 0, 0, 0, 100, 1, 500, 500, 0, 0, 0, 0, 39, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - In Combat - Call For Help (No Repeat)'), +(28731, 0, 1, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 223, 1, 0, 0, 0, 0, 0, 205, 0, 1, 0, 0, 0, 0, 0, 0, 'Watcher Silthik - On Aggro - Do Action ID 1'); + +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` &~ 33554432 WHERE `entry` IN (28684, 31612); diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h index c124b4457..39f15f6ab 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/azjol_nerub.h @@ -26,14 +26,17 @@ enum ANData { - DATA_KRIKTHIR_THE_GATEWATCHER_EVENT = 0, - DATA_HADRONOX_EVENT = 1, + DATA_KRIKTHIR = 0, + DATA_HADRONOX = 1, DATA_ANUBARAK_EVENT = 2, MAX_ENCOUNTERS = 3 }; enum ANIds { + NPC_WATCHER_NARJIL = 28729, + NPC_WATCHER_GASHRA = 28730, + NPC_WATCHER_SILTHIK = 28731, NPC_SKITTERING_SWARMER = 28735, NPC_SKITTERING_INFECTIOR = 28736, NPC_KRIKTHIR_THE_GATEWATCHER = 28684, diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp index daa36c306..f3f9396ba 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_hadronox.cpp @@ -84,7 +84,7 @@ public: struct boss_hadronoxAI : public BossAI { - boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX_EVENT) + boss_hadronoxAI(Creature* creature) : BossAI(creature, DATA_HADRONOX) { } @@ -99,7 +99,7 @@ public: { if (param == ACTION_START_EVENT) { - instance->SetBossState(DATA_HADRONOX_EVENT, IN_PROGRESS); + instance->SetBossState(DATA_HADRONOX, IN_PROGRESS); me->setActive(true); events.ScheduleEvent(EVENT_HADRONOX_MOVE1, 20s); events.ScheduleEvent(EVENT_HADRONOX_MOVE2, 40s); @@ -342,7 +342,7 @@ public: PreventDefaultAction(); Unit* owner = GetUnitOwner(); if (InstanceScript* instance = owner->GetInstanceScript()) - if (instance->GetBossState(DATA_HADRONOX_EVENT) != DONE) + if (!instance->IsBossDone(DATA_HADRONOX)) { if (!owner->HasAura(SPELL_WEB_FRONT_DOORS)) owner->CastSpell(owner, _spellEntry, true); diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp index c32f6490c..36fd31ff1 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_krikthir_the_gatewatcher.cpp @@ -29,24 +29,8 @@ enum Spells SPELL_FRENZY = 28747 }; -enum Events -{ - EVENT_KRIK_START_WAVE = 1, - EVENT_KRIK_SUMMON = 2, - EVENT_KRIK_MIND_FLAY = 3, - EVENT_KRIK_CURSE = 4, - EVENT_KRIK_HEALTH_CHECK = 5, - EVENT_KRIK_ENTER_COMBAT = 6, - EVENT_KILL_TALK = 7, - EVENT_CALL_ADDS = 8, - EVENT_KRIK_CHECK_EVADE = 9 -}; - enum Npcs { - NPC_WATCHER_NARJIL = 28729, - NPC_WATCHER_GASHRA = 28730, - NPC_WATCHER_SILTHIK = 28731, NPC_WARRIOR = 28732, NPC_SKIRMISHER = 28734, NPC_SHADOWCASTER = 28733 @@ -62,6 +46,12 @@ enum Yells SAY_SEND_GROUP = 5 }; +enum MiscActions +{ + ACTION_MINION_ENGAGED = 1, + GROUP_SWARM = 1 +}; + class boss_krik_thir : public CreatureScript { public: @@ -69,18 +59,21 @@ public: struct boss_krik_thirAI : public BossAI { - boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR_THE_GATEWATCHER_EVENT) + boss_krik_thirAI(Creature* creature) : BossAI(creature, DATA_KRIKTHIR) { _initTalk = false; - } + _canTalk = true; + _minionInCombat = false; - EventMap events2; - bool _initTalk; + scheduler.SetValidator([this] + { + return !me->HasUnitState(UNIT_STATE_CASTING); + }); + } void Reset() override { BossAI::Reset(); - events2.Reset(); me->SummonCreature(NPC_WATCHER_NARJIL, 511.8f, 666.493f, 776.278f, 0.977f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); me->SummonCreature(NPC_SHADOWCASTER, 511.63f, 672.44f, 775.71f, 0.90f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); @@ -91,6 +84,22 @@ public: me->SummonCreature(NPC_WATCHER_SILTHIK, 543.826f, 665.123f, 776.245f, 1.55f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); me->SummonCreature(NPC_SKIRMISHER, 547.5f, 669.96f, 776.1f, 2.3f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); me->SummonCreature(NPC_SHADOWCASTER, 548.64f, 664.27f, 776.74f, 1.77f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000); + + ScheduleHealthCheckEvent(25, [&] { + DoCastSelf(SPELL_FRENZY, true); + + scheduler.CancelGroup(GROUP_SWARM); + + scheduler.Schedule(7s, 17s, GROUP_SWARM, [&](TaskContext context) + { + Talk(SAY_SWARM); + DoCastAOE(SPELL_SWARM); + context.Repeat(20s); + }); + }); + + _canTalk = true; + _minionInCombat = false; } void MoveInLineOfSight(Unit* who) override @@ -103,21 +112,25 @@ public: _initTalk = true; Talk(SAY_PREFIGHT); } + } - if (events.Empty() && who->GetPositionZ() < 785.0f) + void DoAction(int32 actionId) override + { + if (actionId == ACTION_MINION_ENGAGED && !_minionInCombat) { - events2.ScheduleEvent(EVENT_KRIK_START_WAVE, 10s); - events2.ScheduleEvent(EVENT_KRIK_START_WAVE, 40s); - events2.ScheduleEvent(EVENT_KRIK_START_WAVE, 70s); - events2.ScheduleEvent(EVENT_KRIK_ENTER_COMBAT, 100s); - events2.ScheduleEvent(EVENT_KRIK_CHECK_EVADE, 5s); + _minionInCombat = true; - events.ScheduleEvent(EVENT_KRIK_HEALTH_CHECK, 1s); - events.ScheduleEvent(EVENT_KRIK_MIND_FLAY, 13s); - events.ScheduleEvent(EVENT_KRIK_SUMMON, 17s); - events.ScheduleEvent(EVENT_KRIK_CURSE, 8s); - events.ScheduleEvent(EVENT_CALL_ADDS, 1s); - me->setActive(true); + for (Seconds const& timer : { 10s, 40s, 70s }) + { + me->m_Events.AddEventAtOffset([this] { + me->CastCustomSpell(SPELL_SUBBOSS_AGGRO_TRIGGER, SPELLVALUE_MAX_TARGETS, 1, me, true); + Talk(SAY_SEND_GROUP); + }, timer); + } + + me->m_Events.AddEventAtOffset([this] { + me->SetInCombatWithZone(); + }, 100s); } } @@ -132,7 +145,28 @@ public: { BossAI::JustEngagedWith(who); Talk(SAY_AGGRO); - events2.Reset(); + + me->m_Events.KillAllEvents(false); + + scheduler.Schedule(8s, 14s, [&](TaskContext context) + { + DoCastVictim(SPELL_MIND_FLAY); + if (!IsInFrenzy()) + context.Repeat(8s, 14s); + else + context.Repeat(5s, 9s); + }).Schedule(10s, 13s, GROUP_SWARM, [&](TaskContext context) + { + Talk(SAY_SWARM); + DoCastAOE(SPELL_SWARM); + context.Repeat(26s, 30s); + }); + + ScheduleTimedEvent(27s, 35s, [&] { + DoCastRandomTarget(SPELL_CURSE_OF_FATIGUE); + }, 27s, 35s); + + summons.DoZoneInCombat(); } void JustDied(Unit* killer) override @@ -141,12 +175,15 @@ public: Talk(SAY_DEATH); } - void KilledUnit(Unit* ) override + void KilledUnit(Unit* /*victim*/) override { - if (events.GetNextEventTime(EVENT_KILL_TALK) == 0) + if (_canTalk) { + _canTalk = false; Talk(SAY_SLAY); - events.ScheduleEvent(EVENT_KILL_TALK, 6s); + me->m_Events.AddEventAtOffset([&] { + _canTalk = true; + }, 6s); } } @@ -161,66 +198,12 @@ public: summons.Despawn(summon); } - void UpdateAI(uint32 diff) override - { - events2.Update(diff); - switch (events2.ExecuteEvent()) - { - case EVENT_KRIK_START_WAVE: - me->CastCustomSpell(SPELL_SUBBOSS_AGGRO_TRIGGER, SPELLVALUE_MAX_TARGETS, 1, me, true); - Talk(SAY_SEND_GROUP); - break; - case EVENT_KRIK_ENTER_COMBAT: - me->SetInCombatWithZone(); - break; - case EVENT_KRIK_CHECK_EVADE: - if (!SelectTargetFromPlayerList(100.0f)) - { - EnterEvadeMode(); - return; - } - events2.ScheduleEvent(EVENT_KRIK_CHECK_EVADE, 5s); - break; - } + private: + bool _initTalk; + bool _canTalk; + bool _minionInCombat; - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) - { - case EVENT_KRIK_HEALTH_CHECK: - if (HealthBelowPct(10)) - { - me->CastSpell(me, SPELL_FRENZY, true); - break; - } - events.ScheduleEvent(EVENT_KRIK_HEALTH_CHECK, 1s); - break; - case EVENT_KRIK_SUMMON: - Talk(SAY_SWARM); - me->CastSpell(me, SPELL_SWARM, false); - events.ScheduleEvent(EVENT_KRIK_SUMMON, 20s); - break; - case EVENT_KRIK_MIND_FLAY: - me->CastSpell(me->GetVictim(), SPELL_MIND_FLAY, false); - events.ScheduleEvent(EVENT_KRIK_MIND_FLAY, 15s); - break; - case EVENT_KRIK_CURSE: - if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) - me->CastSpell(target, SPELL_CURSE_OF_FATIGUE, true); - events.ScheduleEvent(EVENT_KRIK_CURSE, 10s); - break; - case EVENT_CALL_ADDS: - summons.DoZoneInCombat(); - break; - } - - DoMeleeAttackIfReady(); - } + [[nodiscard]] bool IsInFrenzy() const { return me->HasAura(SPELL_FRENZY); } }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp index 9de3f63be..3ee2f91f9 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/instance_azjol_nerub.cpp @@ -25,7 +25,7 @@ DoorData const doorData[] = { - { GO_KRIKTHIR_DOORS, DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, DOOR_TYPE_PASSAGE }, + { GO_KRIKTHIR_DOORS, DATA_KRIKTHIR, DOOR_TYPE_PASSAGE }, { GO_ANUBARAK_DOORS1, DATA_ANUBARAK_EVENT, DOOR_TYPE_ROOM }, { GO_ANUBARAK_DOORS2, DATA_ANUBARAK_EVENT, DOOR_TYPE_ROOM }, { GO_ANUBARAK_DOORS3, DATA_ANUBARAK_EVENT, DOOR_TYPE_ROOM }, @@ -34,15 +34,25 @@ DoorData const doorData[] = ObjectData const creatureData[] = { - { NPC_KRIKTHIR_THE_GATEWATCHER, DATA_KRIKTHIR_THE_GATEWATCHER_EVENT }, - { NPC_HADRONOX, DATA_HADRONOX_EVENT }, - { 0, 0 } + { NPC_KRIKTHIR_THE_GATEWATCHER, DATA_KRIKTHIR }, + { NPC_HADRONOX, DATA_HADRONOX }, + { 0, 0 } +}; + +ObjectData const summonData[] = +{ + { NPC_SKITTERING_SWARMER, DATA_KRIKTHIR }, + { NPC_SKITTERING_INFECTIOR, DATA_KRIKTHIR }, + { NPC_ANUB_AR_CHAMPION, DATA_HADRONOX }, + { NPC_ANUB_AR_NECROMANCER, DATA_HADRONOX }, + { NPC_ANUB_AR_CRYPTFIEND, DATA_HADRONOX }, + { 0, 0 } }; BossBoundaryData const boundaries = { - { DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, new RectangleBoundary(400.0f, 580.0f, 623.5f, 810.0f) }, - { DATA_HADRONOX_EVENT, new ZRangeBoundary(666.0f, 776.0f) }, + { DATA_KRIKTHIR, new RectangleBoundary(400.0f, 580.0f, 623.5f, 810.0f) }, + { DATA_HADRONOX, new ZRangeBoundary(666.0f, 776.0f) }, { DATA_ANUBARAK_EVENT, new CircleBoundary(Position(550.6178f, 253.5917f), 26.0f) } }; @@ -60,52 +70,14 @@ public: LoadBossBoundaries(boundaries); LoadDoorData(doorData); LoadObjectData(creatureData, nullptr); + LoadSummonData(summonData); }; - void OnCreatureCreate(Creature* creature) override + void OnCreatureEvade(Creature* creature) override { - switch (creature->GetEntry()) - { - case NPC_SKITTERING_SWARMER: - case NPC_SKITTERING_INFECTIOR: - if (Creature* krikthir = GetCreature((DATA_KRIKTHIR_THE_GATEWATCHER_EVENT))) - krikthir->AI()->JustSummoned(creature); - break; - case NPC_ANUB_AR_CHAMPION: - case NPC_ANUB_AR_NECROMANCER: - case NPC_ANUB_AR_CRYPTFIEND: - if (Creature* hadronox = GetCreature(DATA_HADRONOX_EVENT)) - hadronox->AI()->JustSummoned(creature); - break; - } - - InstanceScript::OnCreatureCreate(creature); - } - - void OnGameObjectCreate(GameObject* go) override - { - switch (go->GetEntry()) - { - case GO_KRIKTHIR_DOORS: - case GO_ANUBARAK_DOORS1: - case GO_ANUBARAK_DOORS2: - case GO_ANUBARAK_DOORS3: - AddDoor(go); - break; - } - } - - void OnGameObjectRemove(GameObject* go) override - { - switch (go->GetEntry()) - { - case GO_KRIKTHIR_DOORS: - case GO_ANUBARAK_DOORS1: - case GO_ANUBARAK_DOORS2: - case GO_ANUBARAK_DOORS3: - RemoveDoor(go); - break; - } + if (creature->EntryEquals(NPC_WATCHER_NARJIL, NPC_WATCHER_GASHRA, NPC_WATCHER_SILTHIK)) + if (Creature* krikthir = GetCreature(DATA_KRIKTHIR)) + krikthir->AI()->EnterEvadeMode(); } }; From 809a06ad0bf15488698fa08f17d9fe8456230928 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 6 Sep 2025 13:22:01 +0000 Subject: [PATCH 122/155] chore(DB): import pending files Referenced commit(s): 46f12f78e063a9a4684667269e9f032fc3a86a66 --- .../rev_1754697821248272400.sql => db_world/2025_09_06_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1754697821248272400.sql => db_world/2025_09_06_03.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1754697821248272400.sql b/data/sql/updates/db_world/2025_09_06_03.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1754697821248272400.sql rename to data/sql/updates/db_world/2025_09_06_03.sql index 2380f2e04..3bac2621c 100644 --- a/data/sql/updates/pending_db_world/rev_1754697821248272400.sql +++ b/data/sql/updates/db_world/2025_09_06_03.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_06_02 -> 2025_09_06_03 -- Remove heroic casts as it is already handled by spelldifficulty_dbc and add on aggro engage DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28729); INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES From d22f015e52cd43f6a57e988356d5693802ddde9f Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sat, 6 Sep 2025 06:28:01 -0700 Subject: [PATCH 123/155] fix(Core/Player): Haunted Memento aura is present when item is in bank (#22811) --- src/server/game/Entities/Player/Player.cpp | 8 ++++++++ src/server/game/Entities/Player/Player.h | 1 + src/server/game/Entities/Player/PlayerStorage.cpp | 7 ++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index f9fb147a8..ebb62dbe5 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -7067,6 +7067,14 @@ void Player::ApplyItemObtainSpells(Item* item, bool apply) } } +void Player::UpdateItemObtainSpells(Item* item, uint8 bag, uint8 slot) +{ + if (IsBankPos(bag, slot)) + ApplyItemObtainSpells(item, false); + else if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)) + ApplyItemObtainSpells(item, true); +} + SpellSchoolMask Player::GetMeleeDamageSchoolMask(WeaponAttackType attackType /*= BASE_ATTACK*/, uint8 damageIndex /*= 0*/) const { if (Item const* weapon = GetWeaponForAttack(attackType, true)) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index a79392e7f..befde718f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2208,6 +2208,7 @@ public: void CastAllObtainSpells(); void ApplyItemObtainSpells(Item* item, bool apply); + void UpdateItemObtainSpells(Item* item, uint8 bag, uint8 slot); SpellSchoolMask GetMeleeDamageSchoolMask(WeaponAttackType attackType = BASE_ATTACK, uint8 damageIndex = 0) const override; diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 4836811e2..1423afc98 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -2654,9 +2654,7 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool AddEnchantmentDurations(pItem); AddItemDurations(pItem); - - if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)) - ApplyItemObtainSpells(pItem, true); + UpdateItemObtainSpells(pItem, bag, slot); return pItem; } @@ -2694,8 +2692,7 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool pItem2->SetState(ITEM_CHANGED, this); - if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)) - ApplyItemObtainSpells(pItem2, true); + UpdateItemObtainSpells(pItem2, bag, slot); return pItem2; } From e6dd4bcb88a0360e6d2aa1c07fb6c377251aa278 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sat, 6 Sep 2025 15:28:22 +0200 Subject: [PATCH 124/155] fix(DB/Creature) Add immunities to various NPCs. (#22815) --- data/sql/updates/pending_db_world/immunities.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 data/sql/updates/pending_db_world/immunities.sql diff --git a/data/sql/updates/pending_db_world/immunities.sql b/data/sql/updates/pending_db_world/immunities.sql new file mode 100644 index 000000000..b039d9d09 --- /dev/null +++ b/data/sql/updates/pending_db_world/immunities.sql @@ -0,0 +1,12 @@ + +-- Arzeth the Merciless (Charm, Fear, Root, Snare, Banish, Horror) +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|16|64|1024|131072|8388608 WHERE (`entry` = 19354); + +-- Illidari Dreadlord (Charm, Fear, Snare) +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|16|1024 WHERE (`entry` = 21166); + +-- Wrath Master (Charm, Snare) +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|1024 WHERE (`entry` = 19005); + +-- Arazzius the Cruel (Charm, Fear, Root, Snare, Stun, Freeze, Polymorph, Banish) +UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|16|64|1024|2048|4096|65536|131072 WHERE (`entry` = 19191); From d1b75f93f4c1256ee886ee551acc157c74548839 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 6 Sep 2025 13:29:01 +0000 Subject: [PATCH 125/155] chore(DB): import pending files Referenced commit(s): d22f015e52cd43f6a57e988356d5693802ddde9f --- .../immunities.sql => db_world/2025_09_06_04.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/immunities.sql => db_world/2025_09_06_04.sql} (94%) diff --git a/data/sql/updates/pending_db_world/immunities.sql b/data/sql/updates/db_world/2025_09_06_04.sql similarity index 94% rename from data/sql/updates/pending_db_world/immunities.sql rename to data/sql/updates/db_world/2025_09_06_04.sql index b039d9d09..aef37356c 100644 --- a/data/sql/updates/pending_db_world/immunities.sql +++ b/data/sql/updates/db_world/2025_09_06_04.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_06_03 -> 2025_09_06_04 -- Arzeth the Merciless (Charm, Fear, Root, Snare, Banish, Horror) UPDATE `creature_template` SET `mechanic_immune_mask` = `mechanic_immune_mask` |1|16|64|1024|131072|8388608 WHERE (`entry` = 19354); From 62be119760064fd2a8a1ba2f0df80b70961fdd05 Mon Sep 17 00:00:00 2001 From: Yehonal Date: Sat, 6 Sep 2025 18:39:59 +0200 Subject: [PATCH 126/155] fix(CMake/Boost): package finding logic + suppress deprecation warnings + support Boost 1.89 --- deps/boost/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/deps/boost/CMakeLists.txt b/deps/boost/CMakeLists.txt index dd41c52a9..c41ce041a 100644 --- a/deps/boost/CMakeLists.txt +++ b/deps/boost/CMakeLists.txt @@ -31,7 +31,8 @@ else() set(BOOST_REQUIRED_VERSION 1.74) endif() -find_package(Boost ${BOOST_REQUIRED_VERSION} REQUIRED system filesystem program_options iostreams regex) +# Boost.System is header-only since 1.69; do not require it explicitly. +find_package(Boost ${BOOST_REQUIRED_VERSION} REQUIRED COMPONENTS filesystem program_options iostreams regex) if(NOT Boost_FOUND) if(NOT DEFINED ENV{Boost_ROOT} AND NOT DEFINED Boost_DIR AND NOT DEFINED BOOST_ROOT AND NOT DEFINED BOOSTROOT) From 1c0f4804fdf31375dd31582f1eac42bf303798d8 Mon Sep 17 00:00:00 2001 From: sudlud Date: Sat, 6 Sep 2025 23:08:31 +0200 Subject: [PATCH 127/155] =?UTF-8?q?fix(DB/Gameobject):=20Sniffed=20Values?= =?UTF-8?q?=20for=20'Doodad=5FFrostGiantIceShard'=20sp=E2=80=A6=20(#22823)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pending_db_world/rev_1757184397471097600.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757184397471097600.sql diff --git a/data/sql/updates/pending_db_world/rev_1757184397471097600.sql b/data/sql/updates/pending_db_world/rev_1757184397471097600.sql new file mode 100644 index 000000000..e931cd8bb --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757184397471097600.sql @@ -0,0 +1,12 @@ +-- Update gameobject 'Doodad_FrostGiantIceShard' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (192193, 192194, 192195)) AND (`guid` IN (20924, 20925, 20926)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(20924, 192193, 571, 0, 0, 1, 4, 7325.5732421875, -2044.4727783203125, 760.7373046875, 5.478795528411865234, 0.056999683380126953, 0.307971954345703125, -0.35146713256835937, 0.882255733013153076, 120, 255, 1, "", 46158, NULL), +(20925, 192194, 571, 0, 0, 1, 4, 7320.685546875, -2053.649169921875, 761.33929443359375, 5.478795528411865234, 0.056999683380126953, 0.307971954345703125, -0.35146713256835937, 0.882255733013153076, 120, 255, 1, "", 46158, NULL), +(20926, 192195, 571, 0, 0, 1, 4, 7321.001953125, -2054.294677734375, 760.8995361328125, 4.88195037841796875, 0.128789901733398437, -0.01092052459716796, -0.64533519744873046, 0.752885341644287109, 120, 255, 1, "", 46158, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (192186)) AND (`guid` IN (40)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(40, 192186, 571, 0, 0, 1, 4, 7299.98193359375, -2056.776123046875, 760.91754150390625, 3.129810810089111328, 0.336331367492675781, 0.04759979248046875, -0.9404611587524414, 0.012177699245512485, 120, 255, 1, "", 46158, NULL); From fa087ff05460b08ccc2de3065a77f2afa8f54b51 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 6 Sep 2025 21:09:37 +0000 Subject: [PATCH 128/155] chore(DB): import pending files Referenced commit(s): 1c0f4804fdf31375dd31582f1eac42bf303798d8 --- .../rev_1757184397471097600.sql => db_world/2025_09_06_05.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757184397471097600.sql => db_world/2025_09_06_05.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1757184397471097600.sql b/data/sql/updates/db_world/2025_09_06_05.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1757184397471097600.sql rename to data/sql/updates/db_world/2025_09_06_05.sql index e931cd8bb..fea71f267 100644 --- a/data/sql/updates/pending_db_world/rev_1757184397471097600.sql +++ b/data/sql/updates/db_world/2025_09_06_05.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_06_04 -> 2025_09_06_05 -- Update gameobject 'Doodad_FrostGiantIceShard' with sniffed values -- updated spawns DELETE FROM `gameobject` WHERE (`id` IN (192193, 192194, 192195)) AND (`guid` IN (20924, 20925, 20926)); From d55851c513df8a337e2c77719003324cc3c51b69 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Sat, 6 Sep 2025 19:11:38 -0400 Subject: [PATCH 129/155] fix(Scripts/Commands): Don't set error message flag for successful `event activelist` command. (#22824) --- src/server/scripts/Commands/cs_event.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/server/scripts/Commands/cs_event.cpp b/src/server/scripts/Commands/cs_event.cpp index 742bc6789..0f5264e8c 100644 --- a/src/server/scripts/Commands/cs_event.cpp +++ b/src/server/scripts/Commands/cs_event.cpp @@ -71,8 +71,6 @@ public: if (counter == 0) handler->SendSysMessage(LANG_NOEVENTFOUND); - handler->SetSentErrorMessage(true); - return true; } From a28824df85040e68f35938c3e085f0f107c84ef5 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Sun, 7 Sep 2025 04:02:03 -0700 Subject: [PATCH 130/155] feat(Core/Visibility): Far visibility worldobjects (#22828) --- src/common/Dynamic/TypeContainer.h | 46 +++++++++ src/common/Dynamic/TypeContainerFunctions.h | 96 ++++++++++++++++++ src/common/Dynamic/TypeContainerVisitor.h | 21 ++++ .../game/Entities/GameObject/GameObject.cpp | 4 - src/server/game/Entities/Object/Object.cpp | 97 +++++++++++-------- src/server/game/Entities/Object/Object.h | 27 ++++-- .../game/Entities/Object/ObjectDefines.h | 1 - src/server/game/Entities/Player/Player.cpp | 16 ++- src/server/game/Entities/Player/Player.h | 1 + .../game/Entities/Player/PlayerUpdates.cpp | 17 +--- src/server/game/Entities/Unit/Unit.cpp | 26 ++--- src/server/game/Grids/Cells/Cell.h | 2 + src/server/game/Grids/Cells/CellImpl.h | 10 ++ src/server/game/Grids/GridCell.h | 30 +++++- src/server/game/Grids/GridDefines.h | 8 +- src/server/game/Grids/MapGrid.h | 23 +++-- .../game/Grids/Notifiers/GridNotifiers.cpp | 65 ++++--------- .../game/Grids/Notifiers/GridNotifiers.h | 9 +- .../game/Grids/Notifiers/GridNotifiersImpl.h | 12 ++- src/server/game/Maps/Map.cpp | 64 ++++++++++++ src/server/game/Maps/Map.h | 9 ++ src/server/scripts/Commands/cs_debug.cpp | 33 ++++++- 22 files changed, 461 insertions(+), 156 deletions(-) diff --git a/src/common/Dynamic/TypeContainer.h b/src/common/Dynamic/TypeContainer.h index 0c338de5d..6240eb48d 100644 --- a/src/common/Dynamic/TypeContainer.h +++ b/src/common/Dynamic/TypeContainer.h @@ -26,6 +26,7 @@ #include "Dynamic/TypeList.h" #include "GridRefMgr.h" #include +#include /* * @class ContainerMapList is a mulit-type container for map elements @@ -50,6 +51,24 @@ struct ContainerMapList> ContainerMapList _TailElements; }; +template +struct ContainerVector +{ + std::vector _element; +}; + +template<> +struct ContainerVector +{ +}; + +template +struct ContainerVector> +{ + ContainerVector _elements; + ContainerVector _TailElements; +}; + template struct ContainerUnorderedMap { @@ -123,6 +142,33 @@ private: ContainerMapList i_elements; }; +template +class TypeVectorContainer +{ +public: + template [[nodiscard]] std::size_t Count() const { return Acore::Count(i_elements, (SPECIFIC_TYPE*)nullptr); } + + template + bool Insert(SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Acore::Insert(i_elements, obj); + return (t != nullptr); + } + + template + bool Remove(SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Acore::Remove(i_elements, obj); + return (t != nullptr); + } + + ContainerVector& GetElements() { return i_elements; } + [[nodiscard]] const ContainerVector& GetElements() const { return i_elements; } + +private: + ContainerVector i_elements; +}; + template class TypeUnorderedMapContainer { diff --git a/src/common/Dynamic/TypeContainerFunctions.h b/src/common/Dynamic/TypeContainerFunctions.h index b517f4529..61735d7d8 100644 --- a/src/common/Dynamic/TypeContainerFunctions.h +++ b/src/common/Dynamic/TypeContainerFunctions.h @@ -239,5 +239,101 @@ namespace Acore // SPECIFIC_TYPE* t = Remove(elements._elements, obj); // return ( t != nullptr ? t : Remove(elements._TailElements, obj)); //} + + /* ContainerVector Helpers */ + // count functions + template + std::size_t Count(const ContainerVector& elements, SPECIFIC_TYPE* /*fake*/) + { + return elements._element.getSize(); + } + + template + std::size_t Count(const ContainerVector& /*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template + std::size_t Count(const ContainerVector& /*elements*/, SPECIFIC_TYPE* /*fake*/) + { + return 0; + } + + template + std::size_t Count(const ContainerVector>& elements, SPECIFIC_TYPE* fake) + { + return Count(elements._elements, fake); + } + + template + std::size_t Count(const ContainerVector>& elements, SPECIFIC_TYPE* fake) + { + return Count(elements._TailElements, fake); + } + + // non-const insert functions + template + SPECIFIC_TYPE* Insert(ContainerVector& elements, SPECIFIC_TYPE* obj) + { + elements._element.push_back(obj); + return obj; + } + + template + SPECIFIC_TYPE* Insert(ContainerVector& /*elements*/, SPECIFIC_TYPE* /*obj*/) + { + return nullptr; + } + + // this is a missed + template + SPECIFIC_TYPE* Insert(ContainerVector& /*elements*/, SPECIFIC_TYPE* /*obj*/) + { + return nullptr; // a missed + } + + // Recursion + template + SPECIFIC_TYPE* Insert(ContainerVector>& elements, SPECIFIC_TYPE* obj) + { + SPECIFIC_TYPE* t = Insert(elements._elements, obj); + return (t != nullptr ? t : Insert(elements._TailElements, obj)); + } + + // non-const remove method + template SPECIFIC_TYPE* Remove(ContainerVector& elements, SPECIFIC_TYPE *obj) + { + // Simple vector find/swap/pop, this container should be very lightly used + // so I don't suspect the linear search complexity to be an issue + auto itr = std::find(elements._element.begin(), elements._element.end(), obj); + if (itr != elements._element.end()) + { + // Swap the element to be removed with the last element + std::swap(*itr, elements._element.back()); + + // Remove the last element (which is now the element we wanted to remove) + elements._element.pop_back(); + } + return obj; + } + + template SPECIFIC_TYPE* Remove(ContainerVector &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return nullptr; + } + + // this is a missed + template SPECIFIC_TYPE* Remove(ContainerVector &/*elements*/, SPECIFIC_TYPE * /*obj*/) + { + return nullptr; // a missed + } + + template SPECIFIC_TYPE* Remove(ContainerVector > &elements, SPECIFIC_TYPE *obj) + { + // The head element is bad + SPECIFIC_TYPE* t = Remove(elements._elements, obj); + return ( t != nullptr ? t : Remove(elements._TailElements, obj)); + } } #endif diff --git a/src/common/Dynamic/TypeContainerVisitor.h b/src/common/Dynamic/TypeContainerVisitor.h index 1553d918a..066f6f308 100644 --- a/src/common/Dynamic/TypeContainerVisitor.h +++ b/src/common/Dynamic/TypeContainerVisitor.h @@ -56,6 +56,27 @@ template void VisitorHelper(VISITOR& v, TypeM VisitorHelper(v, c.GetElements()); } +// VectorContainer +template void VisitorHelper(VISITOR& /*v*/, ContainerVector& /*c*/) {} + +template void VisitorHelper(VISITOR& v, ContainerVector& c) +{ + v.Visit(c._element); +} + +// recursion container map list +template void VisitorHelper(VISITOR& v, ContainerVector>& c) +{ + VisitorHelper(v, c._elements); + VisitorHelper(v, c._TailElements); +} + +// for TypeMapContainer +template void VisitorHelper(VISITOR& v, TypeVectorContainer& c) +{ + VisitorHelper(v, c.GetElements()); +} + // TypeUnorderedMapContainer template void VisitorHelper(VISITOR& /*v*/, ContainerUnorderedMap& /*c*/) { } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 1fa5a4177..f258db1ca 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -431,15 +431,11 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u // Check if GameObject is Large if (goinfo->IsLargeGameObject()) - { SetVisibilityDistanceOverride(VisibilityDistanceType::Large); - } // Check if GameObject is Infinite if (goinfo->IsInfiniteGameObject()) - { SetVisibilityDistanceOverride(VisibilityDistanceType::Infinite); - } return true; } diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 3081013be..2cd31a8fa 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1037,7 +1037,7 @@ void MovementInfo::OutDebug() } WorldObject::WorldObject() : WorldLocation(), - LastUsedScriptID(0), m_name(""), m_isActive(false), m_visibilityDistanceOverride(), m_zoneScript(nullptr), + LastUsedScriptID(0), m_name(""), m_isActive(false), _visibilityDistanceOverrideType(VisibilityDistanceType::Normal), m_zoneScript(nullptr), _zoneId(0), _areaId(0), _floorZ(INVALID_HEIGHT), _outdoors(false), _liquidData(), _updatePositionData(false), m_transport(nullptr), m_currMap(nullptr), _heartbeatTimer(HEARTBEAT_INTERVAL), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_useCombinedPhases(true), m_notifyflags(0), m_executed_notifies(0), _objectVisibilityContainer(this) @@ -1082,15 +1082,36 @@ void WorldObject::setActive(bool on) map->AddObjectToPendingUpdateList(this); } +float WorldObject::GetVisibilityOverrideDistance() const +{ + ASSERT(_visibilityDistanceOverrideType < VisibilityDistanceType::Max); + return VisibilityDistances[AsUnderlyingType(_visibilityDistanceOverrideType)]; +} + void WorldObject::SetVisibilityDistanceOverride(VisibilityDistanceType type) { ASSERT(type < VisibilityDistanceType::Max); - if (IsPlayer()) - { + + if (type == GetVisibilityOverrideType()) return; + + if (IsPlayer()) + return; + + if (IsVisibilityOverridden()) + { + if (IsFarVisible()) + GetMap()->RemoveWorldObjectFromFarVisibleMap(this); + else if (IsZoneWideVisible()) + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(GetZoneId(), this); } - m_visibilityDistanceOverride = VisibilityDistances[AsUnderlyingType(type)]; + if (type == VisibilityDistanceType::Large || type == VisibilityDistanceType::Gigantic) + GetMap()->AddWorldObjectToFarVisibleMap(this); + else if (type == VisibilityDistanceType::Infinite) + GetMap()->AddWorldObjectToZoneWideVisibleMap(GetZoneId(), this); + + _visibilityDistanceOverrideType = type; } void WorldObject::CleanupsBeforeDelete(bool /*finalCleanup*/) @@ -1127,6 +1148,8 @@ void WorldObject::UpdatePositionData() void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& data) { + uint32 const oldZoneId = _zoneId; + _zoneId = _areaId = data.areaId; if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(_areaId)) @@ -1136,6 +1159,17 @@ void WorldObject::ProcessPositionDataChanged(PositionFullTerrainStatus const& da _outdoors = data.outdoors; _floorZ = data.floorZ; _liquidData = data.liquidInfo; + + // Has zone ID changed? + if (oldZoneId != _zoneId) + { + // If so, check if we are far visibility overridden object and refresh maps if needed. + if (IsZoneWideVisible()) + { + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(oldZoneId, this); + GetMap()->AddWorldObjectToZoneWideVisibleMap(_zoneId, this); + } + } } void WorldObject::AddToWorld() @@ -1150,6 +1184,9 @@ void WorldObject::RemoveFromWorld() if (!IsInWorld()) return; + if (IsZoneWideVisible()) + GetMap()->RemoveWorldObjectFromZoneWideVisibleMap(GetZoneId(), this); + DestroyForVisiblePlayers(); GetObjectVisibilityContainer().CleanVisibilityReferences(); @@ -1612,26 +1649,16 @@ float WorldObject::GetGridActivationRange() const float WorldObject::GetVisibilityRange() const { - if (IsVisibilityOverridden() && IsCreature()) - { - return *m_visibilityDistanceOverride; - } + if (IsCreature() && IsVisibilityOverridden()) + return GetVisibilityOverrideDistance(); else if (IsGameObject()) { - { - if (IsInWintergrasp()) - { - return VISIBILITY_DIST_WINTERGRASP + VISIBILITY_INC_FOR_GOBJECTS; - } - else if (IsVisibilityOverridden()) - { - return *m_visibilityDistanceOverride; - } - else - { - return GetMap()->GetVisibilityRange() + VISIBILITY_INC_FOR_GOBJECTS; - } - } + if (IsInWintergrasp()) + return VISIBILITY_DIST_WINTERGRASP; + else if (IsVisibilityOverridden()) + return GetVisibilityOverrideDistance(); + else + return GetMap()->GetVisibilityRange(); } else return IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); @@ -1645,28 +1672,18 @@ float WorldObject::GetSightRange(WorldObject const* target) const { if (target) { - if (target->IsVisibilityOverridden() && target->IsCreature()) - { - return *target->m_visibilityDistanceOverride; - } + if (target->IsCreature() && target->IsVisibilityOverridden()) + return target->GetVisibilityOverrideDistance(); else if (target->IsGameObject()) { if (IsInWintergrasp() && target->IsInWintergrasp()) - { - return VISIBILITY_DIST_WINTERGRASP + VISIBILITY_INC_FOR_GOBJECTS; - } + return VISIBILITY_DIST_WINTERGRASP; else if (target->IsVisibilityOverridden()) - { - return *target->m_visibilityDistanceOverride; - } + return target->GetVisibilityOverrideDistance(); else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic()) - { return DEFAULT_VISIBILITY_INSTANCE; - } else - { - return GetMap()->GetVisibilityRange() + VISIBILITY_INC_FOR_GOBJECTS; - } + return GetMap()->GetVisibilityRange(); } return IsInWintergrasp() && target->IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); @@ -1674,19 +1691,13 @@ float WorldObject::GetSightRange(WorldObject const* target) const return IsInWintergrasp() ? VISIBILITY_DIST_WINTERGRASP : GetMap()->GetVisibilityRange(); } else if (ToCreature()) - { return ToCreature()->m_SightDistance; - } else - { return SIGHT_RANGE_UNIT; - } } if (ToDynObject() && isActiveObject()) - { return GetMap()->GetVisibilityRange(); - } return 0.0f; } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index 1a2cdf384..23784c9c6 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -358,9 +358,20 @@ template class GridObject { public: - [[nodiscard]] bool IsInGrid() const { return _gridRef.isValid(); } - void AddToGrid(GridRefMgr& m) { ASSERT(!IsInGrid()); _gridRef.link(&m, (T*)this); } - void RemoveFromGrid() { ASSERT(IsInGrid()); _gridRef.unlink(); } + bool IsInGrid() const + { + return _gridRef.isValid(); + } + void AddToGrid(GridRefMgr& m) + { + ASSERT(!IsInGrid()); + _gridRef.link(&m, (T*)this); + } + void RemoveFromGrid() + { + ASSERT(IsInGrid()); + _gridRef.unlink(); + } private: GridReference _gridRef; }; @@ -654,8 +665,11 @@ public: [[nodiscard]] bool isActiveObject() const { return m_isActive; } void setActive(bool isActiveObject); - [[nodiscard]] bool IsFarVisible() const { return m_isFarVisible; } - [[nodiscard]] bool IsVisibilityOverridden() const { return m_visibilityDistanceOverride.has_value(); } + VisibilityDistanceType GetVisibilityOverrideType() const { return _visibilityDistanceOverrideType; } + bool IsVisibilityOverridden() const { return _visibilityDistanceOverrideType > VisibilityDistanceType::Normal; } + bool IsZoneWideVisible() const { return _visibilityDistanceOverrideType == VisibilityDistanceType::Infinite; } + bool IsFarVisible() const { return _visibilityDistanceOverrideType == VisibilityDistanceType::Large || _visibilityDistanceOverrideType == VisibilityDistanceType::Gigantic; } + float GetVisibilityOverrideDistance() const; void SetVisibilityDistanceOverride(VisibilityDistanceType type); [[nodiscard]] bool IsInWintergrasp() const @@ -719,8 +733,7 @@ public: protected: std::string m_name; bool m_isActive; - bool m_isFarVisible; - Optional m_visibilityDistanceOverride; + VisibilityDistanceType _visibilityDistanceOverrideType; ZoneScript* m_zoneScript; virtual void ProcessPositionDataChanged(PositionFullTerrainStatus const& data); diff --git a/src/server/game/Entities/Object/ObjectDefines.h b/src/server/game/Entities/Object/ObjectDefines.h index bfae02203..fb0daa500 100644 --- a/src/server/game/Entities/Object/ObjectDefines.h +++ b/src/server/game/Entities/Object/ObjectDefines.h @@ -25,7 +25,6 @@ #define ATTACK_DISTANCE 5.0f #define VISIBILITY_COMPENSATION 15.0f // increase searchers #define INSPECT_DISTANCE 28.0f -#define VISIBILITY_INC_FOR_GOBJECTS 30.0f // pussywizard #define SPELL_SEARCHER_COMPENSATION 30.0f // increase searchers size in case we have large npc near cell border #define TRADE_DISTANCE 11.11f #define MAX_VISIBILITY_DISTANCE 250.0f // max distance for visible objects, experimental diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index ebb62dbe5..0700d1140 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16311,13 +16311,25 @@ float Player::GetSightRange(WorldObject const* target) const { float sightRange = WorldObject::GetSightRange(target); if (_farSightDistance) - { sightRange += *_farSightDistance; - } return sightRange; } +bool Player::IsWorldObjectOutOfSightRange(WorldObject const* target) const +{ + // Special handling for Infinite visibility override objects -> they are zone wide visible + if (target->GetVisibilityOverrideType() == VisibilityDistanceType::Infinite) + { + // Same zone, always visible + if (target->GetZoneId() == GetZoneId()) + return false; + } + + // Check if out of range + return !m_seer->IsWithinDist(target, GetSightRange(target), true); +} + std::string Player::GetPlayerName() { std::string name = GetName(); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index befde718f..3204f4241 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2630,6 +2630,7 @@ public: [[nodiscard]] Optional GetFarSightDistance() const; float GetSightRange(WorldObject const* target = nullptr) const override; + bool IsWorldObjectOutOfSightRange(WorldObject const* target) const; std::string GetPlayerName(); diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 8426e4c8c..fd41f73aa 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -1594,21 +1594,12 @@ void Player::UpdateVisibilityForPlayer(bool mapChange) // After added to map seer must be a player - there is no possibility to // still have different seer (all charm auras must be already removed) if (mapChange && m_seer != this) - { m_seer = this; - } - Acore::VisibleNotifier notifierNoLarge( - *this, mapChange, - false); // visit only objects which are not large; default distance - Cell::VisitObjects(m_seer, notifierNoLarge, - GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - notifierNoLarge.SendToSelf(); - - Acore::VisibleNotifier notifierLarge( - *this, mapChange, true); // visit only large objects; maximum distance - Cell::VisitObjects(m_seer, notifierLarge, GetSightRange()); - notifierLarge.SendToSelf(); + Acore::VisibleNotifier notifier(*this, mapChange); + Cell::VisitObjects(m_seer, notifier, GetSightRange()); + Cell::VisitFarVisibleObjects(m_seer, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); if (mapChange) m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index a2a33e89d..b5667566e 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -20284,12 +20284,10 @@ void Unit::ExecuteDelayedUnitRelocationEvent() //active->m_last_notify_position.Relocate(active->GetPositionX(), active->GetPositionY(), active->GetPositionZ()); } - Acore::PlayerRelocationNotifier relocateNoLarge(*player, false); // visit only objects which are not large; default distance - Cell::VisitObjects(viewPoint, relocateNoLarge, player->GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - relocateNoLarge.SendToSelf(); - Acore::PlayerRelocationNotifier relocateLarge(*player, true); // visit only large objects; maximum distance - Cell::VisitObjects(viewPoint, relocateLarge, MAX_VISIBILITY_DISTANCE); - relocateLarge.SendToSelf(); + Acore::PlayerRelocationNotifier notifier(*player); + Cell::VisitObjects(viewPoint, notifier, player->GetSightRange()); + Cell::VisitFarVisibleObjects(viewPoint, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); } if (Player* player = this->ToPlayer()) @@ -20323,16 +20321,10 @@ void Unit::ExecuteDelayedUnitRelocationEvent() GetMap()->LoadGridsInRange(*player, MAX_VISIBILITY_DISTANCE); - Acore::PlayerRelocationNotifier relocateNoLarge(*player, false); // visit only objects which are not large; default distance - Cell::VisitObjects(viewPoint, relocateNoLarge, player->GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - relocateNoLarge.SendToSelf(); - - if (!player->GetFarSightDistance()) - { - Acore::PlayerRelocationNotifier relocateLarge(*player, true); // visit only large objects; maximum distance - Cell::VisitObjects(viewPoint, relocateLarge, MAX_VISIBILITY_DISTANCE); - relocateLarge.SendToSelf(); - } + Acore::PlayerRelocationNotifier notifier(*player); + Cell::VisitObjects(viewPoint, notifier, player->GetSightRange()); + Cell::VisitFarVisibleObjects(viewPoint, notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); this->AddToNotify(NOTIFY_AI_RELOCATION); } @@ -20352,7 +20344,7 @@ void Unit::ExecuteDelayedUnitRelocationEvent() unit->m_last_notify_position.Relocate(unit->GetPositionX(), unit->GetPositionY(), unit->GetPositionZ()); Acore::CreatureRelocationNotifier relocate(*unit); - Cell::VisitObjects(unit, relocate, unit->GetVisibilityRange() + VISIBILITY_COMPENSATION); + Cell::VisitObjects(unit, relocate, unit->GetVisibilityRange()); this->AddToNotify(NOTIFY_AI_RELOCATION); } diff --git a/src/server/game/Grids/Cells/Cell.h b/src/server/game/Grids/Cells/Cell.h index 89676468d..a54f7f4cd 100644 --- a/src/server/game/Grids/Cells/Cell.h +++ b/src/server/game/Grids/Cells/Cell.h @@ -106,6 +106,8 @@ struct Cell template static void VisitObjects(WorldObject const* obj, T& visitor, float radius); template static void VisitObjects(float x, float y, Map* map, T& visitor, float radius); + template static void VisitFarVisibleObjects(WorldObject const* obj, T& visitor, float radius); + private: template void VisitCircle(TypeContainerVisitor&, Map&, CellCoord const&, CellCoord const&) const; }; diff --git a/src/server/game/Grids/Cells/CellImpl.h b/src/server/game/Grids/Cells/CellImpl.h index d3f97a09d..997840367 100644 --- a/src/server/game/Grids/Cells/CellImpl.h +++ b/src/server/game/Grids/Cells/CellImpl.h @@ -181,4 +181,14 @@ inline void Cell::VisitObjects(float x, float y, Map* map, T& visitor, float rad cell.Visit(p, gnotifier, *map, x, y, radius); } +template +inline void Cell::VisitFarVisibleObjects(WorldObject const* center_obj, T& visitor, float radius) +{ + CellCoord p(Acore::ComputeCellCoord(center_obj->GetPositionX(), center_obj->GetPositionY())); + Cell cell(p); + + TypeContainerVisitor gnotifier(visitor); + cell.Visit(p, gnotifier, *center_obj->GetMap(), *center_obj, radius); +} + #endif diff --git a/src/server/game/Grids/GridCell.h b/src/server/game/Grids/GridCell.h index 62ff76545..65e3a0315 100644 --- a/src/server/game/Grids/GridCell.h +++ b/src/server/game/Grids/GridCell.h @@ -33,16 +33,20 @@ #include "TypeContainer.h" #include "TypeContainerVisitor.h" +class WorldObject; + template < - class GRID_OBJECT_TYPES + class GRID_OBJECT_TYPES, + class FAR_VISIBLE_OBJECT_TYPES > class GridCell { public: ~GridCell() = default; - template void AddGridObject(SPECIFIC_OBJECT* obj) + template + void AddGridObject(SPECIFIC_OBJECT* obj) { _gridObjects.template insert(obj); ASSERT(obj->IsInGrid()); @@ -50,12 +54,32 @@ public: // Visit grid objects template - void Visit(TypeContainerVisitor >& visitor) + void Visit(TypeContainerVisitor>& visitor) { visitor.Visit(_gridObjects); } + template + void AddFarVisibleObject(SPECIFIC_OBJECT* obj) + { + _farVisibleObjects.template Insert(obj); + } + + template + void RemoveFarVisibleObject(SPECIFIC_OBJECT* obj) + { + _farVisibleObjects.template Remove(obj); + } + + // Visit far objects + template + void Visit(TypeContainerVisitor>& visitor) + { + visitor.Visit(_farVisibleObjects); + } + private: TypeMapContainer _gridObjects; + TypeVectorContainer _farVisibleObjects; }; #endif diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index 6918d2b1a..5c1d09c63 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -57,6 +57,9 @@ typedef TYPELIST_5(GameObject, Player, Creature, Corpse, DynamicObject) AllMapGr // List of object types stored on map level typedef TYPELIST_4(Creature, GameObject, DynamicObject, Corpse) AllMapStoredObjectTypes; +// List of object types that can have far visible range +typedef TYPELIST_2(Creature, GameObject) AllFarVisibleObjectTypes; + typedef GridRefMgr CorpseMapType; typedef GridRefMgr CreatureMapType; typedef GridRefMgr DynamicObjectMapType; @@ -73,10 +76,11 @@ enum GridMapTypeMask GRID_MAP_TYPE_MASK_ALL = 0x1F }; -typedef GridCell GridCellType; -typedef MapGrid MapGridType; +typedef GridCell GridCellType; +typedef MapGrid MapGridType; typedef TypeMapContainer GridTypeMapContainer; +typedef TypeVectorContainer FarVisibleGridContainer; typedef TypeUnorderedMapContainer MapStoredObjectTypesContainer; template diff --git a/src/server/game/Grids/MapGrid.h b/src/server/game/Grids/MapGrid.h index b6dda486c..c80c3b4d3 100644 --- a/src/server/game/Grids/MapGrid.h +++ b/src/server/game/Grids/MapGrid.h @@ -25,12 +25,13 @@ class GridTerrainData; template < - class GRID_OBJECT_TYPES + class GRID_OBJECT_TYPES, + class FAR_VISIBLE_OBJECT_TYPES > class MapGrid { public: - typedef GridCell GridCellType; + typedef GridCell GridCellType; MapGrid(uint16 const x, uint16 const y) : _x(x), _y(y), _objectDataLoaded(false), _terrainData(nullptr) { } @@ -54,9 +55,19 @@ public: GetOrCreateCell(x, y).RemoveGridObject(obj); } + template void AddFarVisibleObject(uint16 const x, uint16 const y, SPECIFIC_OBJECT* obj) + { + GetOrCreateCell(x, y).AddFarVisibleObject(obj); + } + + template void RemoveFarVisibleObject(uint16 const x, uint16 const y, SPECIFIC_OBJECT* obj) + { + GetOrCreateCell(x, y).RemoveFarVisibleObject(obj); + } + // Visit all cells template - void VisitAllCells(TypeContainerVisitor >& visitor) + void VisitAllCells(TypeContainerVisitor& visitor) { for (auto& cellX : _cells) { @@ -72,7 +83,7 @@ public: // Visit single cell template - void VisitCell(uint16 const x, uint16 const y, TypeContainerVisitor >& visitor) + void VisitCell(uint16 const x, uint16 const y, TypeContainerVisitor& visitor) { GridCellType* gridCell = GetCell(x, y); if (!gridCell) @@ -81,7 +92,7 @@ public: gridCell->Visit(visitor); } - void link(GridRefMgr>* pTo) + void link(GridRefMgr>* pTo) { _gridReference.link(pTo, this); } @@ -134,7 +145,7 @@ private: bool _objectDataLoaded; std::array, MAX_NUMBER_OF_CELLS>, MAX_NUMBER_OF_CELLS> _cells; // N * N array - GridReference> _gridReference; + GridReference> _gridReference; // Instances will share a copy of the parent maps terrainData std::shared_ptr _terrainData; diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 563634929..98fdbec76 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -29,38 +29,31 @@ void VisibleNotifier::Visit(GameObjectMapType& m) for (GameObjectMapType::iterator iter = m.begin(); iter != m.end(); ++iter) { GameObject* go = iter->GetSource(); - if (i_largeOnly != go->IsVisibilityOverridden()) - continue; - i_player.UpdateVisibilityOf(go, i_data, i_visibleNow); } } void VisibleNotifier::SendToSelf() { - // at this moment i_clientGUIDs have guids that not iterate at grid level checks - // but exist one case when this possible and object not out of range: transports - if (Transport* transport = i_player.GetTransport()) + // Update far visible objects + ZoneWideVisibleWorldObjectsSet const* zoneWideVisibleObjects = i_player.GetMap()->GetZoneWideVisibleWorldObjectsForZone(i_player.GetZoneId()); + if (zoneWideVisibleObjects) { - for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) + for (WorldObject* obj : *zoneWideVisibleObjects) { - if (i_largeOnly != (*itr)->IsVisibilityOverridden()) - continue; - - switch ((*itr)->GetTypeId()) + switch (obj->GetTypeId()) { - case TYPEID_GAMEOBJECT: - i_player.UpdateVisibilityOf((*itr)->ToGameObject(), i_data, i_visibleNow); - break; - case TYPEID_PLAYER: - i_player.UpdateVisibilityOf((*itr)->ToPlayer(), i_data, i_visibleNow); - (*itr)->ToPlayer()->UpdateVisibilityOf(&i_player); - break; - case TYPEID_UNIT: - i_player.UpdateVisibilityOf((*itr)->ToCreature(), i_data, i_visibleNow); - break; - default: - break; + case TYPEID_GAMEOBJECT: + i_player.UpdateVisibilityOf(obj->ToGameObject(), i_data, i_visibleNow); + break; + case TYPEID_UNIT: + i_player.UpdateVisibilityOf(obj->ToCreature(), i_data, i_visibleNow); + break; + case TYPEID_DYNAMICOBJECT: + i_player.UpdateVisibilityOf(obj->ToDynObject(), i_data, i_visibleNow); + break; + default: + break; } } } @@ -69,26 +62,7 @@ void VisibleNotifier::SendToSelf() for (VisibleWorldObjectsMap::iterator itr = visibleWorldObjects->begin(); itr != visibleWorldObjects->end();) { WorldObject* obj = itr->second; - if (i_largeOnly != obj->IsVisibilityOverridden()) - { - ++itr; - continue; - } - - // pussywizard: static transports are removed only in RemovePlayerFromMap and here if can no longer detect (eg. phase changed) - if (itr->first.IsTransport()) - { - if (GameObject* staticTrans = obj->ToGameObject()) - { - if (i_player.CanSeeOrDetect(staticTrans, false, true)) - { - ++itr; - continue; - } - } - } - - if (i_player.m_seer->IsWithinDist(obj, i_player.GetSightRange(obj), true)) + if (!i_player.IsWorldObjectOutOfSightRange(obj)) { ++itr; continue; @@ -111,12 +85,7 @@ void VisibleNotifier::SendToSelf() i_player.GetSession()->SendPacket(&packet); for (std::vector::const_iterator it = i_visibleNow.begin(); it != i_visibleNow.end(); ++it) - { - if (i_largeOnly != (*it)->IsVisibilityOverridden()) - continue; - i_player.GetInitialVisiblePackets(*it); - } } void VisibleChangesNotifier::Visit(PlayerMapType& m) diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 040c54244..d9556c77f 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -45,16 +45,16 @@ namespace Acore Player& i_player; std::vector& i_visibleNow; bool i_gobjOnly; - bool i_largeOnly; UpdateData i_data; - VisibleNotifier(Player& player, bool gobjOnly, bool largeOnly) : - i_player(player), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly), i_largeOnly(largeOnly) + VisibleNotifier(Player& player, bool gobjOnly) : + i_player(player), i_visibleNow(player.m_newVisible), i_gobjOnly(gobjOnly) { i_visibleNow.clear(); } void Visit(GameObjectMapType&); + template void Visit(std::vector& m); template void Visit(GridRefMgr& m); void SendToSelf(void); }; @@ -72,8 +72,9 @@ namespace Acore struct PlayerRelocationNotifier : public VisibleNotifier { - PlayerRelocationNotifier(Player& player, bool largeOnly): VisibleNotifier(player, false, largeOnly) { } + PlayerRelocationNotifier(Player& player): VisibleNotifier(player, false) { } + template void Visit(std::vector& m) { VisibleNotifier::Visit(m); } template void Visit(GridRefMgr& m) { VisibleNotifier::Visit(m); } void Visit(PlayerMapType&); }; diff --git a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h index 592e584ea..a24e87374 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiersImpl.h +++ b/src/server/game/Grids/Notifiers/GridNotifiersImpl.h @@ -25,6 +25,13 @@ #include "WorldPacket.h" #include "WorldSession.h" +template +inline void Acore::VisibleNotifier::Visit(std::vector& m) +{ + for (typename std::vector::iterator iter = m.begin(); iter != m.end(); ++iter) + i_player.UpdateVisibilityOf((*iter), i_data, i_visibleNow); +} + template inline void Acore::VisibleNotifier::Visit(GridRefMgr& m) { @@ -33,12 +40,7 @@ inline void Acore::VisibleNotifier::Visit(GridRefMgr& m) return; for (typename GridRefMgr::iterator iter = m.begin(); iter != m.end(); ++iter) - { - if (i_largeOnly != iter->GetSource()->IsVisibilityOverridden()) - continue; - i_player.UpdateVisibilityOf(iter->GetSource(), i_data, i_visibleNow); - } } // SEARCHERS & LIST SEARCHERS & WORKERS diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 8e2a0cacf..17ab70da0 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -118,6 +118,8 @@ void Map::AddToGrid(Creature* obj, Cell const& cell) { MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY()); grid->AddGridObject(cell.CellX(), cell.CellY(), obj); + if (obj->IsFarVisible()) + grid->AddFarVisibleObject(cell.CellX(), cell.CellY(), obj); obj->SetCurrentCell(cell); } @@ -127,6 +129,8 @@ void Map::AddToGrid(GameObject* obj, Cell const& cell) { MapGridType* grid = GetMapGrid(cell.GridX(), cell.GridY()); grid->AddGridObject(cell.CellX(), cell.CellY(), obj); + if (obj->IsFarVisible()) + grid->AddFarVisibleObject(cell.CellX(), cell.CellY(), obj); obj->SetCurrentCell(cell); } @@ -604,6 +608,52 @@ void Map::RemoveObjectFromMapUpdateList(WorldObject* obj) _RemoveObjectFromUpdateList(obj); } +// Used in VisibilityDistanceType::Large and VisibilityDistanceType::Gigantic +void Map::AddWorldObjectToFarVisibleMap(WorldObject* obj) +{ + Cell curr_cell(obj->GetPositionX(), obj->GetPositionY()); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + + if (obj->IsCreature()) + grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToCreature()); + else if (obj->IsGameObject()) + grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToGameObject()); +} + +void Map::RemoveWorldObjectFromFarVisibleMap(WorldObject* obj) +{ + Cell curr_cell(obj->GetPositionX(), obj->GetPositionY()); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + if (obj->IsCreature()) + grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToCreature()); + else if (obj->IsGameObject()) + grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToGameObject()); +} + +// Used in VisibilityDistanceType::Infinite +void Map::AddWorldObjectToZoneWideVisibleMap(uint32 zoneId, WorldObject* obj) +{ + _zoneWideVisibleWorldObjectsMap[zoneId].insert(obj); +} + +void Map::RemoveWorldObjectFromZoneWideVisibleMap(uint32 zoneId, WorldObject* obj) +{ + ZoneWideVisibleWorldObjectsMap::iterator itr = _zoneWideVisibleWorldObjectsMap.find(zoneId); + if (itr == _zoneWideVisibleWorldObjectsMap.end()) + return; + + itr->second.erase(obj); +} + +ZoneWideVisibleWorldObjectsSet const* Map::GetZoneWideVisibleWorldObjectsForZone(uint32 zoneId) const +{ + ZoneWideVisibleWorldObjectsMap::const_iterator itr = _zoneWideVisibleWorldObjectsMap.find(zoneId); + if (itr == _zoneWideVisibleWorldObjectsMap.end()) + return nullptr; + + return &itr->second; +} + void Map::HandleDelayedVisibility() { if (i_objectsForDelayedVisibility.empty()) @@ -656,6 +706,8 @@ void Map::RemoveFromMap(T* obj, bool remove) obj->RemoveFromWorld(); obj->RemoveFromGrid(); + if (obj->IsFarVisible()) + RemoveWorldObjectFromFarVisibleMap(obj); obj->ResetMap(); @@ -852,6 +904,12 @@ void Map::MoveAllCreaturesInMoveList() Cell const& old_cell = c->GetCurrentCell(); Cell new_cell(c->GetPositionX(), c->GetPositionY()); + MapGridType* oldGrid = GetMapGrid(old_cell.GridX(), old_cell.GridY()); + if (c->IsFarVisible()) + { + oldGrid->RemoveFarVisibleObject(old_cell.CellX(), old_cell.CellY(), c); + AddWorldObjectToFarVisibleMap(c); + } c->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) @@ -881,6 +939,12 @@ void Map::MoveAllGameObjectsInMoveList() Cell const& old_cell = go->GetCurrentCell(); Cell new_cell(go->GetPositionX(), go->GetPositionY()); + MapGridType* oldGrid = GetMapGrid(old_cell.GridX(), old_cell.GridY()); + if (go->IsFarVisible()) + { + oldGrid->RemoveFarVisibleObject(old_cell.CellX(), old_cell.CellY(), go); + AddWorldObjectToFarVisibleMap(go); + } go->RemoveFromGrid(); if (old_cell.DiffGrid(new_cell)) diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 2e5b0ed0a..c875e4eaa 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -147,6 +147,8 @@ struct ZoneDynamicInfo typedef std::map CreatureGroupHolderType; typedef std::unordered_map ZoneDynamicInfoMap; typedef std::unordered_set TransportsContainer; +typedef std::unordered_set ZoneWideVisibleWorldObjectsSet; +typedef std::unordered_map ZoneWideVisibleWorldObjectsMap; enum EncounterCreditType : uint8 { @@ -496,6 +498,12 @@ public: typedef std::vector UpdatableObjectList; typedef std::unordered_set PendingAddUpdatableObjectList; + void AddWorldObjectToFarVisibleMap(WorldObject* obj); + void RemoveWorldObjectFromFarVisibleMap(WorldObject* obj); + void AddWorldObjectToZoneWideVisibleMap(uint32 zoneId, WorldObject* obj); + void RemoveWorldObjectFromZoneWideVisibleMap(uint32 zoneId, WorldObject* obj); + ZoneWideVisibleWorldObjectsSet const* GetZoneWideVisibleWorldObjectsForZone(uint32 zoneId) const; + private: template void InitializeObject(T* obj); @@ -603,6 +611,7 @@ private: UpdatableObjectList _updatableObjectList; PendingAddUpdatableObjectList _pendingAddUpdatableObjectList; IntervalTimer _updatableObjectListRecheckTimer; + ZoneWideVisibleWorldObjectsMap _zoneWideVisibleWorldObjectsMap; }; enum InstanceResetMethod diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index a18e01e0b..40bb89b93 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -99,7 +99,8 @@ public: { "objectcount", HandleDebugObjectCountCommand, SEC_ADMINISTRATOR, Console::Yes}, { "dummy", HandleDebugDummyCommand, SEC_ADMINISTRATOR, Console::No }, { "mapdata", HandleDebugMapDataCommand, SEC_ADMINISTRATOR, Console::No }, - { "boundary", HandleDebugBoundaryCommand, SEC_ADMINISTRATOR, Console::No } + { "boundary", HandleDebugBoundaryCommand, SEC_ADMINISTRATOR, Console::No }, + { "visibilitydata", HandleDebugVisibilityDataCommand, SEC_ADMINISTRATOR, Console::No } }; static ChatCommandTable commandTable = { @@ -1403,6 +1404,36 @@ public: return true; } + + static bool HandleDebugVisibilityDataCommand(ChatHandler* handler) + { + Player* player = handler->GetPlayer(); + if (!player) + return false; + + std::array objectByTypeCount = {}; + + ObjectVisibilityContainer const& objectVisibilityContainer = player->GetObjectVisibilityContainer(); + for (auto const& kvPair : *objectVisibilityContainer.GetVisibleWorldObjectsMap()) + { + WorldObject const* obj = kvPair.second; + ++objectByTypeCount[obj->GetTypeId()]; + } + + uint32 zoneWideVisibleObjectsInZone = 0; + if (ZoneWideVisibleWorldObjectsSet const* farVisibleSet = player->GetMap()->GetZoneWideVisibleWorldObjectsForZone(player->GetZoneId())) + zoneWideVisibleObjectsInZone = farVisibleSet->size(); + + handler->PSendSysMessage("Visibility Range: {}", player->GetVisibilityRange()); + handler->PSendSysMessage("Visible Creatures: {}", objectByTypeCount[TYPEID_UNIT]); + handler->PSendSysMessage("Visible Players: {}", objectByTypeCount[TYPEID_PLAYER]); + handler->PSendSysMessage("Visible GameObjects: {}", objectByTypeCount[TYPEID_GAMEOBJECT]); + handler->PSendSysMessage("Visible DynamicObjects: {}", objectByTypeCount[TYPEID_DYNAMICOBJECT]); + handler->PSendSysMessage("Visible Corpses: {}", objectByTypeCount[TYPEID_CORPSE]); + handler->PSendSysMessage("Players we are visible to: {}", objectVisibilityContainer.GetVisiblePlayersMap().size()); + handler->PSendSysMessage("Zone wide visible objects in zone: {}", zoneWideVisibleObjectsInZone); + return true; + } }; void AddSC_debug_commandscript() From f1a6dd9c11bc79e88e6089e769e92f8ac680a9ce Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sun, 7 Sep 2025 11:48:13 -0300 Subject: [PATCH 131/155] =?UTF-8?q?fix(DB/SAI):=20Escape=20Silverbrook=20e?= =?UTF-8?q?scort=20should=20only=20start=20on=20passenger=E2=80=A6=20(#228?= =?UTF-8?q?29)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pending_db_world/rev_1757242433390659500.sql | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757242433390659500.sql diff --git a/data/sql/updates/pending_db_world/rev_1757242433390659500.sql b/data/sql/updates/pending_db_world/rev_1757242433390659500.sql new file mode 100644 index 000000000..6f532a9f0 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757242433390659500.sql @@ -0,0 +1,15 @@ +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 1) AND (`SourceEntry` = 27409) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 32) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 16) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 1, 27409, 0, 0, 32, 0, 16, 0, 0, 0, 0, 0, '', 'Ducal\'s horse only run sai if boarding passenger is player'); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 2740900) AND (`source_type` = 9) AND (`id` IN (0)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(2740900, 9, 0, 0, 0, 0, 100, 512, 3000, 3000, 0, 0, 0, 0, 53, 1, 27409, 0, 12308, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Script - Start Waypoint'); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 27409) AND (`source_type` = 0) AND (`id` IN (1, 8, 9, 10)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(27409, 0, 1, 10, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Passenger Boarded - Set Reactstate Passive'), +(27409, 0, 8, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 67, 1, 10000, 10000, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Reset - Create Timed Event'), +(27409, 0, 9, 0, 59, 0, 100, 0, 1, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Timed Event 1 Triggered - Despawn Instant'), +(27409, 0, 10, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 74, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Ducal\'s Horse - On Passenger Boarded - Remove Timed Event 1'); From 96f8149e0e0ce98a8c9de0ca7d3bfe0adc929141 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 7 Sep 2025 14:49:16 +0000 Subject: [PATCH 132/155] chore(DB): import pending files Referenced commit(s): f1a6dd9c11bc79e88e6089e769e92f8ac680a9ce --- .../rev_1757242433390659500.sql => db_world/2025_09_07_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757242433390659500.sql => db_world/2025_09_07_00.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1757242433390659500.sql b/data/sql/updates/db_world/2025_09_07_00.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1757242433390659500.sql rename to data/sql/updates/db_world/2025_09_07_00.sql index 6f532a9f0..2162949fb 100644 --- a/data/sql/updates/pending_db_world/rev_1757242433390659500.sql +++ b/data/sql/updates/db_world/2025_09_07_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_06_05 -> 2025_09_07_00 -- DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 1) AND (`SourceEntry` = 27409) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 32) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 16) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0); INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES From f191d71193afd956ebd0924658cf82f7b896363e Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Sun, 7 Sep 2025 10:17:10 -0700 Subject: [PATCH 133/155] fix(Core/Visibility): Fix visibility issue at sight limit (#22830) --- src/server/game/Grids/Notifiers/GridNotifiers.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 98fdbec76..5dd717f32 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -62,7 +62,8 @@ void VisibleNotifier::SendToSelf() for (VisibleWorldObjectsMap::iterator itr = visibleWorldObjects->begin(); itr != visibleWorldObjects->end();) { WorldObject* obj = itr->second; - if (!i_player.IsWorldObjectOutOfSightRange(obj)) + if (!i_player.IsWorldObjectOutOfSightRange(obj) + || i_player.CanSeeOrDetect(obj, false, true)) { ++itr; continue; From 9e04cb9e374af0dc4903fb5470090d376d248a81 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Mon, 8 Sep 2025 02:51:41 -0700 Subject: [PATCH 134/155] fix(Core/Visibility): Add some safety to visibility grid container (#22837) --- src/server/game/Maps/Map.cpp | 52 +++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 17ab70da0..d8dde935e 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -611,23 +611,40 @@ void Map::RemoveObjectFromMapUpdateList(WorldObject* obj) // Used in VisibilityDistanceType::Large and VisibilityDistanceType::Gigantic void Map::AddWorldObjectToFarVisibleMap(WorldObject* obj) { - Cell curr_cell(obj->GetPositionX(), obj->GetPositionY()); - MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + if (Creature* creature = obj->ToCreature()) + { + if (!creature->IsInGrid()) + return; - if (obj->IsCreature()) - grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToCreature()); - else if (obj->IsGameObject()) - grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToGameObject()); + Cell curr_cell = creature->GetCurrentCell(); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), creature); + } + else if (GameObject* go = obj->ToGameObject()) + { + if (!go->IsInGrid()) + return; + + Cell curr_cell = go->GetCurrentCell(); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + grid->AddFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), go); + } } void Map::RemoveWorldObjectFromFarVisibleMap(WorldObject* obj) { - Cell curr_cell(obj->GetPositionX(), obj->GetPositionY()); - MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); - if (obj->IsCreature()) - grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToCreature()); - else if (obj->IsGameObject()) - grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), obj->ToGameObject()); + if (Creature* creature = obj->ToCreature()) + { + Cell curr_cell = creature->GetCurrentCell(); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), creature); + } + else if (GameObject* go = obj->ToGameObject()) + { + Cell curr_cell = go->GetCurrentCell(); + MapGridType* grid = GetMapGrid(curr_cell.GridX(), curr_cell.GridY()); + grid->RemoveFarVisibleObject(curr_cell.CellX(), curr_cell.CellY(), go); + } } // Used in VisibilityDistanceType::Infinite @@ -904,11 +921,10 @@ void Map::MoveAllCreaturesInMoveList() Cell const& old_cell = c->GetCurrentCell(); Cell new_cell(c->GetPositionX(), c->GetPositionY()); - MapGridType* oldGrid = GetMapGrid(old_cell.GridX(), old_cell.GridY()); if (c->IsFarVisible()) { - oldGrid->RemoveFarVisibleObject(old_cell.CellX(), old_cell.CellY(), c); - AddWorldObjectToFarVisibleMap(c); + // Removes via GetCurrentCell, added back in AddToGrid + RemoveWorldObjectFromFarVisibleMap(c); } c->RemoveFromGrid(); @@ -939,11 +955,11 @@ void Map::MoveAllGameObjectsInMoveList() Cell const& old_cell = go->GetCurrentCell(); Cell new_cell(go->GetPositionX(), go->GetPositionY()); - MapGridType* oldGrid = GetMapGrid(old_cell.GridX(), old_cell.GridY()); + if (go->IsFarVisible()) { - oldGrid->RemoveFarVisibleObject(old_cell.CellX(), old_cell.CellY(), go); - AddWorldObjectToFarVisibleMap(go); + // Removes via GetCurrentCell, added back in AddToGrid + RemoveWorldObjectFromFarVisibleMap(go); } go->RemoveFromGrid(); From fa33b277006d879f2934d8a7f279a775db40a5c8 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Mon, 8 Sep 2025 02:51:53 -0700 Subject: [PATCH 135/155] fix(Core/Visibility): Mark destructible objects infinite visibility (#22838) --- src/server/game/Entities/GameObject/GameObjectData.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h index fac8fa9d2..fd95aa514 100644 --- a/src/server/game/Entities/GameObject/GameObjectData.h +++ b/src/server/game/Entities/GameObject/GameObjectData.h @@ -629,6 +629,8 @@ struct GameObjectTemplate return true; case GAMEOBJECT_TYPE_TRAPDOOR: return true; + case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: + return true; default: return false; } From 4d333518bb69fb15fee63b5e99a761d4d1185147 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Mon, 8 Sep 2025 02:52:14 -0700 Subject: [PATCH 136/155] fix(Core/Visibility): Visibility should be using 2d distance checks (#22839) --- src/server/game/Entities/Object/Object.cpp | 2 +- src/server/game/Entities/Player/Player.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 2cd31a8fa..a183d1467 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1801,7 +1801,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo } // Xinef: check reversely obj vs viewpoint, object could be a gameObject which overrides _IsWithinDist function to include gameobject size - if (!corpseCheck && !viewpoint->IsWithinDist(obj, GetSightRange(obj), true)) + if (!corpseCheck && !viewpoint->IsWithinDist(obj, GetSightRange(obj), false)) return false; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 0700d1140..cc9a820fc 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16327,7 +16327,7 @@ bool Player::IsWorldObjectOutOfSightRange(WorldObject const* target) const } // Check if out of range - return !m_seer->IsWithinDist(target, GetSightRange(target), true); + return !m_seer->IsWithinDist(target, GetSightRange(target), false); } std::string Player::GetPlayerName() From 9ba155c08997483cdef3f46b355ab36167c1cb65 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Mon, 8 Sep 2025 21:15:45 -0700 Subject: [PATCH 137/155] fix(Scripts/Northrend): Scourge Plague Spreader summons ghouls mid-air (#22845) --- .../rev_1757369338961290855.sql | 3 +++ .../scripts/Northrend/zone_borean_tundra.cpp | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757369338961290855.sql diff --git a/data/sql/updates/pending_db_world/rev_1757369338961290855.sql b/data/sql/updates/pending_db_world/rev_1757369338961290855.sql new file mode 100644 index 000000000..b13225d37 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757369338961290855.sql @@ -0,0 +1,3 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=45612; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (45612, 'spell_necropolis_beam'); diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 448f1aaca..88013c47c 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -2124,6 +2124,25 @@ public: } }; +// 45612 - Necropolis Beam +class spell_necropolis_beam: public SpellScript +{ + PrepareSpellScript(spell_necropolis_beam); + + void SetDest(SpellDestination& dest) + { + Unit* caster = GetCaster(); + float floorZ = caster->GetMapHeight(caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ()); + if (floorZ > INVALID_HEIGHT) + dest._position.m_positionZ = floorZ; + } + + void Register() override + { + OnDestinationTargetSelect += SpellDestinationTargetSelectFn(spell_necropolis_beam::SetDest, EFFECT_0, TARGET_DEST_CASTER); + } +}; + void AddSC_borean_tundra() { RegisterSpellScript(spell_q11919_q11940_drake_hunt_aura); @@ -2148,4 +2167,5 @@ void AddSC_borean_tundra() RegisterSpellScript(spell_q11719_bloodspore_ruination_45997); new npc_bloodmage_laurith(); RegisterCreatureAI(npc_jenny); + RegisterSpellScript(spell_necropolis_beam); } From c7e5e7adc0f9538717a26bf23e3fc812ffffb931 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Sep 2025 04:16:51 +0000 Subject: [PATCH 138/155] chore(DB): import pending files Referenced commit(s): 9ba155c08997483cdef3f46b355ab36167c1cb65 --- .../rev_1757369338961290855.sql => db_world/2025_09_09_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757369338961290855.sql => db_world/2025_09_09_00.sql} (78%) diff --git a/data/sql/updates/pending_db_world/rev_1757369338961290855.sql b/data/sql/updates/db_world/2025_09_09_00.sql similarity index 78% rename from data/sql/updates/pending_db_world/rev_1757369338961290855.sql rename to data/sql/updates/db_world/2025_09_09_00.sql index b13225d37..a16856bd5 100644 --- a/data/sql/updates/pending_db_world/rev_1757369338961290855.sql +++ b/data/sql/updates/db_world/2025_09_09_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_07_00 -> 2025_09_09_00 -- DELETE FROM `spell_script_names` WHERE `spell_id`=45612; INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (45612, 'spell_necropolis_beam'); From 5e5b64e04ac0711ced880cf93937b2a8c4ca18d7 Mon Sep 17 00:00:00 2001 From: sudlud Date: Tue, 9 Sep 2025 06:21:36 +0200 Subject: [PATCH 139/155] fix(DB/Gameobject): Sniffed Values for 'Death's Gaze Orb' spawns (#22844) --- .../updates/pending_db_world/rev_1757362863233057700.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757362863233057700.sql diff --git a/data/sql/updates/pending_db_world/rev_1757362863233057700.sql b/data/sql/updates/pending_db_world/rev_1757362863233057700.sql new file mode 100644 index 000000000..f1ea83e2f --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757362863233057700.sql @@ -0,0 +1,7 @@ +-- Update gameobject 'Death's Gaze Orb' with sniffed values +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (192917)) AND (`guid` IN (174, 175, 176)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(174, 192917, 571, 0, 0, 1, 1, 6475.67822265625, 3399.67529296875, 599.08349609375, 2.059488296508789062, 0, 0, 0.857167243957519531, 0.515038192272186279, 120, 255, 1, "", 46368, NULL), +(175, 192917, 571, 0, 0, 1, 1, 6514.7314453125, 3273.22216796875, 667.54388427734375, 3.926995515823364257, 0, 0, -0.92387866973876953, 0.38268551230430603, 120, 255, 1, "", 46368, NULL), +(176, 192917, 571, 0, 0, 1, 1, 6705.81982421875, 3528.986328125, 673.74957275390625, 0.715584874153137207, 0, 0, 0.350207328796386718, 0.936672210693359375, 120, 255, 1, "", 46368, NULL); From 4f3fb1500db0a2219e236c4731029bfd72161af1 Mon Sep 17 00:00:00 2001 From: sudlud Date: Tue, 9 Sep 2025 06:21:56 +0200 Subject: [PATCH 140/155] fix(DB/Gameobject): Sniffed Values for 'Meat Wagon' spawns (#22843) --- .../rev_1757354065794360100.sql | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757354065794360100.sql diff --git a/data/sql/updates/pending_db_world/rev_1757354065794360100.sql b/data/sql/updates/pending_db_world/rev_1757354065794360100.sql new file mode 100644 index 000000000..d4d7d96d5 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757354065794360100.sql @@ -0,0 +1,35 @@ +-- Update gameobject 'Meat Wagon' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (193616, 193618, 193620)) AND (`guid` IN (62425, 62426, 62430, 62435, 62436, 62437)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(62425, 193616, 571, 0, 0, 1, 1, 6879.06787109375, 1607.7725830078125, 388.887359619140625, 3.612837791442871093, 0, 0, -0.97236919403076171, 0.233448356389999389, 120, 255, 1, "", 46368, NULL), +(62426, 193616, 571, 0, 0, 1, 1, 6883.10888671875, 1600.4949951171875, 389.033172607421875, 0.506144583225250244, 0, 0, 0.250379562377929687, 0.968147754669189453, 120, 255, 1, "", 46368, NULL), +(62430, 193618, 571, 0, 0, 1, 1, 6878.4189453125, 1602.437744140625, 389.033172607421875, 0.471238493919372558, 0, 0, 0.233445167541503906, 0.972369968891143798, 120, 255, 1, "", 46368, NULL), +(62435, 193620, 571, 0, 0, 1, 1, 6881.7578125, 1604.779541015625, 388.380401611328125, 3.612837791442871093, 0, 0, -0.97236919403076171, 0.233448356389999389, 120, 255, 1, "", 46368, NULL), +(62436, 193620, 571, 0, 0, 1, 1, 6896.3095703125, 1609.5350341796875, 388.3387451171875, 4.049167633056640625, 0, 0, -0.89879322052001953, 0.438372820615768432, 120, 255, 1, "", 46368, NULL), +(62437, 193620, 571, 0, 0, 1, 1, 6886.94091796875, 1619.73095703125, 388.269287109375, 2.007128477096557617, 0, 0, 0.84339141845703125, 0.537299633026123046, 120, 255, 1, "", 46368, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (193616, 193617, 193618, 193619, 193620)) AND (`guid` BETWEEN 2211 AND 2231); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(2211, 193616, 571, 0, 0, 1, 1, 6876.56005859375, 1539.6214599609375, 389.033172607421875, 6.213373661041259765, 0, 0, -0.03489875793457031, 0.999390840530395507, 120, 255, 1, "", 46368, NULL), +(2212, 193616, 571, 0, 0, 1, 1, 6880.126953125, 1540.478759765625, 389.033172607421875, 3.45575571060180664, 0, 0, -0.98768806457519531, 0.156436234712600708, 120, 255, 1, "", 46368, NULL), +(2213, 193616, 571, 0, 0, 1, 1, 6883.654296875, 1540.2552490234375, 389.033172607421875, 1.134462952613830566, 0, 0, 0.537299156188964843, 0.843391716480255126, 120, 255, 1, "", 46368, NULL), +(2214, 193616, 571, 0, 0, 1, 1, 6884.5859375, 1573.8497314453125, 389.033172607421875, 0.052358884364366531, 0, 0, 0.02617645263671875, 0.999657332897186279, 120, 255, 1, "", 46368, NULL), +(2215, 193616, 571, 0, 0, 1, 1, 6884.59228515625, 1582.0037841796875, 389.033172607421875, 3.159062385559082031, 0, 0, -0.99996185302734375, 0.008734640665352344, 120, 255, 1, "", 46368, NULL), +(2216, 193616, 571, 0, 0, 1, 1, 6888.81982421875, 1541.5426025390625, 389.033172607421875, 3.036838293075561523, 0, 0, 0.998628616333007812, 0.052353221923112869, 120, 255, 1, "", 46368, NULL), +(2217, 193617, 571, 0, 0, 1, 1, 6875.7060546875, 1601.95068359375, 389.033172607421875, 3.612837791442871093, 0, 0, -0.97236919403076171, 0.233448356389999389, 120, 255, 1, "", 46368, NULL), +(2218, 193617, 571, 0, 0, 1, 1, 6879.490234375, 1578.3424072265625, 389.033172607421875, 3.176533222198486328, 0, 0, -0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 46368, NULL), +(2219, 193617, 571, 0, 0, 1, 1, 6903.123046875, 1554.924072265625, 389.033172607421875, 2.286378860473632812, 0, 0, 0.909960746765136718, 0.414694398641586303, 120, 255, 1, "", 46368, NULL), +(2220, 193617, 571, 0, 0, 1, 1, 6907.595703125, 1567.492919921875, 389.033172607421875, 6.161012649536132812, 0, 0, -0.06104850769042968, 0.998134791851043701, 120, 255, 1, "", 46368, NULL), +(2221, 193617, 571, 0, 0, 1, 1, 6912.046875, 1560.1182861328125, 389.033203125, 1.291541695594787597, 0, 0, 0.60181427001953125, 0.798636078834533691, 120, 255, 1, "", 46368, NULL), +(2222, 193618, 571, 0, 0, 1, 1, 6881.86181640625, 1578.113037109375, 389.033172607421875, 0.052358884364366531, 0, 0, 0.02617645263671875, 0.999657332897186279, 120, 255, 1, "", 46368, NULL), +(2223, 193618, 571, 0, 0, 1, 1, 6906.19384765625, 1579.285400390625, 389.033172607421875, 0.610863447189331054, 0, 0, 0.3007049560546875, 0.953717231750488281, 120, 255, 1, "", 46368, NULL), +(2224, 193618, 571, 0, 0, 1, 1, 6906.30126953125, 1589.4271240234375, 389.033172607421875, 1.151916384696960449, 0, 0, 0.544638633728027343, 0.838670849800109863, 120, 255, 1, "", 46368, NULL), +(2225, 193619, 571, 0, 0, 1, 1, 6872.22021484375, 1602.7569580078125, 389.033172607421875, 3.682650327682495117, 0, 0, -0.96362972259521484, 0.26724100112915039, 120, 255, 1, "", 46368, NULL), +(2226, 193619, 571, 0, 0, 1, 1, 6872.970703125, 1600.5621337890625, 389.033172607421875, 3.665196180343627929, 0, 0, -0.96592521667480468, 0.258821308612823486, 120, 255, 1, "", 46368, NULL), +(2227, 193619, 571, 0, 0, 1, 1, 6874.19140625, 1598.2149658203125, 389.033172607421875, 3.665196180343627929, 0, 0, -0.96592521667480468, 0.258821308612823486, 120, 255, 1, "", 46368, NULL), +(2228, 193619, 571, 0, 0, 1, 1, 6875.76220703125, 1577.950927734375, 389.033172607421875, 3.22885894775390625, 0, 0, -0.99904823303222656, 0.043619260191917419, 120, 255, 1, "", 46368, NULL), +(2229, 193619, 571, 0, 0, 1, 1, 6875.81689453125, 1580.9786376953125, 389.033172607421875, 3.106652259826660156, 0, 0, 0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 46368, NULL), +(2230, 193619, 571, 0, 0, 1, 1, 6876.02099609375, 1575.36083984375, 389.033172607421875, 3.333590030670166015, 0, 0, -0.99539566040039062, 0.095851235091686248, 120, 255, 1, "", 46368, NULL), +(2231, 193620, 571, 0, 0, 1, 1, 6885.62060546875, 1578.173583984375, 388.435943603515625, 3.176533222198486328, 0, 0, -0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 46368, NULL); From 6750edfee5393c51224df70407ea455bfe3c8a71 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Sep 2025 04:22:35 +0000 Subject: [PATCH 141/155] chore(DB): import pending files Referenced commit(s): 5e5b64e04ac0711ced880cf93937b2a8c4ca18d7 --- .../rev_1757354065794360100.sql => db_world/2025_09_09_01.sql} | 1 + .../rev_1757362863233057700.sql => db_world/2025_09_09_02.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/rev_1757354065794360100.sql => db_world/2025_09_09_01.sql} (99%) rename data/sql/updates/{pending_db_world/rev_1757362863233057700.sql => db_world/2025_09_09_02.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1757354065794360100.sql b/data/sql/updates/db_world/2025_09_09_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1757354065794360100.sql rename to data/sql/updates/db_world/2025_09_09_01.sql index d4d7d96d5..38d8b7a40 100644 --- a/data/sql/updates/pending_db_world/rev_1757354065794360100.sql +++ b/data/sql/updates/db_world/2025_09_09_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_09_00 -> 2025_09_09_01 -- Update gameobject 'Meat Wagon' with sniffed values -- updated spawns DELETE FROM `gameobject` WHERE (`id` IN (193616, 193618, 193620)) AND (`guid` IN (62425, 62426, 62430, 62435, 62436, 62437)); diff --git a/data/sql/updates/pending_db_world/rev_1757362863233057700.sql b/data/sql/updates/db_world/2025_09_09_02.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1757362863233057700.sql rename to data/sql/updates/db_world/2025_09_09_02.sql index f1ea83e2f..3ddeed53e 100644 --- a/data/sql/updates/pending_db_world/rev_1757362863233057700.sql +++ b/data/sql/updates/db_world/2025_09_09_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_09_01 -> 2025_09_09_02 -- Update gameobject 'Death's Gaze Orb' with sniffed values -- new spawns DELETE FROM `gameobject` WHERE (`id` IN (192917)) AND (`guid` IN (174, 175, 176)); From 79278aa5045872d3f2eea59fd5eb947615a240b0 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Mon, 8 Sep 2025 22:40:01 -0700 Subject: [PATCH 142/155] fix(Scripts/Dragonblight): quest Fresh Remounts (#22846) --- .../rev_1757371089104032155.sql | 24 +++++++++++++++++++ .../scripts/Northrend/zone_dragonblight.cpp | 22 +++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757371089104032155.sql diff --git a/data/sql/updates/pending_db_world/rev_1757371089104032155.sql b/data/sql/updates/pending_db_world/rev_1757371089104032155.sql new file mode 100644 index 000000000..01e6f402c --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757371089104032155.sql @@ -0,0 +1,24 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id`=48297; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (48297, 'spell_handover_reins'); + +DELETE FROM `vehicle_seat_addon` WHERE `SeatEntry`=742; +INSERT INTO `vehicle_seat_addon` (`SeatEntry`, `SeatOrientation`, `ExitParamX`, `ExitParamY`, `ExitParamZ`, `ExitParamO`, `ExitParamValue`) VALUES +(742, 0, 0, -2, 0, 0, 1); + +-- Set Phase from 5 to 1 for id 2 +-- Generate comments with Keira +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 27213); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(27213, 0, 0, 1, 11, 0, 100, 512, 0, 0, 0, 0, 0, 0, 211, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Respawn - Flag reset 0'), +(27213, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Respawn - Set Event Phase 1'), +(27213, 0, 2, 3, 28, 1, 100, 512, 0, 0, 0, 0, 0, 0, 22, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Set Event Phase 2 (Phase 1)'), +(27213, 0, 3, 4, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Set Home Position (Phase 1)'), +(27213, 0, 4, 5, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 2, 35, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Set Faction 35 (Phase 1)'), +(27213, 0, 5, 6, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 18, 131072, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Set Flags Pacified (Phase 1)'), +(27213, 0, 6, 0, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2721300, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Passenger Removed - Run Script (Phase 1)'), +(27213, 0, 7, 0, 59, 2, 100, 512, 1, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Timed Event 1 Triggered - Despawn Instant (Phase 2)'), +(27213, 0, 8, 9, 23, 2, 100, 512, 48290, 1, 500, 500, 0, 0, 74, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Aura \'Onslaught Riding Crop\' - Remove Timed Event 1 (Phase 2)'), +(27213, 0, 9, 0, 61, 2, 100, 512, 0, 0, 0, 0, 0, 0, 22, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Aura \'Onslaught Riding Crop\' - Set Event Phase 3 (Phase 2)'), +(27213, 0, 10, 11, 31, 4, 100, 513, 48297, 0, 0, 0, 0, 0, 22, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Target Spellhit \'Hand Over Reins\' - Set Event Phase 4 (Phase 3) (No Repeat)'), +(27213, 0, 11, 0, 61, 8, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2721301, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Onslaught Warhorse - On Target Spellhit \'Hand Over Reins\' - Run Script (Phase 3) (No Repeat)'); diff --git a/src/server/scripts/Northrend/zone_dragonblight.cpp b/src/server/scripts/Northrend/zone_dragonblight.cpp index 2faac7047..9cf0cb7c0 100644 --- a/src/server/scripts/Northrend/zone_dragonblight.cpp +++ b/src/server/scripts/Northrend/zone_dragonblight.cpp @@ -2240,6 +2240,27 @@ class spell_dragonblight_corrosive_spit : public AuraScript } }; +// 48297 - Hand Over Reins +enum HandOverReins +{ + SPELL_ONSLAUGHT_RIDING_CROP = 48290 +}; + +class spell_handover_reins : public SpellScript +{ + PrepareSpellScript(spell_handover_reins); + + void HandleScriptEffect(SpellEffIndex /*effIndex*/) + { + GetCaster()->RemoveAura(SPELL_ONSLAUGHT_RIDING_CROP); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_handover_reins::HandleScriptEffect, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + void AddSC_dragonblight() { new npc_conversing_with_the_depths_trigger(); @@ -2267,4 +2288,5 @@ void AddSC_dragonblight() RegisterSpellScript(spell_q12096_q12092_bark); new npc_torturer_lecraft(); RegisterSpellScript(spell_dragonblight_corrosive_spit); + RegisterSpellScript(spell_handover_reins); } From cf13c6326628e895ebefa17a55c2d7eb69500ae5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Sep 2025 05:41:05 +0000 Subject: [PATCH 143/155] chore(DB): import pending files Referenced commit(s): 79278aa5045872d3f2eea59fd5eb947615a240b0 --- .../rev_1757371089104032155.sql => db_world/2025_09_09_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757371089104032155.sql => db_world/2025_09_09_03.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1757371089104032155.sql b/data/sql/updates/db_world/2025_09_09_03.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1757371089104032155.sql rename to data/sql/updates/db_world/2025_09_09_03.sql index 01e6f402c..e1be42dfc 100644 --- a/data/sql/updates/pending_db_world/rev_1757371089104032155.sql +++ b/data/sql/updates/db_world/2025_09_09_03.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_09_02 -> 2025_09_09_03 -- DELETE FROM `spell_script_names` WHERE `spell_id`=48297; INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (48297, 'spell_handover_reins'); From 4c9bb353523c634a618b6bea8172b5692eab3839 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Tue, 9 Sep 2025 07:57:04 -0400 Subject: [PATCH 144/155] fix(DB/Creature): Correct visibility distance and addon auras of Fel Reaver. (#22847) --- data/sql/updates/pending_db_world/fel-reaver-addon.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/fel-reaver-addon.sql diff --git a/data/sql/updates/pending_db_world/fel-reaver-addon.sql b/data/sql/updates/pending_db_world/fel-reaver-addon.sql new file mode 100644 index 000000000..1df370574 --- /dev/null +++ b/data/sql/updates/pending_db_world/fel-reaver-addon.sql @@ -0,0 +1,2 @@ +UPDATE `creature_template_addon` SET `auras` = '19818 34623' WHERE `entry` = 18733; +UPDATE `creature_addon` SET `visibilityDistanceType` = 5, `auras` = '19818 34623' WHERE `guid` IN (67001, 203341); From 24ee9e248b1927ee6ed10afb819cc3f1a3b2fa06 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Sep 2025 11:58:10 +0000 Subject: [PATCH 145/155] chore(DB): import pending files Referenced commit(s): 4c9bb353523c634a618b6bea8172b5692eab3839 --- .../fel-reaver-addon.sql => db_world/2025_09_09_04.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/fel-reaver-addon.sql => db_world/2025_09_09_04.sql} (81%) diff --git a/data/sql/updates/pending_db_world/fel-reaver-addon.sql b/data/sql/updates/db_world/2025_09_09_04.sql similarity index 81% rename from data/sql/updates/pending_db_world/fel-reaver-addon.sql rename to data/sql/updates/db_world/2025_09_09_04.sql index 1df370574..9b1aba172 100644 --- a/data/sql/updates/pending_db_world/fel-reaver-addon.sql +++ b/data/sql/updates/db_world/2025_09_09_04.sql @@ -1,2 +1,3 @@ +-- DB update 2025_09_09_03 -> 2025_09_09_04 UPDATE `creature_template_addon` SET `auras` = '19818 34623' WHERE `entry` = 18733; UPDATE `creature_addon` SET `visibilityDistanceType` = 5, `auras` = '19818 34623' WHERE `guid` IN (67001, 203341); From 3a1f43db5c400da73f022d2f2fb8c1dcdb9c6615 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 9 Sep 2025 13:43:04 -0300 Subject: [PATCH 146/155] fix(Scripts/ScarletEnclave): fix crash in Persuasive Strike spell script (#22840) --- .../ScarletEnclave/chapter2.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp index a3325c58d..9c71aad73 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp @@ -1001,20 +1001,25 @@ class spell_chapter2_persuasive_strike : public SpellScript creature->AI()->Talk(SAY_PERSUADED3, 24s); creature->AI()->Talk(SAY_PERSUADED4, 32s); - creature->m_Events.AddEventAtOffset([creature, player] + ObjectGuid playerGuid = player->GetGUID(); + + creature->m_Events.AddEventAtOffset([creature, playerGuid] { - if (player) - sCreatureTextMgr->SendChat(creature, SAY_PERSUADED5, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_NEUTRAL, false, player); + if (Player* caster = ObjectAccessor::GetPlayer(*creature, playerGuid)) + sCreatureTextMgr->SendChat(creature, SAY_PERSUADED5, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_NORMAL, 0, TEAM_NEUTRAL, false, caster); }, 40s); - creature->m_Events.AddEventAtOffset([creature, player] + creature->m_Events.AddEventAtOffset([creature, playerGuid] { creature->AI()->Talk(SAY_PERSUADED6); - if (player) + + if (Player* caster = ObjectAccessor::GetPlayer(*creature, playerGuid)) { - Unit::Kill(player, creature); - player->GroupEventHappens(QUEST_HOW_TO_WIN_FRIENDS, creature); + Unit::Kill(caster, creature); + caster->GroupEventHappens(QUEST_HOW_TO_WIN_FRIENDS, creature); } + else + creature->KillSelf(); }, 48s); } else From e9c1be4648bb6a383e5b5c5cc600a6d0f0617935 Mon Sep 17 00:00:00 2001 From: v-mstrs <104088833+v-mstrs@users.noreply.github.com> Date: Tue, 9 Sep 2025 22:47:24 +0200 Subject: [PATCH 147/155] fix(DB/SAI) "The Earthen Oath" quest kill credit from 2 to 1 (#22667) Co-authored-by: sudlud --- .../updates/pending_db_world/rev_1754879934817034100.sql | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1754879934817034100.sql diff --git a/data/sql/updates/pending_db_world/rev_1754879934817034100.sql b/data/sql/updates/pending_db_world/rev_1754879934817034100.sql new file mode 100644 index 000000000..2eecb07f7 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1754879934817034100.sql @@ -0,0 +1,6 @@ +-- +UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 29978; + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 29984) AND (`source_type` = 0) AND (`id` = 0); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 29978) AND (`source_type` = 0); From 0fb904f82aca61f3d1fd78b600cfb1eaa6b83565 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 9 Sep 2025 20:48:25 +0000 Subject: [PATCH 148/155] chore(DB): import pending files Referenced commit(s): e9c1be4648bb6a383e5b5c5cc600a6d0f0617935 --- .../rev_1754879934817034100.sql => db_world/2025_09_09_05.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1754879934817034100.sql => db_world/2025_09_09_05.sql} (85%) diff --git a/data/sql/updates/pending_db_world/rev_1754879934817034100.sql b/data/sql/updates/db_world/2025_09_09_05.sql similarity index 85% rename from data/sql/updates/pending_db_world/rev_1754879934817034100.sql rename to data/sql/updates/db_world/2025_09_09_05.sql index 2eecb07f7..ff68f7f4b 100644 --- a/data/sql/updates/pending_db_world/rev_1754879934817034100.sql +++ b/data/sql/updates/db_world/2025_09_09_05.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_09_04 -> 2025_09_09_05 -- UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 29978; From 96330bbdd062c3428a67faa5530cf64f9f71a802 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Wed, 10 Sep 2025 07:41:52 -0700 Subject: [PATCH 149/155] fix(DB/Conditions): Horde Siege Tank cannot be entered without the required quest (#22856) --- .../sql/updates/pending_db_world/rev_1757453337252691864.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757453337252691864.sql diff --git a/data/sql/updates/pending_db_world/rev_1757453337252691864.sql b/data/sql/updates/pending_db_world/rev_1757453337252691864.sql new file mode 100644 index 000000000..e3c48d534 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757453337252691864.sql @@ -0,0 +1,5 @@ +-- +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 18) AND (`SourceGroup` = 25334) AND (`SourceEntry` IN (47917, 46598)) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 9) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 11652) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(18, 25334, 47917, 0, 0, 9, 0, 11652, 0, 0, 0, 0, 0, '', 'Horde Siege Tank requires player to be on quest The Plains of Nasam'), +(18, 25334, 46598, 0, 0, 9, 0, 11652, 0, 0, 0, 0, 0, '', 'Horde Siege Tank requires player to be on quest The Plains of Nasam'); From 9db27bc8651f1c6e503be34dadd918e14291db21 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Sep 2025 14:42:54 +0000 Subject: [PATCH 150/155] chore(DB): import pending files Referenced commit(s): 96330bbdd062c3428a67faa5530cf64f9f71a802 --- .../rev_1757453337252691864.sql => db_world/2025_09_10_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757453337252691864.sql => db_world/2025_09_10_00.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1757453337252691864.sql b/data/sql/updates/db_world/2025_09_10_00.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1757453337252691864.sql rename to data/sql/updates/db_world/2025_09_10_00.sql index e3c48d534..ca05e35c1 100644 --- a/data/sql/updates/pending_db_world/rev_1757453337252691864.sql +++ b/data/sql/updates/db_world/2025_09_10_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_09_05 -> 2025_09_10_00 -- DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 18) AND (`SourceGroup` = 25334) AND (`SourceEntry` IN (47917, 46598)) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 9) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 11652) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0); INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES From e64f3601e0def7105e6928a63d7063b09408c67b Mon Sep 17 00:00:00 2001 From: sudlud Date: Wed, 10 Sep 2025 16:55:31 +0200 Subject: [PATCH 151/155] fix(DB/Gameobject): Sniffed Values for 'Weapon Rack' spawns (#22854) --- .../rev_1757450791103483700.sql | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757450791103483700.sql diff --git a/data/sql/updates/pending_db_world/rev_1757450791103483700.sql b/data/sql/updates/pending_db_world/rev_1757450791103483700.sql new file mode 100644 index 000000000..6398a94dd --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757450791103483700.sql @@ -0,0 +1,50 @@ +-- Update gameobject 'Weapon Rack' with sniffed values +-- updated spawns +DELETE FROM `gameobject` WHERE (`id` IN (181627, 183269, 183991, 105172, 105171, 105170, 105169, 188659)) AND (`guid` IN (11420, 24123, 24845, 24846, 45165, 45166, 45167, 45168, 61365, 61366, 61367, 61368, 61369, 61370, 61371, 61372, 61373, 61374, 61375, 61376, 61377, 61378, 61379, 61380, 61381, 61382, 61383, 61384, 61385, 61386)); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(11420, 181627, 0, 0, 0, 1, 1, -6335.33544921875, -3115.09375, 299.723052978515625, 0.907570242881774902, 0, 0, 0.438370704650878906, 0.898794233798980712, 120, 255, 1, "", 48120, NULL), +(24123, 183269, 530, 0, 0, 1, 1, 2228.34716796875, 2312.203125, 89.11359405517578125, 2.321285009384155273, -0.00742387771606445, -0.15768909454345703, 0.902186393737792968, 0.401420950889587402, 120, 255, 1, "", 45704, NULL), +(24845, 183991, 530, 0, 0, 1, 1, -596.56427001953125, 2914.64013671875, 59.21495437622070312, 3.543023586273193359, 0, 0, -0.97992420196533203, 0.199370384216308593, 120, 255, 1, "", 45854, NULL), +(24846, 183991, 530, 0, 0, 1, 1, -584.88446044921875, 2896.530029296875, 59.20400619506835937, 3.700104713439941406, 0, 0, -0.96126079559326171, 0.275640487670898437, 120, 255, 1, "", 45854, NULL), +(45165, 105172, 0, 0, 0, 1, 1, 3023.028564453125, 653.81402587890625, 75.34989166259765625, 1.553341388702392578, 0, 0, 0.700908660888671875, 0.713251054286956787, 120, 255, 1, "", 46779, NULL), +(45166, 105171, 0, 0, 0, 1, 1, 3019.9033203125, 688.79742431640625, 66.45070648193359375, 4.747295856475830078, 0, 0, -0.69465827941894531, 0.719339847564697265, 120, 255, 1, "", 46779, NULL), +(45167, 105170, 0, 0, 0, 1, 1, 3063.673583984375, 697.9215087890625, 66.45069122314453125, 3.211419343948364257, 0, 0, -0.9993906021118164, 0.034906134009361267, 120, 255, 1, "", 46779, NULL), +(45168, 105169, 0, 0, 0, 1, 1, 3058.616455078125, 653.5849609375, 58.1085205078125, 3.996806621551513671, 0, 0, -0.90996074676513671, 0.414694398641586303, 120, 255, 1, "", 46779, NULL), +(61365, 188659, 571, 0, 0, 1, 1, 2953.047119140625, -451.505218505859375, 140.7652130126953125, 5.794494152069091796, 0, 0, -0.24192142486572265, 0.970295846462249755, 120, 255, 1, "", 46158, NULL), +(61366, 188659, 571, 0, 0, 1, 1, 2832.775390625, -279.4259033203125, 136.0123291015625, 2.426007747650146484, 0, 0, 0.936672210693359375, 0.350207358598709106, 120, 255, 1, "", 45942, NULL), +(61367, 188659, 571, 0, 0, 1, 1, 2741.550537109375, -114.055442810058593, 115.7203216552734375, 6.195919513702392578, 0, 0, -0.04361915588378906, 0.999048233032226562, 120, 255, 1, "", 46158, NULL), +(61368, 188659, 571, 0, 0, 1, 1, 2834.82958984375, -531.0296630859375, 121.3561477661132812, 4.48549652099609375, 0, 0, -0.7826080322265625, 0.622514784336090087, 120, 255, 1, "", 45942, NULL), +(61369, 188659, 571, 0, 0, 1, 1, 2782.08203125, -188.334426879882812, 139.139404296875, 3.577930212020874023, 0, 0, -0.97629547119140625, 0.216442063450813293, 120, 255, 1, "", 46158, NULL), +(61370, 188659, 571, 0, 0, 1, 1, 2809.780029296875, -324.545013427734375, 130.2048797607421875, 3.78736734390258789, 0, 0, -0.94832324981689453, 0.317305892705917358, 120, 255, 1, "", 46158, NULL), +(61371, 188659, 571, 0, 0, 1, 1, 2928.7626953125, -353.397918701171875, 112.4615936279296875, 2.932138919830322265, 0, 0, 0.994521141052246093, 0.104535527527332305, 120, 255, 1, "", 45942, NULL), +(61372, 188659, 571, 0, 0, 1, 1, 2878.55908203125, -431.913116455078125, 118.3675765991210937, 3.9793548583984375, 0, 0, -0.9135446548461914, 0.406738430261611938, 120, 255, 1, "", 45942, NULL), +(61373, 188659, 571, 0, 0, 1, 1, 2883.435546875, -373.7969970703125, 112.4618301391601562, 3.9793548583984375, 0, 0, -0.9135446548461914, 0.406738430261611938, 120, 255, 1, "", 46158, NULL), +(61374, 188659, 571, 0, 0, 1, 1, 2858.78125, -276.4066162109375, 114.0344467163085937, 1.553341388702392578, 0, 0, 0.700908660888671875, 0.713251054286956787, 120, 255, 1, "", 45942, NULL), +(61375, 188659, 571, 0, 0, 1, 1, 2864.294189453125, -278.00445556640625, 122.8568801879882812, 1.570795774459838867, 0, 0, 0.707106590270996093, 0.707106947898864746, 120, 255, 1, "", 45942, NULL), +(61376, 188659, 571, 0, 0, 1, 1, 2883.530029296875, -296.61376953125, 114.0345230102539062, 3.071766138076782226, 0, 0, 0.999390602111816406, 0.034906134009361267, 120, 255, 1, "", 46158, NULL), +(61377, 188659, 571, 0, 0, 1, 1, 2891.976318359375, -294.088714599609375, 122.85626220703125, 3.106652259826660156, 0, 0, 0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 45942, NULL), +(61378, 188659, 571, 0, 0, 1, 1, 2889.322998046875, -290.53131103515625, 106.8801422119140625, 3.106652259826660156, 0, 0, 0.999847412109375, 0.017469281330704689, 120, 255, 1, "", 45942, NULL), +(61379, 188659, 571, 0, 0, 1, 1, 2901.346923828125, -320.123565673828125, 114.0344924926757812, 1.535889506340026855, 0, 0, 0.694658279418945312, 0.719339847564697265, 120, 255, 1, "", 45942, NULL), +(61380, 188659, 571, 0, 0, 1, 1, 2912.576416015625, -281.989471435546875, 138.0604248046875, 3.124123096466064453, 0, 0, 0.99996185302734375, 0.008734640665352344, 120, 255, 1, "", 45942, NULL), +(61381, 188659, 571, 0, 0, 1, 1, 2968.82080078125, -444.152923583984375, 125.9091415405273437, 1.780233979225158691, 0, 0, 0.7771453857421875, 0.629321098327636718, 120, 255, 1, "", 45942, NULL), +(61382, 188659, 571, 0, 0, 1, 1, 2942.835205078125, -348.2596435546875, 114.6573333740234375, 4.555310726165771484, 0, 0, -0.76040554046630859, 0.649448513984680175, 120, 255, 1, "", 45942, NULL), +(61383, 188659, 571, 0, 0, 1, 1, 2929.404052734375, -332.448272705078125, 113.43121337890625, 4.572763919830322265, 0, 0, -0.75470924377441406, 0.656059443950653076, 120, 255, 1, "", 45942, NULL), +(61384, 188659, 571, 0, 0, 1, 1, 2757.970458984375, -180.418655395507812, 138.998748779296875, 0.959929943084716796, 0, 0, 0.461748123168945312, 0.887011110782623291, 120, 255, 1, "", 46158, NULL), +(61385, 188659, 571, 0, 0, 1, 1, 2697.780517578125, -200.24945068359375, 140.154632568359375, 1.570795774459838867, 0, 0, 0.707106590270996093, 0.707106947898864746, 120, 255, 1, "", 46158, NULL), +(61386, 188659, 571, 0, 0, 1, 1, 2731.54833984375, -241.892257690429687, 141.5568389892578125, 2.199114561080932617, 0, 0, 0.8910064697265625, 0.453990638256072998, 120, 255, 1, "", 46158, NULL); + +-- new spawns +DELETE FROM `gameobject` WHERE (`id` IN (188426, 188659)) AND (`guid` BETWEEN 939 AND 947); +INSERT INTO `gameobject` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `position_x`, `position_y`, `position_z`, `orientation`, `rotation0`, `rotation1`, `rotation2`, `rotation3`, `spawntimesecs`, `animprogress`, `state`, `ScriptName`, `VerifiedBuild`, `Comment`) VALUES +(939, 188426, 571, 0, 0, 1, 1, 2608.928955078125, 5263.6396484375, 39.4164886474609375, 1.239183306694030761, 0, 0, 0.580702781677246093, 0.814115643501281738, 120, 255, 1, "", 49345, NULL), +(940, 188426, 571, 0, 0, 1, 1, 2611.096923828125, 5260.75732421875, 39.40889358520507812, 2.164205789566040039, 0, 0, 0.882946968078613281, 0.469472706317901611, 120, 255, 1, "", 49345, NULL), +(941, 188426, 571, 0, 0, 1, 1, 2614.411376953125, 5258.751953125, 39.40840911865234375, 3.665196180343627929, 0, 0, -0.96592521667480468, 0.258821308612823486, 120, 255, 1, "", 49345, NULL), +(942, 188426, 571, 0, 0, 1, 1, 2615.94189453125, 5267.6845703125, 39.4207000732421875, 5.462882041931152343, 0, 0, -0.39874839782714843, 0.917060375213623046, 120, 255, 1, "", 49345, NULL), +(943, 188426, 571, 0, 0, 1, 1, 2623.84375, 5255.21875, 38.28279876708984375, 0.750490784645080566, 0, 0, 0.3665008544921875, 0.93041771650314331, 120, 255, 1, "", 49345, NULL), +(944, 188659, 571, 0, 0, 1, 1, 2444.703125, -394.670135498046875, 7.966825008392333984, 0.034906249493360519, 0, 0, 0.017452239990234375, 0.999847710132598876, 120, 255, 1, "", 46158, NULL), +(945, 188659, 571, 0, 0, 1, 1, 2477.215576171875, -350.638916015625, 1.422237038612365722, 0.820303261280059814, 0, 0, 0.398748397827148437, 0.917060375213623046, 120, 255, 1, "", 46158, NULL), +(946, 188659, 571, 0, 0, 1, 1, 2622.64111328125, -264.823455810546875, 2.252971887588500976, 4.066620349884033203, 0, 0, -0.89493370056152343, 0.44619917869567871, 120, 255, 1, "", 45854, NULL), +(947, 188659, 571, 0, 0, 1, 1, 2684.013427734375, -485.452423095703125, 50.89562606811523437, 3.595378875732421875, 0, 0, -0.97437000274658203, 0.224951311945915222, 120, 255, 1, "", 45854, NULL); + +-- remaining spawns (no sniffed values available) +-- (`guid` IN (99937)) From d03024089135fb83cd7d4d49c5306a5f6b5a99f1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Sep 2025 14:56:33 +0000 Subject: [PATCH 152/155] chore(DB): import pending files Referenced commit(s): e64f3601e0def7105e6928a63d7063b09408c67b --- .../rev_1757450791103483700.sql => db_world/2025_09_10_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757450791103483700.sql => db_world/2025_09_10_01.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1757450791103483700.sql b/data/sql/updates/db_world/2025_09_10_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1757450791103483700.sql rename to data/sql/updates/db_world/2025_09_10_01.sql index 6398a94dd..60a729439 100644 --- a/data/sql/updates/pending_db_world/rev_1757450791103483700.sql +++ b/data/sql/updates/db_world/2025_09_10_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_10_00 -> 2025_09_10_01 -- Update gameobject 'Weapon Rack' with sniffed values -- updated spawns DELETE FROM `gameobject` WHERE (`id` IN (181627, 183269, 183991, 105172, 105171, 105170, 105169, 188659)) AND (`guid` IN (11420, 24123, 24845, 24846, 45165, 45166, 45167, 45168, 61365, 61366, 61367, 61368, 61369, 61370, 61371, 61372, 61373, 61374, 61375, 61376, 61377, 61378, 61379, 61380, 61381, 61382, 61383, 61384, 61385, 61386)); From b2283b737a730d0336d982c1dc37edfabd646c6c Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 10 Sep 2025 19:46:55 -0300 Subject: [PATCH 153/155] =?UTF-8?q?fix(Scripts/Stormpeaks):=20Fix=20Feed?= =?UTF-8?q?=20Stormcrest=20Eagle=20spell=20target=20condi=E2=80=A6=20(#228?= =?UTF-8?q?48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rev_1757394793226041500.sql | 13 ++++++++++ .../scripts/Northrend/zone_storm_peaks.cpp | 26 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1757394793226041500.sql diff --git a/data/sql/updates/pending_db_world/rev_1757394793226041500.sql b/data/sql/updates/pending_db_world/rev_1757394793226041500.sql new file mode 100644 index 000000000..4ea3e3c12 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1757394793226041500.sql @@ -0,0 +1,13 @@ +-- +DELETE FROM `creature_template_addon` WHERE `entry`=29854; +INSERT INTO `creature_template_addon` (`entry`,`path_id`,`bytes1`,`mount`,`auras`) VALUES +(29854,0,1,0, ''); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13) AND (`SourceGroup` = 1) AND (`SourceEntry` = 56393); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 56393, 0, 0, 31, 0, 3, 29854, 0, 0, 0, 0, '', 'Feed Stormcrest Eagle target Stormcrest Eagle'), +(13, 1, 56393, 0, 0, 1, 0, 56393, 0, 0, 1, 0, 0, '', 'Feed Stormcrest Eagle target must not have Feed Stormcrest Eagle aura'); + +DELETE FROM `spell_script_names` WHERE `spell_id` = 56393; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(56393, 'spell_feed_stormcrest_eagle'); diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index ec2da77ea..35fd65e31 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -1167,6 +1167,31 @@ public: return new npc_vehicle_d16_propelled_deliveryAI(creature); } }; + +enum StormcrestEagle +{ + NPC_STORMCREST_EAGLE = 29854 +}; + +// 56393 - Feed Stormcrest Eagle +class spell_feed_stormcrest_eagle : public SpellScript +{ + PrepareSpellScript(spell_feed_stormcrest_eagle); + + SpellCastResult CheckCast() + { + if (GetCaster()->FindNearestCreature(NPC_STORMCREST_EAGLE, 15.0f, true)) + return SPELL_CAST_OK; + + return SPELL_FAILED_BAD_TARGETS; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_feed_stormcrest_eagle::CheckCast); + } +}; + void AddSC_storm_peaks() { new npc_frosthound(); @@ -1183,4 +1208,5 @@ void AddSC_storm_peaks() RegisterSpellScript(spell_close_rift_aura); new npc_vehicle_d16_propelled_delivery(); RegisterSpellScript(spell_q12823_remove_collapsing_cave_aura); + RegisterSpellScript(spell_feed_stormcrest_eagle); } From 32573e8f5471ba2f471eecff20df19526e31f17b Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 10 Sep 2025 19:47:24 -0300 Subject: [PATCH 154/155] =?UTF-8?q?fix(Core/Formations):=20Prevent=20forma?= =?UTF-8?q?tion=20members=20from=20moving=20when=20cont=E2=80=A6=20(#22859?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/game/Entities/Creature/CreatureGroups.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/server/game/Entities/Creature/CreatureGroups.cpp b/src/server/game/Entities/Creature/CreatureGroups.cpp index c4ef53ae6..c603b51da 100644 --- a/src/server/game/Entities/Creature/CreatureGroups.cpp +++ b/src/server/game/Entities/Creature/CreatureGroups.cpp @@ -361,8 +361,9 @@ void CreatureGroup::LeaderMoveTo(float x, float y, float z, uint32 move_type) if (member == m_leader || !member->IsAlive() || member->GetVictim() || !pFormationInfo.HasGroupFlag(std::underlying_type_t(GroupAIFlags::GROUP_AI_FLAG_FOLLOW_LEADER))) continue; - // Xinef: If member is stunned / rooted etc don't allow to move him - if (member->HasUnitState(UNIT_STATE_NOT_MOVE)) + // If member is stunned / rooted etc don't allow to move him + // Or if charmed/controlled + if (member->HasUnitState(UNIT_STATE_NOT_MOVE) || member->isPossessed() || member->HasUnitFlag(UNIT_FLAG_PLAYER_CONTROLLED)) continue; // Xinef: this should be automatized, if turn angle is greater than PI/2 (90�) we should swap formation angle From 9fc8e11810245c9aa1442dc2aebef1d3eb3dbf78 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 10 Sep 2025 22:48:39 +0000 Subject: [PATCH 155/155] chore(DB): import pending files Referenced commit(s): 32573e8f5471ba2f471eecff20df19526e31f17b --- .../rev_1757394793226041500.sql => db_world/2025_09_10_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1757394793226041500.sql => db_world/2025_09_10_02.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1757394793226041500.sql b/data/sql/updates/db_world/2025_09_10_02.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1757394793226041500.sql rename to data/sql/updates/db_world/2025_09_10_02.sql index 4ea3e3c12..04d4b34c3 100644 --- a/data/sql/updates/pending_db_world/rev_1757394793226041500.sql +++ b/data/sql/updates/db_world/2025_09_10_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_09_10_01 -> 2025_09_10_02 -- DELETE FROM `creature_template_addon` WHERE `entry`=29854; INSERT INTO `creature_template_addon` (`entry`,`path_id`,`bytes1`,`mount`,`auras`) VALUES