From 42c23a05a0654257597988d992b57292e45a8ec2 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Fri, 24 Apr 2026 17:42:24 -0400 Subject: [PATCH 01/92] fix(Scripts): Use proper gendered Indisposed spell for quest Doing Your Duty. (#25575) --- src/server/scripts/World/go_scripts.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 902193daf..75970cc8e 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -1559,7 +1559,8 @@ public: enum AmberpineOuthouse { QUEST_DOING_YOUR_DUTY = 12227, - SPELL_INDISPOSED = 53017, + SPELL_INDISPOSED_MALE = 48323, + SPELL_INDISPOSED_FEMALE = 53017, SPELL_INDISPOSED_II = 48324, SPELL_INDISPOSED_III = 48341, GOSSIP_OUTHOUSE_INUSE = 12775, @@ -1592,7 +1593,17 @@ public: if (action == GOSSIP_ACTION_INFO_DEF + 1) { CloseGossipMenuFor(player); - player->CastSpell(player, SPELL_INDISPOSED); + switch (player->getGender()) + { + case GENDER_FEMALE: + player->CastSpell(player, SPELL_INDISPOSED_FEMALE); + break; + case GENDER_MALE: + player->CastSpell(player, SPELL_INDISPOSED_MALE); + break; + default: + break; + } player->CastSpell(player, SPELL_INDISPOSED_II); player->CastSpell(player, SPELL_INDISPOSED_III); return true; From 75d7b195e822cfc245c21179d087847b16a69aa3 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 25 Apr 2026 02:04:51 +0200 Subject: [PATCH 02/92] fix(DB/Cooldown): Remove CD override Salvaged Demolisher's Hurl Boulder (#25579) --- data/sql/updates/pending_db_world/rev_1777073605799213829.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777073605799213829.sql diff --git a/data/sql/updates/pending_db_world/rev_1777073605799213829.sql b/data/sql/updates/pending_db_world/rev_1777073605799213829.sql new file mode 100644 index 000000000..f2184b167 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777073605799213829.sql @@ -0,0 +1,2 @@ +-- +DELETE `spell_cooldown_overrides` WHERE `Id`=62306; From 47007bd9a59364de6db3eabe125075d1e8063558 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Apr 2026 00:06:58 +0000 Subject: [PATCH 03/92] chore(DB): import pending files Referenced commit(s): 75d7b195e822cfc245c21179d087847b16a69aa3 --- .../rev_1777073605799213829.sql => db_world/2026_04_25_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777073605799213829.sql => db_world/2026_04_25_00.sql} (55%) diff --git a/data/sql/updates/pending_db_world/rev_1777073605799213829.sql b/data/sql/updates/db_world/2026_04_25_00.sql similarity index 55% rename from data/sql/updates/pending_db_world/rev_1777073605799213829.sql rename to data/sql/updates/db_world/2026_04_25_00.sql index f2184b167..338c64d2e 100644 --- a/data/sql/updates/pending_db_world/rev_1777073605799213829.sql +++ b/data/sql/updates/db_world/2026_04_25_00.sql @@ -1,2 +1,3 @@ +-- DB update 2026_04_24_02 -> 2026_04_25_00 -- DELETE `spell_cooldown_overrides` WHERE `Id`=62306; From 2d09787d454e60d59c34b178ee2caeff93b54ad1 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 25 Apr 2026 02:11:33 +0200 Subject: [PATCH 04/92] =?UTF-8?q?fixup!=20fix(DB/Cooldown):=20Remove=20CD?= =?UTF-8?q?=20override=20Salvaged=20Demolisher's=20Hur=E2=80=A6=20(#25580)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/sql/updates/db_world/2026_04_25_00.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/sql/updates/db_world/2026_04_25_00.sql b/data/sql/updates/db_world/2026_04_25_00.sql index 338c64d2e..2a06f464d 100644 --- a/data/sql/updates/db_world/2026_04_25_00.sql +++ b/data/sql/updates/db_world/2026_04_25_00.sql @@ -1,3 +1,3 @@ -- DB update 2026_04_24_02 -> 2026_04_25_00 -- -DELETE `spell_cooldown_overrides` WHERE `Id`=62306; +DELETE FROM `spell_cooldown_overrides` WHERE `Id`=62306; From 096db2f45bac474cb0d8d99198644d6956c62039 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Sat, 25 Apr 2026 07:12:50 -0500 Subject: [PATCH 05/92] fix(Core/Quest): award kill credit when pet proxies as reward source (#25553) --- src/server/game/Entities/Player/Player.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 389a2edae..275be9437 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -12754,7 +12754,9 @@ void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewar if (!pRewardSource) return; - ObjectGuid creature_guid = (pRewardSource->IsCreature()) ? pRewardSource->GetGUID() : ObjectGuid::Empty; + ObjectGuid creature_guid; + if (pRewardSource->IsCreature() && pRewardSource->GetEntry() == creature_id) + creature_guid = pRewardSource->GetGUID(); // prepare data for near group iteration if (Group* group = GetGroup()) From 92a9146f112d9815e319a1ad844aed5f7fb034b3 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 25 Apr 2026 14:19:57 +0200 Subject: [PATCH 06/92] fix(Scripts/Northrend): Drakkensryd reliable passenger install (#25387) Co-authored-by: Andrew <47818697+Nyeriah@users.noreply.github.com> Co-authored-by: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> --- src/server/scripts/Northrend/zone_storm_peaks.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index 853043860..b8c6bec5e 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -561,7 +561,10 @@ struct npc_hyldsmeet_protodrake : public CreatureAI //! We need to manually reinstall accessories because the vehicle itself is friendly to players, //! so EnterEvadeMode is never triggered. The accessory on the other hand is hostile and killable. Vehicle* vehicleKit = me->GetVehicleKit(); - if (_accessoryRespawnTimer && _accessoryRespawnTimer <= diff && vehicleKit) + if (!vehicleKit || !_accessoryRespawnTimer) + return; + + if (_accessoryRespawnTimer <= diff) { vehicleKit->InstallAllAccessories(true); _accessoryRespawnTimer = 0; From 9d834f0819ffb7ab994173bc4c07682ef69237b1 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 25 Apr 2026 12:27:39 -0300 Subject: [PATCH 07/92] fix(Scripts/Quest): Drake Hunt drake flies away on subdue (#25589) Co-authored-by: Claude Opus 4.7 --- src/server/scripts/Northrend/zone_borean_tundra.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index ce6a3a79b..c3fa1bac4 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -23,6 +23,7 @@ #include "ScriptedEscortAI.h" #include "ScriptedFollowerAI.h" #include "ScriptedGossip.h" +#include "SmartAI.h" #include "SpellAuras.h" #include "SpellInfo.h" #include "SpellScript.h" @@ -55,6 +56,8 @@ class spell_q11919_q11940_drake_hunt_aura : public AuraScript Creature* owner = GetOwner()->ToCreature(); owner->RemoveAllAurasExceptType(SPELL_AURA_DUMMY); + if (SmartAI* ai = CAST_AI(SmartAI, owner->AI())) + ai->SetEvadeDisabled(true); owner->CombatStop(true); owner->GetThreatMgr().ClearAllThreat(); owner->GetMotionMaster()->Clear(false); From 504b172f9192682413d4b86feb35e7105c27cb78 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 25 Apr 2026 13:19:19 -0300 Subject: [PATCH 08/92] fix(DB/Creature): Ironhand Guardian should not enter combat (#25590) Co-authored-by: Claude Opus 4.7 --- data/sql/updates/pending_db_world/rev_1777129916924602400.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777129916924602400.sql diff --git a/data/sql/updates/pending_db_world/rev_1777129916924602400.sql b/data/sql/updates/pending_db_world/rev_1777129916924602400.sql new file mode 100644 index 000000000..624a093d5 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777129916924602400.sql @@ -0,0 +1,2 @@ +-- Ironhand Guardian (entry 8982) should never enter combat +UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 0x2000 WHERE `entry` = 8982; From 587239a29bf0673b38eab59d68b3cdefb730eda1 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Apr 2026 16:20:23 +0000 Subject: [PATCH 09/92] chore(DB): import pending files Referenced commit(s): 504b172f9192682413d4b86feb35e7105c27cb78 --- .../rev_1777129916924602400.sql => db_world/2026_04_25_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777129916924602400.sql => db_world/2026_04_25_01.sql} (77%) diff --git a/data/sql/updates/pending_db_world/rev_1777129916924602400.sql b/data/sql/updates/db_world/2026_04_25_01.sql similarity index 77% rename from data/sql/updates/pending_db_world/rev_1777129916924602400.sql rename to data/sql/updates/db_world/2026_04_25_01.sql index 624a093d5..b73fef093 100644 --- a/data/sql/updates/pending_db_world/rev_1777129916924602400.sql +++ b/data/sql/updates/db_world/2026_04_25_01.sql @@ -1,2 +1,3 @@ +-- DB update 2026_04_25_00 -> 2026_04_25_01 -- Ironhand Guardian (entry 8982) should never enter combat UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 0x2000 WHERE `entry` = 8982; From 0db7c2d01b08128173f3fc869de61516b7e42e75 Mon Sep 17 00:00:00 2001 From: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> Date: Sat, 25 Apr 2026 18:30:07 +0100 Subject: [PATCH 10/92] fix(DB/SAI) - "Apothecary Lysander" and "Apothecary Ravien" updated (#25568) Co-authored-by: FlyingArowana Co-authored-by: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> --- .../rev_1776981524979657200.sql | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1776981524979657200.sql diff --git a/data/sql/updates/pending_db_world/rev_1776981524979657200.sql b/data/sql/updates/pending_db_world/rev_1776981524979657200.sql new file mode 100644 index 000000000..46d7e9a27 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1776981524979657200.sql @@ -0,0 +1,38 @@ +-- Update Apothecary Ravien SmartAI +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23782; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 23782); +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 +(23782, 0, 0, 0, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 1, 23782, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Ravien - On Just Summoned - Start Waypoint Path 23782'), +(23782, 0, 1, 2, 40, 0, 100, 512, 6, 23782, 0, 0, 0, 0, 54, 60000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Ravien - On Point 6 of Path 23782 Reached - Pause Waypoint'), +(23782, 0, 2, 3, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 10, 101906, 24126, 0, 0, 0, 0, 0, 0, 'Apothecary Ravien - On Point 6 of Path 23782 Reached - Set Orientation Closest Creature \'Apothecary Lysander\''), +(23782, 0, 3, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 223, 10, 0, 0, 0, 0, 0, 10, 101906, 24126, 0, 0, 0, 0, 0, 0, 'Apothecary Ravien - On Point 6 of Path 23782 Reached - Do Action ID 10'), +(23782, 0, 4, 0, 40, 0, 100, 512, 7, 23782, 0, 0, 0, 0, 223, 11, 0, 0, 0, 0, 0, 10, 101906, 24126, 0, 0, 0, 0, 0, 0, 'Apothecary Ravien - On Point 7 of Path 23782 Reached - Do Action ID 11'), +(23782, 0, 5, 0, 40, 0, 100, 512, 18, 23782, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Ravien - On Point 18 of Path 23782 Reached - Despawn Instant'); + +-- Update Apothecary Lysander SmartAI +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 24126; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24126); +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 +(24126, 0, 0, 0, 20, 1, 100, 0, 11170, 0, 0, 0, 0, 0, 80, 2412600, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - On Quest \'Test at Sea\' Finished - Run Script (Phase 1)'), +(24126, 0, 1, 0, 72, 0, 100, 0, 10, 0, 0, 0, 0, 0, 80, 2412601, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - On Action 10 Done - Run Script'), +(24126, 0, 2, 0, 72, 0, 100, 0, 11, 0, 0, 0, 0, 0, 22, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - On Action 11 Done - Set Event Phase 1'), +(24126, 0, 3, 0, 1, 1, 100, 0, 0, 30000, 30000, 90000, 0, 0, 1, 6, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Out of Combat - Say Line 6 (Phase 1)'), +(24126, 0, 4, 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, 'Apothecary Lysander - On Reset - Set Event Phase 1'); + +-- Set Action Lists +DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (2412600, 2412601)); +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 +(2412600, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 12, 23782, 1, 90000, 0, 0, 0, 8, 0, 0, 0, 0, 1965.13, -6126.74, 25.6126, 3.27167, 'Apothecary Lysander - Actionlist - Summon Creature \'Apothecary Ravien\''), +(2412600, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Set Event Phase 0'), +(2412601, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 19, 23782, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 0 (Apothecary Ravien)'), +(2412601, 9, 1, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 0'), +(2412601, 9, 2, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 204, 23782, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 1 (Apothecary Ravien)'), +(2412601, 9, 3, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 1'), +(2412601, 9, 4, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 204, 23782, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 2 (Apothecary Ravien)'), +(2412601, 9, 5, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 2'), +(2412601, 9, 6, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 3'), +(2412601, 9, 7, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 4'), +(2412601, 9, 8, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 5, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 5'), +(2412601, 9, 9, 0, 0, 0, 100, 0, 6000, 6000, 0, 0, 0, 0, 1, 3, 0, 0, 0, 0, 0, 204, 23782, 0, 0, 0, 0, 0, 0, 0, 'Apothecary Lysander - Actionlist - Say Line 3 (Apothecary Ravien)'); From 240876556aad5b439a5133ea2e3818b1427a99e8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Apr 2026 17:31:16 +0000 Subject: [PATCH 11/92] chore(DB): import pending files Referenced commit(s): 0db7c2d01b08128173f3fc869de61516b7e42e75 --- .../rev_1776981524979657200.sql => db_world/2026_04_25_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1776981524979657200.sql => db_world/2026_04_25_02.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1776981524979657200.sql b/data/sql/updates/db_world/2026_04_25_02.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1776981524979657200.sql rename to data/sql/updates/db_world/2026_04_25_02.sql index 46d7e9a27..958c20d50 100644 --- a/data/sql/updates/pending_db_world/rev_1776981524979657200.sql +++ b/data/sql/updates/db_world/2026_04_25_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_25_01 -> 2026_04_25_02 -- Update Apothecary Ravien SmartAI UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 23782; From c6c308aed7755e1bc3c4b4c74f0ed758877f7d45 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 25 Apr 2026 18:20:41 -0300 Subject: [PATCH 12/92] fix(Core/Creature): Fix Wintergrasp mobs not respawning (#25583) --- src/server/game/Entities/Creature/Creature.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index ca6c60323..206bc6869 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2054,10 +2054,6 @@ void Creature::Respawn(bool force) if (getDeathState() == DeathState::Dead) { - // TempSummons (no m_spawnId) shouldn't be resurrected here; TempSummon::Update UnSummons them on the next tick once deathState is Dead. - if (!m_spawnId && !force) - return; - if (m_spawnId) { GetMap()->RemoveCreatureRespawnTime(m_spawnId); From 2209e08481be3aa32b855fe62c531a6dbe267566 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 25 Apr 2026 23:21:07 +0200 Subject: [PATCH 13/92] fix(Scripts/Northrend): fix console errors due to Freya WP scripts (#25581) --- .../rev_1777077414077078712.sql | 4 +++ .../Northrend/Ulduar/Ulduar/boss_freya.cpp | 35 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777077414077078712.sql diff --git a/data/sql/updates/pending_db_world/rev_1777077414077078712.sql b/data/sql/updates/pending_db_world/rev_1777077414077078712.sql new file mode 100644 index 000000000..ce41d817a --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777077414077078712.sql @@ -0,0 +1,4 @@ +-- +-- Remove AoE spell cast to entry, that may sometimes not be in range. +DELETE FROM `waypoint_scripts` WHERE `id` IN (488, 489, 490) AND `guid` IN (891, 892, 893); +UPDATE `waypoint_data` SET `action` = 0 WHERE `id` = 1365540 AND `point` IN (4, 10, 18); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp index cb3668d45..3be8247d1 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp @@ -48,6 +48,11 @@ enum FreyaSpells SPELL_UNSTABLE_SUN_FREYA_DAMAGE = 62451, SPELL_UNSTABLE_SUN_VISUAL = 62216, + // CHANNEL STALKER VISUALS + SPELL_FREYA_DUMMY_GREEN = 63295, + SPELL_FREYA_DUMMY_YELLOW = 63292, + SPELL_FREYA_DUMMY_BLUE = 63294, + // ELDERS SPELL_DRAINED_OF_POWER = 62467, SPELL_STONEBARK_ESSENCE = 62483, @@ -164,6 +169,7 @@ enum Texts enum FreyaNPCs { NPC_NATURE_BOMB = 34129, + NPC_CHANNEL_STALKER_FREYA = 33575, NPC_IRON_ROOT_TRIGGER = 33088, NPC_FREYA_UNSTABLE_SUN_BEAM = 33170, NPC_UNSTABLE_SUN_BRIGHTLEAF = 33050, // 10 SECS? @@ -196,6 +202,10 @@ enum Misc DATA_BACK_TO_NATURE = 2, CRITERIA_LUMBERJACKED = 21686, + + WAYPOINT_GREEN = 4, // path: 1365540 + WAYPOINT_YELLOW = 10, + WAYPOINT_BLUE = 18, }; struct boss_freya : public BossAI @@ -591,6 +601,31 @@ struct boss_freya : public BossAI { return me->GetPositionX() < 2135.0f; } + + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != WAYPOINT_MOTION_TYPE) + return; + + Unit* target = GetClosestCreatureWithEntry(me, NPC_CHANNEL_STALKER_FREYA, 30.0f); + if (!target) + return; + + switch (pointId) + { + case WAYPOINT_GREEN: + DoCast(target, SPELL_FREYA_DUMMY_GREEN); + break; + case WAYPOINT_YELLOW: + DoCast(target, SPELL_FREYA_DUMMY_YELLOW); + break; + case WAYPOINT_BLUE: + DoCast(target, SPELL_FREYA_DUMMY_BLUE); + break; + default: + break; + } + } }; struct boss_freya_elder_stonebark : public ScriptedAI From 1eb781bc75cf5241be6ee9085afd8540438d3016 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Apr 2026 21:21:49 +0000 Subject: [PATCH 14/92] chore(DB): import pending files Referenced commit(s): c6c308aed7755e1bc3c4b4c74f0ed758877f7d45 --- .../rev_1777077414077078712.sql => db_world/2026_04_25_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777077414077078712.sql => db_world/2026_04_25_03.sql} (85%) diff --git a/data/sql/updates/pending_db_world/rev_1777077414077078712.sql b/data/sql/updates/db_world/2026_04_25_03.sql similarity index 85% rename from data/sql/updates/pending_db_world/rev_1777077414077078712.sql rename to data/sql/updates/db_world/2026_04_25_03.sql index ce41d817a..09fc17a08 100644 --- a/data/sql/updates/pending_db_world/rev_1777077414077078712.sql +++ b/data/sql/updates/db_world/2026_04_25_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_25_02 -> 2026_04_25_03 -- -- Remove AoE spell cast to entry, that may sometimes not be in range. DELETE FROM `waypoint_scripts` WHERE `id` IN (488, 489, 490) AND `guid` IN (891, 892, 893); From 6f31825b132dd67d328f62609aede30eeca15ade Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sun, 26 Apr 2026 00:50:29 +0200 Subject: [PATCH 15/92] fix(DB/SAI): Edit Eye of Acherus SmartAI. (#25598) --- .../pending_db_world/rev_1777153480.sql | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777153480.sql diff --git a/data/sql/updates/pending_db_world/rev_1777153480.sql b/data/sql/updates/pending_db_world/rev_1777153480.sql new file mode 100644 index 000000000..9bb7ed451 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777153480.sql @@ -0,0 +1,21 @@ + +-- Update General SAI. +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28511; + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28511) AND (`source_type` = 0) AND (`id` IN (3, 4)); +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 +(28511, 0, 3, 0, 108, 0, 100, 512, 3, 2851100, 0, 0, 0, 0, 80, 2851101, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - On Point 3 of Path 2851100 Reached - Run Script'), +(28511, 0, 4, 0, 6, 0, 100, 512, 0, 0, 0, 0, 0, 0, 28, 51852, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - On Just Died - Remove Aura \'The Eye of Acherus\''); + +-- Delete Action List rows (they are moved on new Action List). +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 2851100) AND (`source_type` = 9) AND (`id` IN (5, 6, 7, 8, 9, 10)); + +-- Create new Action List. +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 2851101) 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 +(2851101, 9, 0, 0, 0, 0, 100, 512, 0, 0, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Set Rooted On'), +(2851101, 9, 1, 0, 0, 0, 100, 512, 500, 500, 0, 0, 0, 0, 11, 51890, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Cast \'Eye of Acherus Flight\''), +(2851101, 9, 2, 0, 0, 0, 100, 512, 500, 500, 0, 0, 0, 0, 28, 51923, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Remove Aura \'Eye of Acherus Flight (Boost)\''), +(2851101, 9, 3, 0, 0, 0, 100, 0, 500, 500, 0, 0, 0, 0, 28, 51860, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Remove Aura \'Serverside - Root Self\''), +(2851101, 9, 4, 0, 0, 0, 100, 512, 500, 500, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Say Line 1'), +(2851101, 9, 5, 0, 0, 0, 100, 512, 500, 500, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Set Rooted Off'); From d24f9b067fa9e445a95241fc6f096e44d1b34ca4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Apr 2026 22:51:33 +0000 Subject: [PATCH 16/92] chore(DB): import pending files Referenced commit(s): 6f31825b132dd67d328f62609aede30eeca15ade --- .../rev_1777153480.sql => db_world/2026_04_25_04.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777153480.sql => db_world/2026_04_25_04.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1777153480.sql b/data/sql/updates/db_world/2026_04_25_04.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1777153480.sql rename to data/sql/updates/db_world/2026_04_25_04.sql index 9bb7ed451..2412b984c 100644 --- a/data/sql/updates/pending_db_world/rev_1777153480.sql +++ b/data/sql/updates/db_world/2026_04_25_04.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_25_03 -> 2026_04_25_04 -- Update General SAI. UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28511; From bcf13e4f1abf29451f319176a3b409be1e4655fa Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sun, 26 Apr 2026 01:18:45 -0300 Subject: [PATCH 17/92] fix(DB/SAI): Whisper Gulch gems despawn too fast (#25601) --- data/sql/updates/pending_db_world/rev_1777173822227629900.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777173822227629900.sql diff --git a/data/sql/updates/pending_db_world/rev_1777173822227629900.sql b/data/sql/updates/pending_db_world/rev_1777173822227629900.sql new file mode 100644 index 000000000..6ce4d0526 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777173822227629900.sql @@ -0,0 +1,4 @@ +DELETE FROM `smart_scripts` +WHERE `entryorguid` IN (2397400, 2397401, 2397402, 2397403) + AND `source_type` = 9 + AND `id` = 1; From e01f41be82fd00dc7c2a61a37109b4d457bc3861 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 04:19:50 +0000 Subject: [PATCH 18/92] chore(DB): import pending files Referenced commit(s): bcf13e4f1abf29451f319176a3b409be1e4655fa --- .../rev_1777173822227629900.sql => db_world/2026_04_26_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777173822227629900.sql => db_world/2026_04_26_00.sql} (74%) diff --git a/data/sql/updates/pending_db_world/rev_1777173822227629900.sql b/data/sql/updates/db_world/2026_04_26_00.sql similarity index 74% rename from data/sql/updates/pending_db_world/rev_1777173822227629900.sql rename to data/sql/updates/db_world/2026_04_26_00.sql index 6ce4d0526..b5dddbd04 100644 --- a/data/sql/updates/pending_db_world/rev_1777173822227629900.sql +++ b/data/sql/updates/db_world/2026_04_26_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_25_04 -> 2026_04_26_00 DELETE FROM `smart_scripts` WHERE `entryorguid` IN (2397400, 2397401, 2397402, 2397403) AND `source_type` = 9 From ffaebacb3fc432626c8a3d5118e3db85082d7880 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sun, 26 Apr 2026 07:13:48 +0200 Subject: [PATCH 19/92] fix(DB/Vendor): update Scrapbot and Jeeves' limited items (#24684) --- .../rev_1770899143405473549.sql | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1770899143405473549.sql diff --git a/data/sql/updates/pending_db_world/rev_1770899143405473549.sql b/data/sql/updates/pending_db_world/rev_1770899143405473549.sql new file mode 100644 index 000000000..bd8e150f2 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1770899143405473549.sql @@ -0,0 +1,23 @@ +-- +DELETE FROM `npc_vendor` WHERE (`entry` = 35642) AND (`item` IN (3775, 43231, 43233, 43235, 43237)); +INSERT INTO `npc_vendor` (`entry`, `slot`, `item`, `maxcount`, `incrtime`, `ExtendedCost`, `VerifiedBuild`) VALUES +(35642, 0, 3775, 0, 0, 0, 0), +(35642, 0, 43231, 0, 0, 0, 0), +(35642, 0, 43233, 0, 0, 0, 0), +(35642, 0, 43235, 0, 0, 0, 0), +(35642, 0, 43237, 0, 0, 0, 0); + +DELETE FROM `npc_vendor` WHERE (`entry` = 29561) AND (`item` IN (5565, 16583, 17020, 17030, 17031, 17032, 17033, 37201, 44605, 44614, 44615)); +INSERT INTO `npc_vendor` (`entry`, `slot`, `item`, `maxcount`, `incrtime`, `ExtendedCost`, `VerifiedBuild`) VALUES +(29561, 0, 5565, 5, 600, 0, 0), +(29561, 0, 16583, 2, 600, 0, 0), +(29561, 0, 17020, 80, 600, 0, 0), +(29561, 0, 17030, 10, 600, 0, 0), +(29561, 0, 17031, 10, 600, 0, 0), +(29561, 0, 17032, 4, 600, 0, 0), +(29561, 0, 17033, 10, 600, 0, 0), +-- (29561, 0, 21177, 300, 600, 0, 0), -- fixed in 36a1f31c2911e312a894c7a02027b98c1af79dbc +(29561, 0, 37201, 40, 600, 0, 0), +(29561, 0, 44605, 55, 600, 0, 0), +(29561, 0, 44614, 20, 600, 0, 0), +(29561, 0, 44615, 55, 600, 0, 0); From 295e45b0a5f68afbbb85cd514bea998e22584081 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 05:14:52 +0000 Subject: [PATCH 20/92] chore(DB): import pending files Referenced commit(s): ffaebacb3fc432626c8a3d5118e3db85082d7880 --- .../rev_1770899143405473549.sql => db_world/2026_04_26_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1770899143405473549.sql => db_world/2026_04_26_01.sql} (96%) diff --git a/data/sql/updates/pending_db_world/rev_1770899143405473549.sql b/data/sql/updates/db_world/2026_04_26_01.sql similarity index 96% rename from data/sql/updates/pending_db_world/rev_1770899143405473549.sql rename to data/sql/updates/db_world/2026_04_26_01.sql index bd8e150f2..a9fe420d1 100644 --- a/data/sql/updates/pending_db_world/rev_1770899143405473549.sql +++ b/data/sql/updates/db_world/2026_04_26_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_26_00 -> 2026_04_26_01 -- DELETE FROM `npc_vendor` WHERE (`entry` = 35642) AND (`item` IN (3775, 43231, 43233, 43235, 43237)); INSERT INTO `npc_vendor` (`entry`, `slot`, `item`, `maxcount`, `incrtime`, `ExtendedCost`, `VerifiedBuild`) VALUES From abbc37a2cb5573d8bd3eb5c2007d7171c764342e Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sun, 26 Apr 2026 10:32:38 +0200 Subject: [PATCH 21/92] fix(DB/SAI): Solve Gurgthock missing NPC Flags reset. (#25602) --- .../updates/pending_db_world/rev_1777189601.sql | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777189601.sql diff --git a/data/sql/updates/pending_db_world/rev_1777189601.sql b/data/sql/updates/pending_db_world/rev_1777189601.sql new file mode 100644 index 000000000..43687a32e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777189601.sql @@ -0,0 +1,14 @@ + +-- Update Mogor SAI +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 18069; + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 18069) AND (`source_type` = 0) AND (`id` IN (7, 9, 11)); +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 +(18069, 0, 7, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Mogor - On Evade - Despawn Instant - Failure Event'), +(18069, 0, 9, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 5000, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Mogor - On Just Died - Despawn In 5000 ms'), +(18069, 0, 11, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 82, 2, 0, 0, 0, 0, 0, 10, 66480, 18471, 0, 0, 0, 0, 0, 0, 'Mogor - On Respawn - Add Npc Flags Questgiver'); + +-- Update Gurgthock Action List +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 1847117) AND (`source_type` = 9) AND (`id` IN (1, 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 +(1847117, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 6, 9977, 0, 0, 0, 0, 0, 12, 2, 0, 0, 0, 0, 0, 0, 0, 'Gurgthock - Mogor Failure Script - Fail Quest \'The Ring of Blood: The Final Challenge\''); From 625b5cf172234f4d16a38f9bdc8230e9896299c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 08:33:43 +0000 Subject: [PATCH 22/92] chore(DB): import pending files Referenced commit(s): abbc37a2cb5573d8bd3eb5c2007d7171c764342e --- .../rev_1777189601.sql => db_world/2026_04_26_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777189601.sql => db_world/2026_04_26_02.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1777189601.sql b/data/sql/updates/db_world/2026_04_26_02.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1777189601.sql rename to data/sql/updates/db_world/2026_04_26_02.sql index 43687a32e..2ae742cd8 100644 --- a/data/sql/updates/pending_db_world/rev_1777189601.sql +++ b/data/sql/updates/db_world/2026_04_26_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_26_01 -> 2026_04_26_02 -- Update Mogor SAI UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 18069; From 7ff0efa4c9d6845c155ad2a6fcc49ac8033d994c Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sun, 26 Apr 2026 19:56:44 +0200 Subject: [PATCH 23/92] fix(SAI/SpellInfo): Rework Eye of Acherus AI. (#25607) --- .../pending_db_world/rev_1777200317.sql | 39 +++++++++++++++++++ .../game/Spells/SpellInfoCorrections.cpp | 1 - 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 data/sql/updates/pending_db_world/rev_1777200317.sql diff --git a/data/sql/updates/pending_db_world/rev_1777200317.sql b/data/sql/updates/pending_db_world/rev_1777200317.sql new file mode 100644 index 000000000..0e0839859 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777200317.sql @@ -0,0 +1,39 @@ + +-- Wipe creature_template_movement. +DELETE FROM `creature_template_movement` WHERE (`CreatureId` = 28511); + +-- Delete old waypoint. +DELETE FROM `waypoint_data` WHERE (`id` IN (2851100)); + +-- Add new waypoint. +DELETE FROM `waypoints` WHERE `entry` IN (2851100); +INSERT INTO `waypoints` (`entry`, `pointid`, `position_x`, `position_y`, `position_z`, `orientation`, `point_comment`) VALUES +(2851100, 1, 2341.57, -5672.8, 538.394, NULL, 'Eye of Acherus Flight'), +(2851100, 2, 1957.4, -5844.11, 273.867, NULL, 'Eye of Acherus Flight'), +(2851100, 3, 1758.01, -5876.78, 166.867, NULL, 'Eye of Acherus Flight'); + +-- Edit SAI. +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28511; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28511); +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 +(28511, 0, 0, 1, 54, 0, 100, 512, 0, 0, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - On Just Summoned - Set Rooted On'), +(28511, 0, 1, 2, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 51860, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - On Just Summoned - Cast \'Serverside - Root Self\''), +(28511, 0, 2, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 80, 2851100, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - On Just Summoned - Run Script'), +(28511, 0, 3, 0, 40, 0, 100, 512, 3, 2851100, 0, 0, 0, 0, 80, 2851101, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - On Point 3 of Path 2851100 Reached - Run Script'), +(28511, 0, 4, 0, 6, 0, 100, 512, 0, 0, 0, 0, 0, 0, 28, 51852, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - On Just Died - Remove Aura \'The Eye of Acherus\''); + +-- Edit Timed Action Lists +DELETE FROM `smart_scripts` WHERE (`source_type` = 9) AND (`entryorguid` IN (2851100, 2851101)); +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 +(2851100, 9, 0, 0, 0, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 51892, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Cast \'Eye of Acherus Visual\''), +(2851100, 9, 1, 0, 0, 0, 100, 512, 6000, 6000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Say Line 0'), +(2851100, 9, 2, 0, 0, 0, 100, 512, 0, 0, 0, 0, 0, 0, 11, 51923, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Cast \'Eye of Acherus Flight (Boost)\''), +(2851100, 9, 3, 0, 0, 0, 100, 512, 1000, 1000, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Set Rooted Off'), +(2851100, 9, 4, 0, 0, 0, 100, 512, 0, 0, 0, 0, 0, 0, 53, 2, 2851100, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Start Waypoint Path 2851100'), +(2851101, 9, 0, 0, 0, 0, 100, 512, 0, 0, 0, 0, 0, 0, 103, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Set Rooted On'), +(2851101, 9, 1, 0, 0, 0, 100, 512, 1000, 1000, 0, 0, 0, 0, 11, 51890, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Cast \'Eye of Acherus Flight\''), +(2851101, 9, 2, 0, 0, 0, 100, 512, 0, 0, 0, 0, 0, 0, 28, 51923, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Remove Aura \'Eye of Acherus Flight (Boost)\''), +(2851101, 9, 3, 0, 0, 0, 100, 512, 1000, 1000, 0, 0, 0, 0, 28, 51860, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Remove Aura \'Serverside - Root Self\''), +(2851101, 9, 4, 0, 0, 0, 100, 512, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Say Line 1'), +(2851101, 9, 5, 0, 0, 0, 100, 512, 1000, 1000, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Eye of Acherus - Actionlist - Set Rooted Off'); diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 2b819330e..e09e82a37 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -5075,7 +5075,6 @@ void SpellMgr::LoadSpellInfoCorrections() // Eye of Acherus Flight (Boost) ApplySpellFix({ 51923 }, [](SpellInfo* spellInfo) { - spellInfo->Effects[EFFECT_0].ApplyAuraName = SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED; spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_CASTER); }); From cb0ffd34aa6006db32a904dc85cf1cec2c1a2117 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 17:57:53 +0000 Subject: [PATCH 24/92] chore(DB): import pending files Referenced commit(s): 7ff0efa4c9d6845c155ad2a6fcc49ac8033d994c --- .../rev_1777200317.sql => db_world/2026_04_26_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777200317.sql => db_world/2026_04_26_03.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1777200317.sql b/data/sql/updates/db_world/2026_04_26_03.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1777200317.sql rename to data/sql/updates/db_world/2026_04_26_03.sql index 0e0839859..79c4ce2be 100644 --- a/data/sql/updates/pending_db_world/rev_1777200317.sql +++ b/data/sql/updates/db_world/2026_04_26_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_26_02 -> 2026_04_26_03 -- Wipe creature_template_movement. DELETE FROM `creature_template_movement` WHERE (`CreatureId` = 28511); From cf62b76ae05d75ae6a16e50739f91dde3ccb7aeb Mon Sep 17 00:00:00 2001 From: sogladev Date: Sun, 26 Apr 2026 23:04:10 +0200 Subject: [PATCH 25/92] fix(Scripts/Karazhan): Midnight no longer evades during Attumen fight (#25610) --- .../Karazhan/boss_midnight.cpp | 21 ++++--------------- .../Karazhan/instance_karazhan.cpp | 8 +++++++ 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp index 654ee2289..bc93a0303 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp @@ -82,11 +82,6 @@ struct boss_attumen : public BossAI Initialize(); } - bool CanMeleeHit() - { - return me->GetVictim() && (me->GetVictim()->GetPositionZ() < 53.0f || me->GetVictim()->GetDistance(me->GetHomePosition()) < 50.0f); - } - void EnterEvadeMode(EvadeReason why) override { if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) @@ -215,18 +210,10 @@ struct boss_attumen : public BossAI void UpdateAI(uint32 diff) override { - if (_phase != PHASE_NONE) - { - if (!UpdateVictim()) - { - return; - } - } - if (!CanMeleeHit()) - { - BossAI::EnterEvadeMode(EvadeReason::EVADE_REASON_BOUNDARY); - } - scheduler.Update(diff, std::bind(&BossAI::DoMeleeAttackIfReady, this)); + if (_phase != PHASE_NONE && !UpdateVictim()) + return; + + scheduler.Update(diff, [this] { DoMeleeAttackIfReady(); }); } void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override diff --git a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp index c683a4961..a771c067f 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp @@ -15,6 +15,7 @@ * with this program. If not, see . */ +#include "AreaBoundary.h" #include "Creature.h" #include "GameObject.h" #include "InstanceMapScript.h" @@ -65,6 +66,12 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM } }; +BossBoundaryData const boundaries = +{ + { DATA_ATTUMEN, new CircleBoundary(Position(-11126.3f, -1929.11f), 50.0f) }, + { DATA_ATTUMEN, new ZRangeBoundary(49.0f, 55.0f) }, +}; + class instance_karazhan : public InstanceMapScript { public: @@ -81,6 +88,7 @@ public: { SetHeaders(DataHeader); SetBossNumber(EncounterCount); + LoadBossBoundaries(boundaries); LoadObjectData(creatureData, gameObjectData); LoadDoorData(doorData); From 803749d8795b472f541d5182b7c448546231561e Mon Sep 17 00:00:00 2001 From: sogladev Date: Sun, 26 Apr 2026 23:04:46 +0200 Subject: [PATCH 26/92] fix(DB/InstanceEncounter): Update Attumen the Huntsman creditEntry (#25609) --- data/sql/updates/pending_db_world/rev_1777207812126443040.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777207812126443040.sql diff --git a/data/sql/updates/pending_db_world/rev_1777207812126443040.sql b/data/sql/updates/pending_db_world/rev_1777207812126443040.sql new file mode 100644 index 000000000..74d170a7d --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777207812126443040.sql @@ -0,0 +1,2 @@ +-- +UPDATE `instance_encounters` SET `creditEntry`=16152 WHERE `entry`=652; From 4958d99a231cce1eec0c32013e462a0a46a10011 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 21:05:14 +0000 Subject: [PATCH 27/92] chore(DB): import pending files Referenced commit(s): cf62b76ae05d75ae6a16e50739f91dde3ccb7aeb --- .../rev_1777207812126443040.sql => db_world/2026_04_26_04.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777207812126443040.sql => db_world/2026_04_26_04.sql} (63%) diff --git a/data/sql/updates/pending_db_world/rev_1777207812126443040.sql b/data/sql/updates/db_world/2026_04_26_04.sql similarity index 63% rename from data/sql/updates/pending_db_world/rev_1777207812126443040.sql rename to data/sql/updates/db_world/2026_04_26_04.sql index 74d170a7d..18a1d166c 100644 --- a/data/sql/updates/pending_db_world/rev_1777207812126443040.sql +++ b/data/sql/updates/db_world/2026_04_26_04.sql @@ -1,2 +1,3 @@ +-- DB update 2026_04_26_03 -> 2026_04_26_04 -- UPDATE `instance_encounters` SET `creditEntry`=16152 WHERE `entry`=652; From 721415008aed995768a11ab154c6cdbeca83af5a Mon Sep 17 00:00:00 2001 From: Apatia <107246426+Apathyxia@users.noreply.github.com> Date: Mon, 27 Apr 2026 01:12:03 +0200 Subject: [PATCH 28/92] fix(DB/npc_vendor): Update correct S6 vendor prices and add missing items (#25563) Co-authored-by: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> --- .../pending_db_world/furious_update.sql | 468 ++++++++++++++++++ 1 file changed, 468 insertions(+) create mode 100644 data/sql/updates/pending_db_world/furious_update.sql diff --git a/data/sql/updates/pending_db_world/furious_update.sql b/data/sql/updates/pending_db_world/furious_update.sql new file mode 100644 index 000000000..af41db8ff --- /dev/null +++ b/data/sql/updates/pending_db_world/furious_update.sql @@ -0,0 +1,468 @@ +-- Adds missing items to vendors, corrects rating requirements and arena point+honour costs. +DELETE FROM `npc_vendor` WHERE (`entry` IN (33915, 33918, 33921, 34087, 34088, 34089)); +INSERT INTO `npc_vendor` (`entry`, `slot`, `item`, `maxcount`, `incrtime`, `ExtendedCost`, `VerifiedBuild`) VALUES +(33915, 0, 40787, 0, 0, 2622, 0), +(33915, 0, 40788, 0, 0, 2622, 0), +(33915, 0, 40789, 0, 0, 2622, 0), +(33915, 0, 40807, 0, 0, 2621, 0), +(33915, 0, 40808, 0, 0, 2621, 0), +(33915, 0, 40809, 0, 0, 2621, 0), +(33915, 0, 40826, 0, 0, 2629, 0), +(33915, 0, 40827, 0, 0, 2629, 0), +(33915, 0, 40828, 0, 0, 2629, 0), +(33915, 0, 40847, 0, 0, 2340, 0), +(33915, 0, 40848, 0, 0, 2340, 0), +(33915, 0, 40849, 0, 0, 2340, 0), +(33915, 0, 40866, 0, 0, 2470, 0), +(33915, 0, 40868, 0, 0, 2470, 0), +(33915, 0, 40869, 0, 0, 2470, 0), +(33915, 0, 40907, 0, 0, 2622, 0), +(33915, 0, 40927, 0, 0, 2621, 0), +(33915, 0, 40933, 0, 0, 2629, 0), +(33915, 0, 40939, 0, 0, 2340, 0), +(33915, 0, 40963, 0, 0, 2470, 0), +(33915, 0, 40992, 0, 0, 2622, 0), +(33915, 0, 40993, 0, 0, 2622, 0), +(33915, 0, 41001, 0, 0, 2621, 0), +(33915, 0, 41007, 0, 0, 2621, 0), +(33915, 0, 41013, 0, 0, 2629, 0), +(33915, 0, 41019, 0, 0, 2629, 0), +(33915, 0, 41027, 0, 0, 2340, 0), +(33915, 0, 41033, 0, 0, 2340, 0), +(33915, 0, 41038, 0, 0, 2470, 0), +(33915, 0, 41044, 0, 0, 2470, 0), +(33915, 0, 41081, 0, 0, 2622, 0), +(33915, 0, 41087, 0, 0, 2622, 0), +(33915, 0, 41137, 0, 0, 2621, 0), +(33915, 0, 41143, 0, 0, 2621, 0), +(33915, 0, 41151, 0, 0, 2629, 0), +(33915, 0, 41157, 0, 0, 2629, 0), +(33915, 0, 41199, 0, 0, 2340, 0), +(33915, 0, 41205, 0, 0, 2340, 0), +(33915, 0, 41211, 0, 0, 2470, 0), +(33915, 0, 41217, 0, 0, 2470, 0), +(33915, 0, 41275, 0, 0, 2470, 0), +(33915, 0, 41281, 0, 0, 2470, 0), +(33915, 0, 41287, 0, 0, 2621, 0), +(33915, 0, 41293, 0, 0, 2621, 0), +(33915, 0, 41298, 0, 0, 2340, 0), +(33915, 0, 41304, 0, 0, 2340, 0), +(33915, 0, 41310, 0, 0, 2622, 0), +(33915, 0, 41316, 0, 0, 2622, 0), +(33915, 0, 41321, 0, 0, 2629, 0), +(33915, 0, 41327, 0, 0, 2629, 0), +(33915, 0, 41650, 0, 0, 2622, 0), +(33915, 0, 41655, 0, 0, 2340, 0), +(33915, 0, 41661, 0, 0, 2622, 0), +(33915, 0, 41667, 0, 0, 2340, 0), +(33915, 0, 41672, 0, 0, 2629, 0), +(33915, 0, 41678, 0, 0, 2629, 0), +(33915, 0, 41683, 0, 0, 2470, 0), +(33915, 0, 41715, 0, 0, 2470, 0), +(33915, 0, 41767, 0, 0, 2621, 0), +(33915, 0, 41773, 0, 0, 2621, 0), +(33915, 0, 41854, 0, 0, 2629, 0), +(33915, 0, 41859, 0, 0, 2622, 0), +(33915, 0, 41864, 0, 0, 2340, 0), +(33915, 0, 41869, 0, 0, 2470, 0), +(33915, 0, 41874, 0, 0, 2621, 0), +(33915, 0, 41915, 0, 0, 2629, 0), +(33915, 0, 41921, 0, 0, 2622, 0), +(33915, 0, 41927, 0, 0, 2340, 0), +(33915, 0, 41934, 0, 0, 2470, 0), +(33915, 0, 41940, 0, 0, 2621, 0), +(33915, 0, 41946, 0, 0, 2629, 0), +(33915, 0, 41953, 0, 0, 2622, 0), +(33915, 0, 41959, 0, 0, 2340, 0), +(33915, 0, 41965, 0, 0, 2470, 0), +(33915, 0, 41971, 0, 0, 2621, 0), +(33915, 0, 41993, 0, 0, 2629, 0), +(33915, 0, 41998, 0, 0, 2622, 0), +(33915, 0, 42005, 0, 0, 2340, 0), +(33915, 0, 42011, 0, 0, 2470, 0), +(33915, 0, 42017, 0, 0, 2621, 0), +(33915, 0, 42209, 0, 0, 2624, 0), +(33915, 0, 42228, 0, 0, 2721, 0), +(33915, 0, 42233, 0, 0, 2721, 0), +(33915, 0, 42243, 0, 0, 2624, 0), +(33915, 0, 42249, 0, 0, 2721, 0), +(33915, 0, 42256, 0, 0, 2721, 0), +(33915, 0, 42261, 0, 0, 2624, 0), +(33915, 0, 42266, 0, 0, 2721, 0), +(33915, 0, 42271, 0, 0, 2721, 0), +(33915, 0, 42276, 0, 0, 2624, 0), +(33915, 0, 42281, 0, 0, 2721, 0), +(33915, 0, 42286, 0, 0, 2624, 0), +(33915, 0, 42291, 0, 0, 2721, 0), +(33915, 0, 42318, 0, 0, 2623, 0), +(33915, 0, 42323, 0, 0, 2623, 0), +(33915, 0, 42328, 0, 0, 2623, 0), +(33915, 0, 42333, 0, 0, 2623, 0), +(33915, 0, 42347, 0, 0, 2624, 0), +(33915, 0, 42353, 0, 0, 2624, 0), +(33915, 0, 42364, 0, 0, 2623, 0), +(33915, 0, 42385, 0, 0, 2623, 0), +(33915, 0, 42391, 0, 0, 2623, 0), +(33915, 0, 42451, 0, 0, 2384, 0), +(33915, 0, 42486, 0, 0, 2623, 0), +(33915, 0, 42491, 0, 0, 2623, 0), +(33915, 0, 42496, 0, 0, 2623, 0), +(33915, 0, 42503, 0, 0, 2384, 0), +(33915, 0, 42514, 0, 0, 2384, 0), +(33915, 0, 42520, 0, 0, 2384, 0), +(33915, 0, 42526, 0, 0, 2721, 0), +(33915, 0, 42532, 0, 0, 2721, 0), +(33915, 0, 42538, 0, 0, 2721, 0), +(33915, 0, 42560, 0, 0, 2628, 0), +(33915, 0, 42565, 0, 0, 2628, 0), +(33915, 0, 42571, 0, 0, 2628, 0), +(33915, 0, 42579, 0, 0, 2384, 0), +(33915, 0, 42584, 0, 0, 2384, 0), +(33915, 0, 42589, 0, 0, 2384, 0), +(33915, 0, 42598, 0, 0, 2384, 0), +(33915, 0, 42603, 0, 0, 2384, 0), +(33915, 0, 42608, 0, 0, 2384, 0), +(33915, 0, 42615, 0, 0, 2384, 0), +(33915, 0, 42621, 0, 0, 2384, 0), +(33915, 0, 42853, 0, 0, 2384, 0), +(33915, 0, 44421, 0, 0, 2623, 0), +(33915, 0, 44422, 0, 0, 2623, 0), +(33915, 0, 45706, 0, 0, 2596, 0), +(33915, 0, 45983, 0, 0, 2636, 0), +(33918, 0, 40787, 0, 0, 2622, 0), +(33918, 0, 40788, 0, 0, 2622, 0), +(33918, 0, 40789, 0, 0, 2622, 0), +(33918, 0, 40807, 0, 0, 2621, 0), +(33918, 0, 40808, 0, 0, 2621, 0), +(33918, 0, 40809, 0, 0, 2621, 0), +(33918, 0, 40826, 0, 0, 2629, 0), +(33918, 0, 40827, 0, 0, 2629, 0), +(33918, 0, 40828, 0, 0, 2629, 0), +(33918, 0, 40847, 0, 0, 2340, 0), +(33918, 0, 40848, 0, 0, 2340, 0), +(33918, 0, 40849, 0, 0, 2340, 0), +(33918, 0, 40866, 0, 0, 2470, 0), +(33918, 0, 40868, 0, 0, 2470, 0), +(33918, 0, 40869, 0, 0, 2470, 0), +(33918, 0, 40907, 0, 0, 2622, 0), +(33918, 0, 40927, 0, 0, 2621, 0), +(33918, 0, 40933, 0, 0, 2629, 0), +(33918, 0, 40939, 0, 0, 2340, 0), +(33918, 0, 40963, 0, 0, 2470, 0), +(33918, 0, 40992, 0, 0, 2622, 0), +(33918, 0, 40993, 0, 0, 2622, 0), +(33918, 0, 41001, 0, 0, 2621, 0), +(33918, 0, 41007, 0, 0, 2621, 0), +(33918, 0, 41013, 0, 0, 2629, 0), +(33918, 0, 41019, 0, 0, 2629, 0), +(33918, 0, 41027, 0, 0, 2340, 0), +(33918, 0, 41033, 0, 0, 2340, 0), +(33918, 0, 41038, 0, 0, 2470, 0), +(33918, 0, 41044, 0, 0, 2470, 0), +(33918, 0, 41081, 0, 0, 2622, 0), +(33918, 0, 41087, 0, 0, 2622, 0), +(33918, 0, 41137, 0, 0, 2621, 0), +(33918, 0, 41143, 0, 0, 2621, 0), +(33918, 0, 41151, 0, 0, 2629, 0), +(33918, 0, 41157, 0, 0, 2629, 0), +(33918, 0, 41199, 0, 0, 2340, 0), +(33918, 0, 41205, 0, 0, 2340, 0), +(33918, 0, 41211, 0, 0, 2470, 0), +(33918, 0, 41217, 0, 0, 2470, 0), +(33918, 0, 41275, 0, 0, 2470, 0), +(33918, 0, 41281, 0, 0, 2470, 0), +(33918, 0, 41287, 0, 0, 2621, 0), +(33918, 0, 41293, 0, 0, 2621, 0), +(33918, 0, 41298, 0, 0, 2340, 0), +(33918, 0, 41304, 0, 0, 2340, 0), +(33918, 0, 41310, 0, 0, 2622, 0), +(33918, 0, 41316, 0, 0, 2622, 0), +(33918, 0, 41321, 0, 0, 2629, 0), +(33918, 0, 41327, 0, 0, 2629, 0), +(33918, 0, 41650, 0, 0, 2622, 0), +(33918, 0, 41655, 0, 0, 2340, 0), +(33918, 0, 41661, 0, 0, 2622, 0), +(33918, 0, 41667, 0, 0, 2340, 0), +(33918, 0, 41672, 0, 0, 2629, 0), +(33918, 0, 41678, 0, 0, 2629, 0), +(33918, 0, 41683, 0, 0, 2470, 0), +(33918, 0, 41715, 0, 0, 2470, 0), +(33918, 0, 41767, 0, 0, 2621, 0), +(33918, 0, 41773, 0, 0, 2621, 0), +(33918, 0, 41854, 0, 0, 2629, 0), +(33918, 0, 41859, 0, 0, 2622, 0), +(33918, 0, 41864, 0, 0, 2340, 0), +(33918, 0, 41869, 0, 0, 2470, 0), +(33918, 0, 41874, 0, 0, 2621, 0), +(33918, 0, 41915, 0, 0, 2629, 0), +(33918, 0, 41921, 0, 0, 2622, 0), +(33918, 0, 41927, 0, 0, 2340, 0), +(33918, 0, 41934, 0, 0, 2470, 0), +(33918, 0, 41940, 0, 0, 2621, 0), +(33918, 0, 41946, 0, 0, 2629, 0), +(33918, 0, 41953, 0, 0, 2622, 0), +(33918, 0, 41959, 0, 0, 2340, 0), +(33918, 0, 41965, 0, 0, 2470, 0), +(33918, 0, 41971, 0, 0, 2621, 0), +(33918, 0, 41993, 0, 0, 2629, 0), +(33918, 0, 41998, 0, 0, 2622, 0), +(33918, 0, 42005, 0, 0, 2340, 0), +(33918, 0, 42011, 0, 0, 2470, 0), +(33918, 0, 42017, 0, 0, 2621, 0), +(33918, 0, 42209, 0, 0, 2624, 0), +(33918, 0, 42228, 0, 0, 2721, 0), +(33918, 0, 42233, 0, 0, 2721, 0), +(33918, 0, 42243, 0, 0, 2624, 0), +(33918, 0, 42249, 0, 0, 2721, 0), +(33918, 0, 42256, 0, 0, 2721, 0), +(33918, 0, 42261, 0, 0, 2624, 0), +(33918, 0, 42266, 0, 0, 2721, 0), +(33918, 0, 42271, 0, 0, 2721, 0), +(33918, 0, 42276, 0, 0, 2624, 0), +(33918, 0, 42281, 0, 0, 2721, 0), +(33918, 0, 42286, 0, 0, 2624, 0), +(33918, 0, 42291, 0, 0, 2721, 0), +(33918, 0, 42318, 0, 0, 2623, 0), +(33918, 0, 42323, 0, 0, 2623, 0), +(33918, 0, 42328, 0, 0, 2623, 0), +(33918, 0, 42333, 0, 0, 2623, 0), +(33918, 0, 42347, 0, 0, 2624, 0), +(33918, 0, 42353, 0, 0, 2624, 0), +(33918, 0, 42364, 0, 0, 2623, 0), +(33918, 0, 42385, 0, 0, 2623, 0), +(33918, 0, 42391, 0, 0, 2623, 0), +(33918, 0, 42451, 0, 0, 2384, 0), +(33918, 0, 42486, 0, 0, 2623, 0), +(33918, 0, 42491, 0, 0, 2623, 0), +(33918, 0, 42496, 0, 0, 2623, 0), +(33918, 0, 42503, 0, 0, 2384, 0), +(33918, 0, 42514, 0, 0, 2384, 0), +(33918, 0, 42520, 0, 0, 2384, 0), +(33918, 0, 42526, 0, 0, 2721, 0), +(33918, 0, 42532, 0, 0, 2721, 0), +(33918, 0, 42538, 0, 0, 2721, 0), +(33918, 0, 42560, 0, 0, 2628, 0), +(33918, 0, 42565, 0, 0, 2628, 0), +(33918, 0, 42571, 0, 0, 2628, 0), +(33918, 0, 42579, 0, 0, 2384, 0), +(33918, 0, 42584, 0, 0, 2384, 0), +(33918, 0, 42589, 0, 0, 2384, 0), +(33918, 0, 42598, 0, 0, 2384, 0), +(33918, 0, 42603, 0, 0, 2384, 0), +(33918, 0, 42608, 0, 0, 2384, 0), +(33918, 0, 42615, 0, 0, 2384, 0), +(33918, 0, 42621, 0, 0, 2384, 0), +(33918, 0, 42853, 0, 0, 2384, 0), +(33918, 0, 44421, 0, 0, 2623, 0), +(33918, 0, 44422, 0, 0, 2623, 0), +(33918, 0, 45706, 0, 0, 2596, 0), +(33918, 0, 45983, 0, 0, 2636, 0), +(33921, 0, 40787, 0, 0, 2622, 0), +(33921, 0, 40788, 0, 0, 2622, 0), +(33921, 0, 40789, 0, 0, 2622, 0), +(33921, 0, 40807, 0, 0, 2621, 0), +(33921, 0, 40808, 0, 0, 2621, 0), +(33921, 0, 40809, 0, 0, 2621, 0), +(33921, 0, 40826, 0, 0, 2629, 0), +(33921, 0, 40827, 0, 0, 2629, 0), +(33921, 0, 40828, 0, 0, 2629, 0), +(33921, 0, 40847, 0, 0, 2340, 0), +(33921, 0, 40848, 0, 0, 2340, 0), +(33921, 0, 40849, 0, 0, 2340, 0), +(33921, 0, 40866, 0, 0, 2470, 0), +(33921, 0, 40868, 0, 0, 2470, 0), +(33921, 0, 40869, 0, 0, 2470, 0), +(33921, 0, 40907, 0, 0, 2622, 0), +(33921, 0, 40927, 0, 0, 2621, 0), +(33921, 0, 40933, 0, 0, 2629, 0), +(33921, 0, 40939, 0, 0, 2340, 0), +(33921, 0, 40963, 0, 0, 2470, 0), +(33921, 0, 40992, 0, 0, 2622, 0), +(33921, 0, 40993, 0, 0, 2622, 0), +(33921, 0, 41001, 0, 0, 2621, 0), +(33921, 0, 41007, 0, 0, 2621, 0), +(33921, 0, 41013, 0, 0, 2629, 0), +(33921, 0, 41019, 0, 0, 2629, 0), +(33921, 0, 41027, 0, 0, 2340, 0), +(33921, 0, 41033, 0, 0, 2340, 0), +(33921, 0, 41038, 0, 0, 2470, 0), +(33921, 0, 41044, 0, 0, 2470, 0), +(33921, 0, 41081, 0, 0, 2622, 0), +(33921, 0, 41087, 0, 0, 2622, 0), +(33921, 0, 41137, 0, 0, 2621, 0), +(33921, 0, 41143, 0, 0, 2621, 0), +(33921, 0, 41151, 0, 0, 2629, 0), +(33921, 0, 41157, 0, 0, 2629, 0), +(33921, 0, 41199, 0, 0, 2340, 0), +(33921, 0, 41205, 0, 0, 2340, 0), +(33921, 0, 41211, 0, 0, 2470, 0), +(33921, 0, 41217, 0, 0, 2470, 0), +(33921, 0, 41275, 0, 0, 2470, 0), +(33921, 0, 41281, 0, 0, 2470, 0), +(33921, 0, 41287, 0, 0, 2621, 0), +(33921, 0, 41293, 0, 0, 2621, 0), +(33921, 0, 41298, 0, 0, 2340, 0), +(33921, 0, 41304, 0, 0, 2340, 0), +(33921, 0, 41310, 0, 0, 2622, 0), +(33921, 0, 41316, 0, 0, 2622, 0), +(33921, 0, 41321, 0, 0, 2629, 0), +(33921, 0, 41327, 0, 0, 2629, 0), +(33921, 0, 41650, 0, 0, 2622, 0), +(33921, 0, 41655, 0, 0, 2340, 0), +(33921, 0, 41661, 0, 0, 2622, 0), +(33921, 0, 41667, 0, 0, 2340, 0), +(33921, 0, 41672, 0, 0, 2629, 0), +(33921, 0, 41678, 0, 0, 2629, 0), +(33921, 0, 41683, 0, 0, 2470, 0), +(33921, 0, 41715, 0, 0, 2470, 0), +(33921, 0, 41767, 0, 0, 2621, 0), +(33921, 0, 41773, 0, 0, 2621, 0), +(33921, 0, 41854, 0, 0, 2629, 0), +(33921, 0, 41859, 0, 0, 2622, 0), +(33921, 0, 41864, 0, 0, 2340, 0), +(33921, 0, 41869, 0, 0, 2470, 0), +(33921, 0, 41874, 0, 0, 2621, 0), +(33921, 0, 41915, 0, 0, 2629, 0), +(33921, 0, 41921, 0, 0, 2622, 0), +(33921, 0, 41927, 0, 0, 2340, 0), +(33921, 0, 41934, 0, 0, 2470, 0), +(33921, 0, 41940, 0, 0, 2621, 0), +(33921, 0, 41946, 0, 0, 2629, 0), +(33921, 0, 41953, 0, 0, 2622, 0), +(33921, 0, 41959, 0, 0, 2340, 0), +(33921, 0, 41965, 0, 0, 2470, 0), +(33921, 0, 41971, 0, 0, 2621, 0), +(33921, 0, 41993, 0, 0, 2629, 0), +(33921, 0, 41998, 0, 0, 2622, 0), +(33921, 0, 42005, 0, 0, 2340, 0), +(33921, 0, 42011, 0, 0, 2470, 0), +(33921, 0, 42017, 0, 0, 2621, 0), +(33921, 0, 42209, 0, 0, 2624, 0), +(33921, 0, 42228, 0, 0, 2721, 0), +(33921, 0, 42233, 0, 0, 2721, 0), +(33921, 0, 42243, 0, 0, 2624, 0), +(33921, 0, 42249, 0, 0, 2721, 0), +(33921, 0, 42256, 0, 0, 2721, 0), +(33921, 0, 42261, 0, 0, 2624, 0), +(33921, 0, 42266, 0, 0, 2721, 0), +(33921, 0, 42271, 0, 0, 2721, 0), +(33921, 0, 42276, 0, 0, 2624, 0), +(33921, 0, 42281, 0, 0, 2721, 0), +(33921, 0, 42286, 0, 0, 2624, 0), +(33921, 0, 42291, 0, 0, 2721, 0), +(33921, 0, 42318, 0, 0, 2623, 0), +(33921, 0, 42323, 0, 0, 2623, 0), +(33921, 0, 42328, 0, 0, 2623, 0), +(33921, 0, 42333, 0, 0, 2623, 0), +(33921, 0, 42347, 0, 0, 2624, 0), +(33921, 0, 42353, 0, 0, 2624, 0), +(33921, 0, 42364, 0, 0, 2623, 0), +(33921, 0, 42385, 0, 0, 2623, 0), +(33921, 0, 42391, 0, 0, 2623, 0), +(33921, 0, 42451, 0, 0, 2384, 0), +(33921, 0, 42486, 0, 0, 2623, 0), +(33921, 0, 42491, 0, 0, 2623, 0), +(33921, 0, 42496, 0, 0, 2623, 0), +(33921, 0, 42503, 0, 0, 2384, 0), +(33921, 0, 42514, 0, 0, 2384, 0), +(33921, 0, 42520, 0, 0, 2384, 0), +(33921, 0, 42526, 0, 0, 2721, 0), +(33921, 0, 42532, 0, 0, 2721, 0), +(33921, 0, 42538, 0, 0, 2721, 0), +(33921, 0, 42560, 0, 0, 2628, 0), +(33921, 0, 42565, 0, 0, 2628, 0), +(33921, 0, 42571, 0, 0, 2628, 0), +(33921, 0, 42579, 0, 0, 2384, 0), +(33921, 0, 42584, 0, 0, 2384, 0), +(33921, 0, 42589, 0, 0, 2384, 0), +(33921, 0, 42598, 0, 0, 2384, 0), +(33921, 0, 42603, 0, 0, 2384, 0), +(33921, 0, 42608, 0, 0, 2384, 0), +(33921, 0, 42615, 0, 0, 2384, 0), +(33921, 0, 42621, 0, 0, 2384, 0), +(33921, 0, 42853, 0, 0, 2384, 0), +(33921, 0, 44421, 0, 0, 2623, 0), +(33921, 0, 44422, 0, 0, 2623, 0), +(33921, 0, 45706, 0, 0, 2596, 0), +(33921, 0, 45983, 0, 0, 2636, 0), +(34087, 0, 45937, 0, 0, 2632, 0), +(34087, 0, 45938, 0, 0, 2632, 0), +(34087, 0, 45939, 0, 0, 2632, 0), +(34087, 0, 45948, 0, 0, 2632, 0), +(34087, 0, 45949, 0, 0, 2632, 0), +(34087, 0, 45950, 0, 0, 2632, 0), +(34087, 0, 45951, 0, 0, 2632, 0), +(34087, 0, 45952, 0, 0, 2632, 0), +(34087, 0, 45953, 0, 0, 2632, 0), +(34087, 0, 45954, 0, 0, 2632, 0), +(34087, 0, 45955, 0, 0, 2632, 0), +(34087, 0, 45956, 0, 0, 2632, 0), +(34087, 0, 45957, 0, 0, 2634, 0), +(34087, 0, 45958, 0, 0, 2634, 0), +(34087, 0, 45959, 0, 0, 2634, 0), +(34087, 0, 45960, 0, 0, 2634, 0), +(34087, 0, 45961, 0, 0, 2635, 0), +(34087, 0, 45962, 0, 0, 2635, 0), +(34087, 0, 45963, 0, 0, 2635, 0), +(34087, 0, 45964, 0, 0, 2635, 0), +(34087, 0, 45965, 0, 0, 2635, 0), +(34087, 0, 45966, 0, 0, 2635, 0), +(34087, 0, 45967, 0, 0, 2635, 0), +(34087, 0, 45968, 0, 0, 2635, 0), +(34087, 0, 45969, 0, 0, 2634, 0), +(34087, 0, 45970, 0, 0, 2633, 0), +(34087, 0, 45971, 0, 0, 2633, 0), +(34088, 0, 45937, 0, 0, 2632, 0), +(34088, 0, 45938, 0, 0, 2632, 0), +(34088, 0, 45939, 0, 0, 2632, 0), +(34088, 0, 45948, 0, 0, 2632, 0), +(34088, 0, 45949, 0, 0, 2632, 0), +(34088, 0, 45950, 0, 0, 2632, 0), +(34088, 0, 45951, 0, 0, 2632, 0), +(34088, 0, 45952, 0, 0, 2632, 0), +(34088, 0, 45953, 0, 0, 2632, 0), +(34088, 0, 45954, 0, 0, 2632, 0), +(34088, 0, 45955, 0, 0, 2632, 0), +(34088, 0, 45956, 0, 0, 2632, 0), +(34088, 0, 45957, 0, 0, 2634, 0), +(34088, 0, 45958, 0, 0, 2634, 0), +(34088, 0, 45959, 0, 0, 2634, 0), +(34088, 0, 45960, 0, 0, 2634, 0), +(34088, 0, 45961, 0, 0, 2635, 0), +(34088, 0, 45962, 0, 0, 2635, 0), +(34088, 0, 45963, 0, 0, 2635, 0), +(34088, 0, 45964, 0, 0, 2635, 0), +(34088, 0, 45965, 0, 0, 2635, 0), +(34088, 0, 45966, 0, 0, 2635, 0), +(34088, 0, 45967, 0, 0, 2635, 0), +(34088, 0, 45968, 0, 0, 2635, 0), +(34088, 0, 45969, 0, 0, 2634, 0), +(34088, 0, 45970, 0, 0, 2633, 0), +(34088, 0, 45971, 0, 0, 2633, 0), +(34089, 0, 45937, 0, 0, 2632, 0), +(34089, 0, 45938, 0, 0, 2632, 0), +(34089, 0, 45939, 0, 0, 2632, 0), +(34089, 0, 45948, 0, 0, 2632, 0), +(34089, 0, 45949, 0, 0, 2632, 0), +(34089, 0, 45950, 0, 0, 2632, 0), +(34089, 0, 45951, 0, 0, 2632, 0), +(34089, 0, 45952, 0, 0, 2632, 0), +(34089, 0, 45953, 0, 0, 2632, 0), +(34089, 0, 45954, 0, 0, 2632, 0), +(34089, 0, 45955, 0, 0, 2632, 0), +(34089, 0, 45956, 0, 0, 2632, 0), +(34089, 0, 45957, 0, 0, 2634, 0), +(34089, 0, 45958, 0, 0, 2634, 0), +(34089, 0, 45959, 0, 0, 2634, 0), +(34089, 0, 45960, 0, 0, 2634, 0), +(34089, 0, 45961, 0, 0, 2635, 0), +(34089, 0, 45962, 0, 0, 2635, 0), +(34089, 0, 45963, 0, 0, 2635, 0), +(34089, 0, 45964, 0, 0, 2635, 0), +(34089, 0, 45965, 0, 0, 2635, 0), +(34089, 0, 45966, 0, 0, 2635, 0), +(34089, 0, 45967, 0, 0, 2635, 0), +(34089, 0, 45968, 0, 0, 2635, 0), +(34089, 0, 45969, 0, 0, 2634, 0), +(34089, 0, 45970, 0, 0, 2633, 0), +(34089, 0, 45971, 0, 0, 2633, 0); From c4970eef80b6044deeeb4dab1130a6efaa9f4196 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 26 Apr 2026 23:13:09 +0000 Subject: [PATCH 29/92] chore(DB): import pending files Referenced commit(s): 721415008aed995768a11ab154c6cdbeca83af5a --- .../furious_update.sql => db_world/2026_04_26_05.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/furious_update.sql => db_world/2026_04_26_05.sql} (99%) diff --git a/data/sql/updates/pending_db_world/furious_update.sql b/data/sql/updates/db_world/2026_04_26_05.sql similarity index 99% rename from data/sql/updates/pending_db_world/furious_update.sql rename to data/sql/updates/db_world/2026_04_26_05.sql index af41db8ff..e63154c66 100644 --- a/data/sql/updates/pending_db_world/furious_update.sql +++ b/data/sql/updates/db_world/2026_04_26_05.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_26_04 -> 2026_04_26_05 -- Adds missing items to vendors, corrects rating requirements and arena point+honour costs. DELETE FROM `npc_vendor` WHERE (`entry` IN (33915, 33918, 33921, 34087, 34088, 34089)); INSERT INTO `npc_vendor` (`entry`, `slot`, `item`, `maxcount`, `incrtime`, `ExtendedCost`, `VerifiedBuild`) VALUES From 6fd0a6710422429acaa2518a89f4267b83547296 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Mon, 27 Apr 2026 18:25:19 +0200 Subject: [PATCH 30/92] fix(Core/Player): Object Visibility Condition should not apply game masters. (#25622) --- src/server/game/Entities/Player/Player.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 275be9437..4b5804c29 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -14346,6 +14346,9 @@ bool Player::CanSeeSpellClickOn(Creature const* c) const bool Player::CanSeeObjectByVisibilityConditions(WorldObject const* object) const { + if (IsGameMaster()) + return true; + ConditionList conds = sConditionMgr->GetConditionsForObjectVisibility(object); ConditionSourceInfo info = ConditionSourceInfo(const_cast(this), const_cast(object)); return sConditionMgr->IsObjectMeetToConditions(info, conds); From 60b3a02f814b6949919edf77aefdc01599c011ba Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Mon, 27 Apr 2026 22:46:56 +0200 Subject: [PATCH 31/92] fix(DB/SAI): Change wrong target_type on Obsidion SmartAI. (#25627) --- data/sql/updates/pending_db_world/rev_1777317086.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777317086.sql diff --git a/data/sql/updates/pending_db_world/rev_1777317086.sql b/data/sql/updates/pending_db_world/rev_1777317086.sql new file mode 100644 index 000000000..925fb9595 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777317086.sql @@ -0,0 +1,3 @@ + +-- Change target to self. +UPDATE `smart_scripts` SET `target_type` = 1 WHERE (`entryorguid` = 8400) AND (`source_type` = 0) AND (`id` IN (0)); From 9dc68434c8f88cd4d83c8777d0932713b744acfb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 27 Apr 2026 20:48:04 +0000 Subject: [PATCH 32/92] chore(DB): import pending files Referenced commit(s): 60b3a02f814b6949919edf77aefdc01599c011ba --- .../rev_1777317086.sql => db_world/2026_04_27_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777317086.sql => db_world/2026_04_27_00.sql} (76%) diff --git a/data/sql/updates/pending_db_world/rev_1777317086.sql b/data/sql/updates/db_world/2026_04_27_00.sql similarity index 76% rename from data/sql/updates/pending_db_world/rev_1777317086.sql rename to data/sql/updates/db_world/2026_04_27_00.sql index 925fb9595..79bf3a10f 100644 --- a/data/sql/updates/pending_db_world/rev_1777317086.sql +++ b/data/sql/updates/db_world/2026_04_27_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_26_05 -> 2026_04_27_00 -- Change target to self. UPDATE `smart_scripts` SET `target_type` = 1 WHERE (`entryorguid` = 8400) AND (`source_type` = 0) AND (`id` IN (0)); From 65613eaeeb8d5753037527447aeca5797aa916a5 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Mon, 27 Apr 2026 22:50:16 +0200 Subject: [PATCH 33/92] fix(Core/SmartAI): Prevent NPCs from attacking invalid targets. (#25626) --- src/server/game/AI/SmartScripts/SmartAI.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index c80e2a0ac..8ce3641e8 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -818,6 +818,10 @@ bool SmartAI::AssistPlayerInCombatAgainst(Unit* who) if (!me->IsValidAssistTarget(who->GetVictim())) return false; + // Do not engage if we cannot actually attack the attacker (e.g. neutral faction) + if (!me->IsValidAttackTarget(who)) + return false; + //too far away and no free sight? if (me->IsWithinDistInMap(who, SMART_MAX_AID_DIST) && me->IsWithinLOSInMap(who)) { From baea9a845a77b39004b62a645384591fde1a275f Mon Sep 17 00:00:00 2001 From: sogladev Date: Tue, 28 Apr 2026 13:32:07 +0200 Subject: [PATCH 34/92] fix(DB/Loot): Allow 10m Heart of Magic to be looted in 25man EoE (#25443) --- .../updates/pending_db_world/rev_1776028939227348948.sql | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1776028939227348948.sql diff --git a/data/sql/updates/pending_db_world/rev_1776028939227348948.sql b/data/sql/updates/pending_db_world/rev_1776028939227348948.sql new file mode 100644 index 000000000..0beb78677 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1776028939227348948.sql @@ -0,0 +1,9 @@ +-- +DELETE FROM `gameobject_loot_template` WHERE (`Entry` = 26860); +INSERT INTO `gameobject_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(26860, 44651, 0, 100, 1, 1, 1, 1, 1, 'Heart of Magic - Heart of Magic - 25m'), +(26860, 44650, 0, 100, 1, 1, 2, 1, 1, 'Heart of Magic - Heart of Magic - 10m'); + +DELETE FROM `gameobject_questitem` WHERE (`GameObjectEntry` = 194159) AND (`Idx` IN (1)); +INSERT INTO `gameobject_questitem` (`GameObjectEntry`, `Idx`, `ItemId`, `VerifiedBuild`) VALUES +(194159, 1, 44650, 0); From 8afe06c61920b252f4864ee7145824dd86f038b0 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Tue, 28 Apr 2026 07:32:52 -0400 Subject: [PATCH 35/92] fix(DB/Achievement): Correct reward text for `What A Long, Strange Trip It's Been` (#25629) --- data/sql/updates/pending_db_world/what-a-long-strange-trip.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 data/sql/updates/pending_db_world/what-a-long-strange-trip.sql diff --git a/data/sql/updates/pending_db_world/what-a-long-strange-trip.sql b/data/sql/updates/pending_db_world/what-a-long-strange-trip.sql new file mode 100644 index 000000000..7e3ae71b1 --- /dev/null +++ b/data/sql/updates/pending_db_world/what-a-long-strange-trip.sql @@ -0,0 +1 @@ +UPDATE `achievement_reward` SET `Body` = 'With the drums of war pounding in the distance, it is easy for the denizens of Azeroth to forget all that life has to offer.$B$BYou, on the other hand, have maintained the dignity of the good races of Azeroth with your ability to remember what we fight for. To not celebrate our victories is another form of defeat. Remember that well, Reveler.$B$BMay others be inspired by your good cheer,$B$BAlexstrasza the Life-Binder' WHERE `ID` IN (2144, 2145); From b116adb00a4920f46aa1d831147989c7d4c662a6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Apr 2026 11:33:16 +0000 Subject: [PATCH 36/92] chore(DB): import pending files Referenced commit(s): baea9a845a77b39004b62a645384591fde1a275f --- .../rev_1776028939227348948.sql => db_world/2026_04_28_00.sql} | 1 + .../what-a-long-strange-trip.sql => db_world/2026_04_28_01.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/rev_1776028939227348948.sql => db_world/2026_04_28_00.sql} (93%) rename data/sql/updates/{pending_db_world/what-a-long-strange-trip.sql => db_world/2026_04_28_01.sql} (91%) diff --git a/data/sql/updates/pending_db_world/rev_1776028939227348948.sql b/data/sql/updates/db_world/2026_04_28_00.sql similarity index 93% rename from data/sql/updates/pending_db_world/rev_1776028939227348948.sql rename to data/sql/updates/db_world/2026_04_28_00.sql index 0beb78677..ccbf28bd3 100644 --- a/data/sql/updates/pending_db_world/rev_1776028939227348948.sql +++ b/data/sql/updates/db_world/2026_04_28_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_27_00 -> 2026_04_28_00 -- DELETE FROM `gameobject_loot_template` WHERE (`Entry` = 26860); INSERT INTO `gameobject_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES diff --git a/data/sql/updates/pending_db_world/what-a-long-strange-trip.sql b/data/sql/updates/db_world/2026_04_28_01.sql similarity index 91% rename from data/sql/updates/pending_db_world/what-a-long-strange-trip.sql rename to data/sql/updates/db_world/2026_04_28_01.sql index 7e3ae71b1..6eac72f17 100644 --- a/data/sql/updates/pending_db_world/what-a-long-strange-trip.sql +++ b/data/sql/updates/db_world/2026_04_28_01.sql @@ -1 +1,2 @@ +-- DB update 2026_04_28_00 -> 2026_04_28_01 UPDATE `achievement_reward` SET `Body` = 'With the drums of war pounding in the distance, it is easy for the denizens of Azeroth to forget all that life has to offer.$B$BYou, on the other hand, have maintained the dignity of the good races of Azeroth with your ability to remember what we fight for. To not celebrate our victories is another form of defeat. Remember that well, Reveler.$B$BMay others be inspired by your good cheer,$B$BAlexstrasza the Life-Binder' WHERE `ID` IN (2144, 2145); From 5d0c19abfc1ecc1ca23e3288c47b48b61d9194fa Mon Sep 17 00:00:00 2001 From: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Date: Tue, 28 Apr 2026 13:33:47 +0200 Subject: [PATCH 37/92] fix(CI): bump mysql chocolatey version (#25625) --- apps/installer/includes/os_configs/windows.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/installer/includes/os_configs/windows.sh b/apps/installer/includes/os_configs/windows.sh index 6f49052a3..6957ba62d 100644 --- a/apps/installer/includes/os_configs/windows.sh +++ b/apps/installer/includes/os_configs/windows.sh @@ -26,4 +26,4 @@ choco install -y --skip-checksums "${INSTALL_ARGS[@]}" cmake.install -y --insta choco install -y --skip-checksums "${INSTALL_ARGS[@]}" visualstudio2022-workload-nativedesktop choco install -y --skip-checksums "${INSTALL_ARGS[@]}" openssl --force --version=3.6.2 choco install -y --skip-checksums "${INSTALL_ARGS[@]}" boost-msvc-14.3 --force --version=1.87.0 -choco install -y --skip-checksums "${INSTALL_ARGS[@]}" mysql --force --version=8.4.6 +choco install -y --skip-checksums "${INSTALL_ARGS[@]}" mysql --force --version=8.4.9 From 975ac69b3bed877896342e7be2510524f4122ccc Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Tue, 28 Apr 2026 12:08:07 +0000 Subject: [PATCH 38/92] fix(DB/SmartAI): Savage Hill Scavenger no longer stays stationary during combat (#25623) --- data/sql/updates/pending_db_world/rev_1777307230974435500.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777307230974435500.sql diff --git a/data/sql/updates/pending_db_world/rev_1777307230974435500.sql b/data/sql/updates/pending_db_world/rev_1777307230974435500.sql new file mode 100644 index 000000000..99bb5d4bb --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777307230974435500.sql @@ -0,0 +1,2 @@ +-- +UPDATE `smart_scripts` SET `action_param2` = 0 WHERE `entryorguid` = 29404 AND `source_type` = 0 AND `id` = 0; From 0a4a4ed5c79cc611d9873b70ec4b818788a3daf7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Apr 2026 12:12:07 +0000 Subject: [PATCH 39/92] chore(DB): import pending files Referenced commit(s): 975ac69b3bed877896342e7be2510524f4122ccc --- .../rev_1777307230974435500.sql => db_world/2026_04_28_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777307230974435500.sql => db_world/2026_04_28_02.sql} (72%) diff --git a/data/sql/updates/pending_db_world/rev_1777307230974435500.sql b/data/sql/updates/db_world/2026_04_28_02.sql similarity index 72% rename from data/sql/updates/pending_db_world/rev_1777307230974435500.sql rename to data/sql/updates/db_world/2026_04_28_02.sql index 99bb5d4bb..e5cb102de 100644 --- a/data/sql/updates/pending_db_world/rev_1777307230974435500.sql +++ b/data/sql/updates/db_world/2026_04_28_02.sql @@ -1,2 +1,3 @@ +-- DB update 2026_04_28_01 -> 2026_04_28_02 -- UPDATE `smart_scripts` SET `action_param2` = 0 WHERE `entryorguid` = 29404 AND `source_type` = 0 AND `id` = 0; From 9fbc38ae63ba4125bb4aa9e1e2fe3d22e02dbc6a Mon Sep 17 00:00:00 2001 From: kb31 Date: Tue, 28 Apr 2026 13:22:32 +0100 Subject: [PATCH 40/92] fix(DB/Loot): Fix incorrect loot pool structure for 9 Naxxramas 25-man bosses (#25276) --- .../pending_db_world/rev_1774823456789.sql | 342 ++++++++++++++++++ 1 file changed, 342 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1774823456789.sql diff --git a/data/sql/updates/pending_db_world/rev_1774823456789.sql b/data/sql/updates/pending_db_world/rev_1774823456789.sql new file mode 100644 index 000000000..2f7513a2d --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1774823456789.sql @@ -0,0 +1,342 @@ +-- Naxxramas loot pool structure fix +-- Splits single GroupId pools into multiple pools matching retail WotLK behavior +-- Source: Wowhead WotLK 25-man drop rates, WowDB cross-validation, ChromieCraft #9131 (Anub'Rekhan) + +-- ===================== +-- Spider Wing +-- ===================== + +-- Anub'Rekhan 25-man (4 pools: 5+4+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29249 AND `Item`=1 AND `Reference`=34137; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34137 AND `Item`=39701; -- Dawnwalkers +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34137 AND `Item`=39702; -- Arachnoid Gold Band +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34137 AND `Item`=39703; -- Rescinding Grips +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34137 AND `Item`=39704; -- Pauldrons of Unnatural Death +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34137 AND `Item`=39706; -- Sabatons of Sudden Reprisal + +-- Pool B (4 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34137 AND `Item`=39712; -- Gemmed Wand of the Nerubians +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34137 AND `Item`=39714; -- Webbed Death +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34137 AND `Item`=39716; -- Shield of Assimilation +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34137 AND `Item`=39717; -- Inexorable Sabatons + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34137 AND `Item`=39718; -- Corpse Scarab Handguards +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34137 AND `Item`=39719; -- Mantle of the Locusts +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34137 AND `Item`=39720; -- Leggings of Atrophy +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34137 AND `Item`=39721; -- Sash of the Parlor +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34137 AND `Item`=39722; -- Swarm Bindings + +-- Pool D (9 items, shared jewelry) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40064; -- Thunderstorm Amulet +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40065; -- Fool's Trial +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40069; -- Heritage +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40071; -- Chains of Adoration +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40074; -- Strong-Handed Ring +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40075; -- Ruthlessness +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40080; -- Lost Jewel +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40107; -- Sand-Worn Band +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34137 AND `Item`=40108; -- Seized Beauty + +-- Grand Widow Faerlina 25-man (4 pools: 5+5+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29268 AND `Item`=1 AND `Reference`=34138; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34138 AND `Item`=39723; -- Fire-Scorched Greathelm +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34138 AND `Item`=39724; -- Cult's Chestguard +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34138 AND `Item`=39725; -- Epaulets of the Grieving Servant +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34138 AND `Item`=39726; -- Callous-Hearted Gauntlets +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34138 AND `Item`=39727; -- Dislocating Handguards + +-- Pool B (5 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34138 AND `Item`=39728; -- Totem of Misery +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34138 AND `Item`=39729; -- Bracers of the Tyrant +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34138 AND `Item`=39730; -- Widow's Fury +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34138 AND `Item`=39731; -- Punctilious Bindings +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34138 AND `Item`=39732; -- Faerlina's Madness + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34138 AND `Item`=39733; -- Gloves of Token Respect +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34138 AND `Item`=39734; -- Atonement Greaves +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34138 AND `Item`=39735; -- Belt of False Dignity +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34138 AND `Item`=39756; -- Tunic of Prejudice +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34138 AND `Item`=39757; -- Idol of Worship + +-- Pool D (9 items, shared jewelry) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40064; -- Thunderstorm Amulet +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40065; -- Fool's Trial +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40069; -- Heritage +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40071; -- Chains of Adoration +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40074; -- Strong-Handed Ring +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40075; -- Ruthlessness +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40080; -- Lost Jewel +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40107; -- Sand-Worn Band +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34138 AND `Item`=40108; -- Seized Beauty + +-- Maexxna 25-man (4 pools: 5+5+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29278 AND `Item`=1 AND `Reference`=34139; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34139 AND `Item`=39758; -- The Jawbone +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34139 AND `Item`=39759; -- Ablative Chitin Girdle +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34139 AND `Item`=39760; -- Helm of Diminished Pride +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34139 AND `Item`=39761; -- Infectious Skitterer Leggings +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34139 AND `Item`=39762; -- Torn Web Wrapping + +-- Pool B (5 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34139 AND `Item`=39763; -- Wraith Strike +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34139 AND `Item`=39764; -- Bindings of the Hapless Prey +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34139 AND `Item`=39765; -- Sinner's Bindings +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34139 AND `Item`=39766; -- Matriarch's Spawn +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34139 AND `Item`=39767; -- Undiminished Battleplate + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34139 AND `Item`=39768; -- Cowl of the Perished +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34139 AND `Item`=40060; -- Distorted Limbs +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34139 AND `Item`=40061; -- Quivering Tunic +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34139 AND `Item`=40062; -- Digested Silken Robes +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34139 AND `Item`=40063; -- Mantle of Shattered Kinship + +-- Pool D (9 items, shared cloaks/trinkets) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40250; -- Aged Winter Cloak +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40251; -- Shroud of Luminosity +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40252; -- Cloak of the Shadowed Sun +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40253; -- Shawl of the Old Maid +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40254; -- Cloak of Averted Crisis +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40255; -- Dying Curse +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40256; -- Grim Toll +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40257; -- Defender's Code +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34139 AND `Item`=40258; -- Forethought Talisman + +-- ===================== +-- Plague Wing +-- ===================== + +-- Noth the Plaguebringer 25-man (4 pools: 5+5+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29615 AND `Item`=1 AND `Reference`=34147; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34147 AND `Item`=40184; -- Crippled Treads +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34147 AND `Item`=40185; -- Shoulderguards of Opportunity +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34147 AND `Item`=40186; -- Thrusting Bands +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34147 AND `Item`=40187; -- Poignant Sabatons +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34147 AND `Item`=40188; -- Gauntlets of the Disobedient + +-- Pool B (5 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34147 AND `Item`=40189; -- Angry Dread +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34147 AND `Item`=40190; -- Spinning Fate +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34147 AND `Item`=40191; -- Libram of Radiance +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34147 AND `Item`=40192; -- Accursed Spine +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34147 AND `Item`=40193; -- Tunic of Masked Suffering + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34147 AND `Item`=40196; -- Legguards of the Undisturbed +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34147 AND `Item`=40197; -- Gloves of the Fallen Wizard +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34147 AND `Item`=40198; -- Bands of Impurity +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34147 AND `Item`=40200; -- Belt of Potent Chanting +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34147 AND `Item`=40602; -- Robes of Mutation + +-- Pool D (9 items, shared jewelry) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40064; -- Thunderstorm Amulet +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40065; -- Fool's Trial +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40069; -- Heritage +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40071; -- Chains of Adoration +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40074; -- Strong-Handed Ring +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40075; -- Ruthlessness +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40080; -- Lost Jewel +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40107; -- Sand-Worn Band +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34147 AND `Item`=40108; -- Seized Beauty + +-- Heigan the Unclean 25-man (4 pools: 5+5+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29701 AND `Item`=1 AND `Reference`=34148; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34148 AND `Item`=40201; -- Leggings of Colossal Strides +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34148 AND `Item`=40203; -- Breastplate of Tormented Rage +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34148 AND `Item`=40204; -- Legguards of the Apostle +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34148 AND `Item`=40205; -- Stalk-Skin Belt +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34148 AND `Item`=40206; -- Iron-Spring Jumpers + +-- Pool B (5 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34148 AND `Item`=40207; -- Sigil of Awareness +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34148 AND `Item`=40208; -- Cryptfiend's Bite +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34148 AND `Item`=40209; -- Bindings of the Decrepit +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34148 AND `Item`=40210; -- Chestguard of Bitter Charms +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34148 AND `Item`=40233; -- The Undeath Carrier + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34148 AND `Item`=40234; -- Heigan's Putrid Vestments +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34148 AND `Item`=40235; -- Helm of Pilgrimage +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34148 AND `Item`=40236; -- Serene Echoes +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34148 AND `Item`=40237; -- Eruption-Scarred Boots +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34148 AND `Item`=40238; -- Gloves of the Dancing Bear + +-- Pool D (9 items, shared cloaks/trinkets) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40250; -- Aged Winter Cloak +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40251; -- Shroud of Luminosity +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40252; -- Cloak of the Shadowed Sun +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40253; -- Shawl of the Old Maid +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40254; -- Cloak of Averted Crisis +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40255; -- Dying Curse +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40256; -- Grim Toll +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40257; -- Defender's Code +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34148 AND `Item`=40258; -- Forethought Talisman + +-- ===================== +-- Military Wing +-- ===================== + +-- Instructor Razuvious 25-man (4 pools: 5+5+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29940 AND `Item`=1 AND `Reference`=34144; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34144 AND `Item`=40305; -- Spaulders of Egotism +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34144 AND `Item`=40306; -- Bracers of the Unholy Knight +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34144 AND `Item`=40315; -- Shoulderpads of Secret Arts +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34144 AND `Item`=40316; -- Gauntlets of Guiding Touch +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34144 AND `Item`=40317; -- Girdle of Razuvious + +-- Pool B (5 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34144 AND `Item`=40318; -- Legplates of Double Strikes +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34144 AND `Item`=40319; -- Chestpiece of Suspicion +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34144 AND `Item`=40320; -- Faithful Steel Sabatons +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34144 AND `Item`=40321; -- Idol of the Shooting Star +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34144 AND `Item`=40322; -- Totem of Dueling + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34144 AND `Item`=40323; -- Esteemed Bindings +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34144 AND `Item`=40324; -- Bands of Mutual Respect +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34144 AND `Item`=40325; -- Bindings of the Expansive Mind +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34144 AND `Item`=40326; -- Boots of Forlorn Wishes +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34144 AND `Item`=40327; -- Girdle of Recuperation + +-- Pool D (9 items, shared jewelry) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40064; -- Thunderstorm Amulet +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40065; -- Fool's Trial +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40069; -- Heritage +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40071; -- Chains of Adoration +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40074; -- Strong-Handed Ring +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40075; -- Ruthlessness +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40080; -- Lost Jewel +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40107; -- Sand-Worn Band +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34144 AND `Item`=40108; -- Seized Beauty + +-- Gothik the Harvester 25-man (4 pools: 5+5+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29955 AND `Item`=1 AND `Reference`=34145; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34145 AND `Item`=40328; -- Helm of Vital Protection +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34145 AND `Item`=40329; -- Hood of the Exodus +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34145 AND `Item`=40330; -- Bracers of Unrelenting Attack +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34145 AND `Item`=40331; -- Leggings of Failed Escape +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34145 AND `Item`=40332; -- Abetment Bracers + +-- Pool B (5 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34145 AND `Item`=40333; -- Leggings of Fleeting Moments +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34145 AND `Item`=40334; -- Burdened Shoulderplates +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34145 AND `Item`=40335; -- Touch of Horror +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34145 AND `Item`=40336; -- Life and Death +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34145 AND `Item`=40337; -- Libram of Resurgence + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34145 AND `Item`=40338; -- Bindings of Yearning +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34145 AND `Item`=40339; -- Gothik's Cowl +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34145 AND `Item`=40340; -- Helm of Unleashed Energy +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34145 AND `Item`=40341; -- Shackled Cinch +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34145 AND `Item`=40342; -- Idol of Awakening + +-- Pool D (9 items, shared cloaks/trinkets) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40250; -- Aged Winter Cloak +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40251; -- Shroud of Luminosity +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40252; -- Cloak of the Shadowed Sun +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40253; -- Shawl of the Old Maid +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40254; -- Cloak of Averted Crisis +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40255; -- Dying Curse +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40256; -- Grim Toll +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40257; -- Defender's Code +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34145 AND `Item`=40258; -- Forethought Talisman + +-- ===================== +-- Construct Wing +-- ===================== + +-- Patchwerk 25-man (4 pools: 5+5+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29324 AND `Item`=1 AND `Reference`=34140; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34140 AND `Item`=40259; -- Waistguard of Divine Grace +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34140 AND `Item`=40260; -- Belt of the Tortured +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34140 AND `Item`=40261; -- Crude Discolored Battlegrips +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34140 AND `Item`=40262; -- Gloves of Calculated Risk +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34140 AND `Item`=40263; -- Fleshless Girdle + +-- Pool B (5 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34140 AND `Item`=40264; -- Split Greathammer +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34140 AND `Item`=40265; -- Arrowsong +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34140 AND `Item`=40266; -- Hero's Surrender +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34140 AND `Item`=40267; -- Totem of Hex +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34140 AND `Item`=40268; -- Libram of Tolerance + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34140 AND `Item`=40269; -- Boots of Persuasion +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34140 AND `Item`=40270; -- Boots of Septic Wounds +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34140 AND `Item`=40271; -- Sash of Solitude +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34140 AND `Item`=40272; -- Girdle of the Gambit +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34140 AND `Item`=40273; -- Surplus Limb + +-- Pool D (9 items, shared jewelry) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40064; -- Thunderstorm Amulet +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40065; -- Fool's Trial +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40069; -- Heritage +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40071; -- Chains of Adoration +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40074; -- Strong-Handed Ring +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40075; -- Ruthlessness +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40080; -- Lost Jewel +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40107; -- Sand-Worn Band +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34140 AND `Item`=40108; -- Seized Beauty + +-- Grobbulus 25-man (4 pools: 5+5+5+9) +UPDATE `creature_loot_template` SET `MinCount`=1, `MaxCount`=1 + WHERE `Entry`=29373 AND `Item`=1 AND `Reference`=34141; + +-- Pool A (5 items) +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34141 AND `Item`=40274; -- Bracers of Liberation +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34141 AND `Item`=40275; -- Depraved Linked Belt +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34141 AND `Item`=40277; -- Tunic of Indulgence +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34141 AND `Item`=40278; -- Girdle of Chivalry +UPDATE `reference_loot_template` SET `GroupId`=1 WHERE `Entry`=34141 AND `Item`=40279; -- Chestguard of the Exhausted + +-- Pool B (5 items) +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34141 AND `Item`=40280; -- Origin of Nightmares +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34141 AND `Item`=40281; -- Twilight Mist +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34141 AND `Item`=40282; -- Slime Stream Bands +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34141 AND `Item`=40283; -- Fallout Impervious Tunic +UPDATE `reference_loot_template` SET `GroupId`=2 WHERE `Entry`=34141 AND `Item`=40284; -- Plague Igniter + +-- Pool C (5 items) +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34141 AND `Item`=40285; -- Desecrated Past +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34141 AND `Item`=40287; -- Cowl of Vanity +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34141 AND `Item`=40288; -- Spaulders of Incoherence +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34141 AND `Item`=40289; -- Sympathetic Amice +UPDATE `reference_loot_template` SET `GroupId`=3 WHERE `Entry`=34141 AND `Item`=40351; -- Mantle of the Fatigued Sage + +-- Pool D (9 items, shared cloaks/trinkets) +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40250; -- Aged Winter Cloak +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40251; -- Shroud of Luminosity +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40252; -- Cloak of the Shadowed Sun +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40253; -- Shawl of the Old Maid +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40254; -- Cloak of Averted Crisis +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40255; -- Dying Curse +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40256; -- Grim Toll +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40257; -- Defender's Code +UPDATE `reference_loot_template` SET `GroupId`=4 WHERE `Entry`=34141 AND `Item`=40258; -- Forethought Talisman From 0e4e795ea98b938d17040f3a3175e6adcca7e9f3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Apr 2026 12:24:01 +0000 Subject: [PATCH 41/92] chore(DB): import pending files Referenced commit(s): 9fbc38ae63ba4125bb4aa9e1e2fe3d22e02dbc6a --- .../rev_1774823456789.sql => db_world/2026_04_28_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1774823456789.sql => db_world/2026_04_28_03.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1774823456789.sql b/data/sql/updates/db_world/2026_04_28_03.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1774823456789.sql rename to data/sql/updates/db_world/2026_04_28_03.sql index 2f7513a2d..96bd2869e 100644 --- a/data/sql/updates/pending_db_world/rev_1774823456789.sql +++ b/data/sql/updates/db_world/2026_04_28_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_28_02 -> 2026_04_28_03 -- Naxxramas loot pool structure fix -- Splits single GroupId pools into multiple pools matching retail WotLK behavior -- Source: Wowhead WotLK 25-man drop rates, WowDB cross-validation, ChromieCraft #9131 (Anub'Rekhan) From 7958034c27dca05a26a58b287ef546f8e5eb9767 Mon Sep 17 00:00:00 2001 From: sogladev Date: Tue, 28 Apr 2026 23:18:30 +0200 Subject: [PATCH 42/92] fix(Scripts/Outland): store creatures as GUIDs instead of raw pointers (#25642) --- .../Outland/zone_hellfire_peninsula.cpp | 39 ++++++++++++------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp index b8dfd4deb..97c9fdf91 100644 --- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp +++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp @@ -469,38 +469,51 @@ public: struct go_beaconAI : public GameObjectAI { - go_beaconAI(GameObject* gameObject) : GameObjectAI(gameObject) { } + explicit go_beaconAI(GameObject* gameObject) : GameObjectAI(gameObject) { } - std::list creatureList; - - void OnStateChanged(uint32 state, Unit* /*unit*/) override + void OnStateChanged(uint32 state, Unit* /*unit*/) override { if (state == GO_ACTIVATED) { + std::list creatureList; + + _whelpGUIDs.clear(); me->GetCreaturesWithEntryInRange(creatureList, 40, NPC_STONESCHYE_WHELP); + + for (Creature* whelp : creatureList) { - for (Creature* whelp : creatureList) - { - if (whelp && whelp->IsAlive() && !whelp->IsInCombat() && whelp->GetMotionMaster()->GetCurrentMovementGeneratorType() != HOME_MOTION_TYPE) - whelp->GetMotionMaster()->MovePoint(0, me->GetNearPosition(4.0f, whelp->GetOrientation())); - } + if (!whelp) + continue; + + _whelpGUIDs.push_back(whelp->GetGUID()); + + MotionMaster* motionMaster = whelp->GetMotionMaster(); + if (whelp->IsAlive() && !whelp->IsInCombat() && motionMaster && motionMaster->GetCurrentMovementGeneratorType() != HOME_MOTION_TYPE) + motionMaster->MovePoint(0, me->GetNearPosition(4.0f, whelp->GetOrientation())); } } else if (state == GO_JUST_DEACTIVATED) { + for (ObjectGuid const& guid : _whelpGUIDs) { - for (Creature* whelp : creatureList) + if (Creature* whelp = ObjectAccessor::GetCreature(*me, guid)) { - if (whelp && whelp->IsAlive() && !whelp->IsInCombat() && whelp->GetMotionMaster()->GetCurrentMovementGeneratorType() != HOME_MOTION_TYPE) - whelp->GetMotionMaster()->MoveTargetedHome(); + MotionMaster* motionMaster = whelp->GetMotionMaster(); + if (whelp->IsAlive() && !whelp->IsInCombat() && motionMaster && motionMaster->GetCurrentMovementGeneratorType() != HOME_MOTION_TYPE) + motionMaster->MoveTargetedHome(); } } + + _whelpGUIDs.clear(); } else { - creatureList.clear(); + _whelpGUIDs.clear(); } } + + private: + GuidList _whelpGUIDs; }; GameObjectAI* GetAI(GameObject* go) const override From c8a3e121987b8ae8160cd3b227b7ea422d278e39 Mon Sep 17 00:00:00 2001 From: Apatia <107246426+Apathyxia@users.noreply.github.com> Date: Wed, 29 Apr 2026 00:08:36 +0200 Subject: [PATCH 43/92] fix(DB/Creature): Add S6 offpiece vendors to spawn (#25571) --- .../updates/pending_db_world/s6_offpiece_spawn.sql | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 data/sql/updates/pending_db_world/s6_offpiece_spawn.sql diff --git a/data/sql/updates/pending_db_world/s6_offpiece_spawn.sql b/data/sql/updates/pending_db_world/s6_offpiece_spawn.sql new file mode 100644 index 000000000..a7a4dd26c --- /dev/null +++ b/data/sql/updates/pending_db_world/s6_offpiece_spawn.sql @@ -0,0 +1,14 @@ +-- Adds S6 off piece vendors to creature table, and then adds them to S6 event. +DELETE FROM `creature` WHERE (`guid` IN (208509, 208510, 208511, 208512) AND `id1` IN (34036, 34058, 34073, 34076)); +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 +(208509, 34036, 0, 0, 1, 0, 0, 1, 1, 1, 1669.09, -4196.78, 56.3827, 4.10416, 25, 0, 0, 11828, 0, 0, 0, 0, 0, '', 0, 0, NULL), +(208510, 34058, 0, 0, 1, 0, 0, 1, 1, 0, 1673.07, -4201.89, 56.3826, 3.62927, 25, 0, 0, 55888, 0, 0, 0, 0, 0, '', 0, 0, NULL), +(208511, 34073, 0, 0, 0, 0, 0, 1, 1, 1, -8781.18, 419.883, 105.233, 6.18459, 180, 0, 0, 7048, 0, 0, 0, 0, 0, '', 0, 0, NULL), +(208512, 34076, 0, 0, 0, 0, 0, 1, 1, 1, -8773.78, 425.804, 105.233, 4.80621, 180, 0, 0, 11828, 0, 0, 0, 0, 0, '', 0, 0, NULL); + +DELETE FROM `game_event_creature` WHERE (`guid` IN (208509, 208510, 208511, 208512) AND `eventEntry` = 58); +INSERT INTO `game_event_creature` (`guid`, `eventEntry`) VALUES +(208509, 58), +(208510, 58), +(208511, 58), +(208512, 58); From 684418cfda696325824539a8c45950a310ee5cbe Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Apr 2026 22:09:44 +0000 Subject: [PATCH 44/92] chore(DB): import pending files Referenced commit(s): c8a3e121987b8ae8160cd3b227b7ea422d278e39 --- .../s6_offpiece_spawn.sql => db_world/2026_04_28_04.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/s6_offpiece_spawn.sql => db_world/2026_04_28_04.sql} (96%) diff --git a/data/sql/updates/pending_db_world/s6_offpiece_spawn.sql b/data/sql/updates/db_world/2026_04_28_04.sql similarity index 96% rename from data/sql/updates/pending_db_world/s6_offpiece_spawn.sql rename to data/sql/updates/db_world/2026_04_28_04.sql index a7a4dd26c..cbbf43d82 100644 --- a/data/sql/updates/pending_db_world/s6_offpiece_spawn.sql +++ b/data/sql/updates/db_world/2026_04_28_04.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_28_03 -> 2026_04_28_04 -- Adds S6 off piece vendors to creature table, and then adds them to S6 event. DELETE FROM `creature` WHERE (`guid` IN (208509, 208510, 208511, 208512) AND `id1` IN (34036, 34058, 34073, 34076)); 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 From 13d6d3cf40891a15e05241710118c09c8e64e903 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Tue, 28 Apr 2026 18:06:56 -0500 Subject: [PATCH 45/92] fix(DB/Spells): Ribbon of Sacrifice direct heal proc (#25644) Co-authored-by: blinkysc --- data/sql/updates/pending_db_world/rev_1777416298415433931.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777416298415433931.sql diff --git a/data/sql/updates/pending_db_world/rev_1777416298415433931.sql b/data/sql/updates/pending_db_world/rev_1777416298415433931.sql new file mode 100644 index 000000000..8a14de2be --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777416298415433931.sql @@ -0,0 +1,3 @@ +-- Ribbon of Sacrifice (28590) - Blessing of Life proc on direct heals +DELETE FROM `spell_proc` WHERE `SpellId` = 38332; +INSERT INTO `spell_proc` (`SpellId`, `SchoolMask`, `SpellFamilyName`, `SpellFamilyMask0`, `SpellFamilyMask1`, `SpellFamilyMask2`, `ProcFlags`, `SpellTypeMask`, `SpellPhaseMask`, `HitMask`, `AttributesMask`, `DisableEffectsMask`, `ProcsPerMinute`, `Chance`, `Cooldown`, `Charges`) VALUES (38332, 0, 0, 0, 0, 0, 0x4000, 2, 2, 0, 0, 0, 0, 100, 0, 0); From fc34ec72f49f43b0c5f20a1825fd18f6d612bcb3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Apr 2026 23:08:08 +0000 Subject: [PATCH 46/92] chore(DB): import pending files Referenced commit(s): 13d6d3cf40891a15e05241710118c09c8e64e903 --- .../rev_1777416298415433931.sql => db_world/2026_04_28_05.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777416298415433931.sql => db_world/2026_04_28_05.sql} (91%) diff --git a/data/sql/updates/pending_db_world/rev_1777416298415433931.sql b/data/sql/updates/db_world/2026_04_28_05.sql similarity index 91% rename from data/sql/updates/pending_db_world/rev_1777416298415433931.sql rename to data/sql/updates/db_world/2026_04_28_05.sql index 8a14de2be..0eee825b7 100644 --- a/data/sql/updates/pending_db_world/rev_1777416298415433931.sql +++ b/data/sql/updates/db_world/2026_04_28_05.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_28_04 -> 2026_04_28_05 -- Ribbon of Sacrifice (28590) - Blessing of Life proc on direct heals DELETE FROM `spell_proc` WHERE `SpellId` = 38332; INSERT INTO `spell_proc` (`SpellId`, `SchoolMask`, `SpellFamilyName`, `SpellFamilyMask0`, `SpellFamilyMask1`, `SpellFamilyMask2`, `ProcFlags`, `SpellTypeMask`, `SpellPhaseMask`, `HitMask`, `AttributesMask`, `DisableEffectsMask`, `ProcsPerMinute`, `Chance`, `Cooldown`, `Charges`) VALUES (38332, 0, 0, 0, 0, 0, 0x4000, 2, 2, 0, 0, 0, 0, 100, 0, 0); From 40f0ed95551f61fe6958ab7aaa162605e619a6ff Mon Sep 17 00:00:00 2001 From: youdeoo Date: Wed, 29 Apr 2026 07:31:35 +0800 Subject: [PATCH 47/92] fix(DB/Spells): Fix Spark of Life proc on craft and equip (#25449) --- data/sql/updates/pending_db_world/rev_1776046976446994900.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1776046976446994900.sql diff --git a/data/sql/updates/pending_db_world/rev_1776046976446994900.sql b/data/sql/updates/pending_db_world/rev_1776046976446994900.sql new file mode 100644 index 000000000..e083f43a2 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1776046976446994900.sql @@ -0,0 +1,2 @@ +-- Fix Spark of Life (spell 60519) proc on craft and equip +UPDATE `spell_proc` SET `SpellTypeMask` = 3 WHERE `SpellId` = 60519; From 7d69148ce094143fac70f4c4493ff2af42078eff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 28 Apr 2026 23:32:51 +0000 Subject: [PATCH 48/92] chore(DB): import pending files Referenced commit(s): 40f0ed95551f61fe6958ab7aaa162605e619a6ff --- .../rev_1776046976446994900.sql => db_world/2026_04_28_06.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1776046976446994900.sql => db_world/2026_04_28_06.sql} (74%) diff --git a/data/sql/updates/pending_db_world/rev_1776046976446994900.sql b/data/sql/updates/db_world/2026_04_28_06.sql similarity index 74% rename from data/sql/updates/pending_db_world/rev_1776046976446994900.sql rename to data/sql/updates/db_world/2026_04_28_06.sql index e083f43a2..88b4e4959 100644 --- a/data/sql/updates/pending_db_world/rev_1776046976446994900.sql +++ b/data/sql/updates/db_world/2026_04_28_06.sql @@ -1,2 +1,3 @@ +-- DB update 2026_04_28_05 -> 2026_04_28_06 -- Fix Spark of Life (spell 60519) proc on craft and equip UPDATE `spell_proc` SET `SpellTypeMask` = 3 WHERE `SpellId` = 60519; From ed8341c860bc485bdcd416c6f54aa223df53260b Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 28 Apr 2026 21:00:09 -0300 Subject: [PATCH 49/92] fix(DB/SAI): Remove Baneflight defender summons (#25643) --- data/sql/updates/pending_db_world/rev_1777414508507884400.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777414508507884400.sql diff --git a/data/sql/updates/pending_db_world/rev_1777414508507884400.sql b/data/sql/updates/pending_db_world/rev_1777414508507884400.sql new file mode 100644 index 000000000..78361bfba --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777414508507884400.sql @@ -0,0 +1,2 @@ +DELETE FROM `smart_scripts` WHERE `entryorguid`=28615 AND `source_type`=0 AND `id` IN (0,1); +UPDATE `creature_template` SET `AIName`='' WHERE `entry`=28615; From 10013a9fd02962b8511b50987578ec568810bb82 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 00:01:36 +0000 Subject: [PATCH 50/92] chore(DB): import pending files Referenced commit(s): ed8341c860bc485bdcd416c6f54aa223df53260b --- .../rev_1777414508507884400.sql => db_world/2026_04_29_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777414508507884400.sql => db_world/2026_04_29_00.sql} (78%) diff --git a/data/sql/updates/pending_db_world/rev_1777414508507884400.sql b/data/sql/updates/db_world/2026_04_29_00.sql similarity index 78% rename from data/sql/updates/pending_db_world/rev_1777414508507884400.sql rename to data/sql/updates/db_world/2026_04_29_00.sql index 78361bfba..f116c5564 100644 --- a/data/sql/updates/pending_db_world/rev_1777414508507884400.sql +++ b/data/sql/updates/db_world/2026_04_29_00.sql @@ -1,2 +1,3 @@ +-- DB update 2026_04_28_06 -> 2026_04_29_00 DELETE FROM `smart_scripts` WHERE `entryorguid`=28615 AND `source_type`=0 AND `id` IN (0,1); UPDATE `creature_template` SET `AIName`='' WHERE `entry`=28615; From b6b6b55ed314a70d57992e525bbae87edba97ac6 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Wed, 29 Apr 2026 02:22:15 +0200 Subject: [PATCH 51/92] fix(DB/SAI): Reanimated Crusaders no longer spam Hammer of Injustice. (#25638) --- data/sql/updates/pending_db_world/rev_1777394998.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777394998.sql diff --git a/data/sql/updates/pending_db_world/rev_1777394998.sql b/data/sql/updates/pending_db_world/rev_1777394998.sql new file mode 100644 index 000000000..531f9fa69 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777394998.sql @@ -0,0 +1,12 @@ + +-- Add CastFlags 32 and update comments. +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 30202; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 30202); +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 +(30202, 0, 0, 1, 8, 0, 100, 1, 57806, 0, 0, 0, 0, 0, 11, 57808, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Crusader - On Spellhit \'Sprinkle Holy Water\' - Cast \'Freed Crusader Soul\' (No Repeat)'), +(30202, 0, 1, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Crusader - On Spellhit \'Sprinkle Holy Water\' - Despawn Instant (No Repeat)'), +(30202, 0, 2, 0, 0, 0, 100, 0, 1000, 5000, 6500, 12000, 0, 0, 11, 32674, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Crusader - In Combat - Cast \'Avenger`s Shield\''), +(30202, 0, 3, 0, 0, 0, 100, 0, 7000, 12000, 8500, 20000, 0, 0, 11, 58154, 32, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Crusader - In Combat - Cast \'Hammer of Injustice\''), +(30202, 0, 4, 0, 2, 0, 100, 0, 10, 90, 7000, 15000, 0, 0, 11, 58153, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Crusader - Between 10-90% Health - Cast \'Unholy Light\''), +(30202, 0, 5, 0, 14, 0, 100, 0, 1000, 20, 8000, 20000, 0, 0, 11, 58153, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Crusader - Friendly At 1000 Health - Cast \'Unholy Light\''); From 0ecbe4a704c9cc4cb98ce18fdbcd1ba94ceca438 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 00:23:28 +0000 Subject: [PATCH 52/92] chore(DB): import pending files Referenced commit(s): b6b6b55ed314a70d57992e525bbae87edba97ac6 --- .../rev_1777394998.sql => db_world/2026_04_29_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777394998.sql => db_world/2026_04_29_01.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1777394998.sql b/data/sql/updates/db_world/2026_04_29_01.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1777394998.sql rename to data/sql/updates/db_world/2026_04_29_01.sql index 531f9fa69..46a6490ba 100644 --- a/data/sql/updates/pending_db_world/rev_1777394998.sql +++ b/data/sql/updates/db_world/2026_04_29_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_29_00 -> 2026_04_29_01 -- Add CastFlags 32 and update comments. UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 30202; From e32249f926996b1ec8632048511ad8352532987e Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 28 Apr 2026 21:31:00 -0300 Subject: [PATCH 53/92] fix(Scripts/Spells): Shadow Vault Decree engages Thane Ufrang (#25645) Co-authored-by: Claude Opus 4.7 --- .../rev_1777417304651314500.sql | 20 +++++++++++++++++++ src/server/scripts/Spells/spell_quest.cpp | 17 +++++----------- 2 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1777417304651314500.sql diff --git a/data/sql/updates/pending_db_world/rev_1777417304651314500.sql b/data/sql/updates/pending_db_world/rev_1777417304651314500.sql new file mode 100644 index 000000000..bc92a7d40 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777417304651314500.sql @@ -0,0 +1,20 @@ +-- +-- Quest 12943 "Shadow Vault Decree" - Thane Ufrang the Mighty (29919) +-- +-- The CheckCast "Thane is nearby" check moves out of spell_q12943_shadow_vault_decree +-- and into a CONDITION_NEAR_CREATURE on spell 31696. The script now also clears +-- Thane's idle unit flags via ReplaceAllUnitFlags before calling AttackStart, so +-- combat actually engages instead of the player getting stuck unable to attack. +-- +-- The old SAI "Remove Unit Flags" action (id 6) chained off the 12s "Say Line 4" +-- timer is removed - the C++ handles flag removal at cast time. +-- + +-- Cast-time precondition: Thane (alive) must be within 30y of the caster. +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 17 AND `SourceEntry` = 31696; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(17, 0, 31696, 0, 0, 29, 0, 29919, 30, 0, 0, 12, 0, '', 'Spell Shadow Vault Decree requires nearby Thane Ufrang the Mighty'); + +-- Drop the now-redundant linked Remove-Unit-Flags action and its parent's link to it. +UPDATE `smart_scripts` SET `link` = 0 WHERE `entryorguid` = 29919 AND `source_type` = 0 AND `id` = 5; +DELETE FROM `smart_scripts` WHERE `entryorguid` = 29919 AND `source_type` = 0 AND `id` = 6; diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index bbf9bb52e..65596dd4a 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -381,27 +381,20 @@ class spell_q12943_shadow_vault_decree : public SpellScript { PrepareSpellScript(spell_q12943_shadow_vault_decree); - SpellCastResult CheckRequirement() - { - // if thane is present and not in combat - allow cast - Unit* caster = GetCaster(); - if (Creature* thane = caster->FindNearestCreature(NPC_THANE_UFRANG, 30.0f)) - if (!thane->IsInCombat()) - return SPELL_CAST_OK; - - return SPELL_FAILED_CASTER_AURASTATE; - } - void HandleScriptEffect(SpellEffIndex /*effIndex*/) { Unit* caster = GetCaster(); if (Creature* thane = caster->FindNearestCreature(NPC_THANE_UFRANG, 30.0f)) + { + if (thane->IsInCombat()) + return; + thane->ReplaceAllUnitFlags(UNIT_FLAG_NONE); thane->AI()->AttackStart(caster); + } } void Register() override { - OnCheckCast += SpellCheckCastFn(spell_q12943_shadow_vault_decree::CheckRequirement); OnEffectHitTarget += SpellEffectFn(spell_q12943_shadow_vault_decree::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); } }; From 797628e7a56bb075fea1dfef09962f0c034eca9e Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 28 Apr 2026 21:31:13 -0300 Subject: [PATCH 54/92] =?UTF-8?q?fix(DB/Conditions):=20Fix=20Eye=20of=20Do?= =?UTF-8?q?minion=20gossip=20not=20being=20offered=20whil=E2=80=A6=20(#256?= =?UTF-8?q?46)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../updates/pending_db_world/rev_1777420384420184300.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777420384420184300.sql diff --git a/data/sql/updates/pending_db_world/rev_1777420384420184300.sql b/data/sql/updates/pending_db_world/rev_1777420384420184300.sql new file mode 100644 index 000000000..37ff86791 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777420384420184300.sql @@ -0,0 +1,7 @@ +-- Allow Eye of Dominion (Rise of Suffering, GO 193424 / gossip menu 10111) to +-- show its "Seize control of a Lithe Stalker" gossip option for quest 13160 +-- (Stunning View). Without this row players who have only Stunning View active +-- see no gossip option and cannot progress the quest. +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 15) AND (`SourceGroup` = 10111) AND (`SourceEntry` = 0) AND (`SourceId` = 0) AND (`ElseGroup` = 3) AND (`ConditionTypeOrReference` = 9) AND (`ConditionValue1` = 13160); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(15, 10111, 0, 0, 3, 9, 0, 13160, 0, 0, 0, 0, 0, '', 'Show Gossip option only if player has quest Stunning View active'); From 7fac1fca9aee484925b8cccd04024a15a94a8ab7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 00:32:06 +0000 Subject: [PATCH 55/92] chore(DB): import pending files Referenced commit(s): e32249f926996b1ec8632048511ad8352532987e --- .../rev_1777417304651314500.sql => db_world/2026_04_29_02.sql} | 1 + .../rev_1777420384420184300.sql => db_world/2026_04_29_03.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/rev_1777417304651314500.sql => db_world/2026_04_29_02.sql} (97%) rename data/sql/updates/{pending_db_world/rev_1777420384420184300.sql => db_world/2026_04_29_03.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1777417304651314500.sql b/data/sql/updates/db_world/2026_04_29_02.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1777417304651314500.sql rename to data/sql/updates/db_world/2026_04_29_02.sql index bc92a7d40..5a503eb6a 100644 --- a/data/sql/updates/pending_db_world/rev_1777417304651314500.sql +++ b/data/sql/updates/db_world/2026_04_29_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_29_01 -> 2026_04_29_02 -- -- Quest 12943 "Shadow Vault Decree" - Thane Ufrang the Mighty (29919) -- diff --git a/data/sql/updates/pending_db_world/rev_1777420384420184300.sql b/data/sql/updates/db_world/2026_04_29_03.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1777420384420184300.sql rename to data/sql/updates/db_world/2026_04_29_03.sql index 37ff86791..b8e5263fa 100644 --- a/data/sql/updates/pending_db_world/rev_1777420384420184300.sql +++ b/data/sql/updates/db_world/2026_04_29_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_29_02 -> 2026_04_29_03 -- Allow Eye of Dominion (Rise of Suffering, GO 193424 / gossip menu 10111) to -- show its "Seize control of a Lithe Stalker" gossip option for quest 13160 -- (Stunning View). Without this row players who have only Stunning View active From e043f0356db78cbce661d6a89b8a63c57a73aae6 Mon Sep 17 00:00:00 2001 From: sogladev Date: Wed, 29 Apr 2026 02:36:34 +0200 Subject: [PATCH 56/92] fix(DB/Loot): Sartharion 25M always drops 2 T7.5 Tokens (#25578) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../pending_db_world/rev_1777072262285005362.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777072262285005362.sql diff --git a/data/sql/updates/pending_db_world/rev_1777072262285005362.sql b/data/sql/updates/pending_db_world/rev_1777072262285005362.sql new file mode 100644 index 000000000..d1f531f1f --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777072262285005362.sql @@ -0,0 +1,12 @@ +-- +-- Sartharion 25M drops 2x T7.5 Gloves +SET @REF := 34377; +DELETE FROM `creature_loot_template` WHERE (`Entry` = 31311) AND (`Item` IN (5, 40628, 40629, 40630)); +INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(31311, 5, -@REF, 100, 0, 1, 0, 2, 2, 'Sartharion (1) - (ReferenceTable)'); + +DELETE FROM `reference_loot_template` WHERE (`Entry` = @REF) AND (`Item` IN (40752, 40628, 40629, 40630)); +INSERT INTO `reference_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES +(@REF, 40628, 0, 0, 0, 1, 1, 1, 1, 'T7.5 Gloves Token'), +(@REF, 40629, 0, 0, 0, 1, 1, 1, 1, 'T7.5 Gloves Token'), +(@REF, 40630, 0, 0, 0, 1, 1, 1, 1, 'T7.5 Gloves Token'); From 5beb53e7b56028f8c2dd0f415e644098592128fe Mon Sep 17 00:00:00 2001 From: sogladev Date: Wed, 29 Apr 2026 02:37:23 +0200 Subject: [PATCH 57/92] fix(DB/Creature): Templates for Green and Gold arena npcs in Dalaran (#25467) --- .../updates/pending_db_world/rev_1776197344536353914.sql | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1776197344536353914.sql diff --git a/data/sql/updates/pending_db_world/rev_1776197344536353914.sql b/data/sql/updates/pending_db_world/rev_1776197344536353914.sql new file mode 100644 index 000000000..a991dfbb8 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1776197344536353914.sql @@ -0,0 +1,6 @@ +-- +-- Unit flags observed: 8, 2304 (256 | 2048) +-- +256 IMMUNE_TO_PC - disables combat/assistance with PlayerCharacters (PC) +-- +2048 PET_IN_COMBAT +UPDATE `creature_template` SET `faction` = 2101, `unit_flags` = 256, `unit_flags2` = 2048 WHERE (`entry` IN (32343, 32342, 32324, 32321)); +UPDATE `creature_template` SET `faction` = 1770, `unit_flags` = 256, `unit_flags2` = 2048 WHERE (`entry` IN (32322, 32341, 32340, 32325)); From ace204810f7ff5d5ae6bc418beb03590da993030 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 00:37:40 +0000 Subject: [PATCH 58/92] chore(DB): import pending files Referenced commit(s): e043f0356db78cbce661d6a89b8a63c57a73aae6 --- .../rev_1776197344536353914.sql => db_world/2026_04_29_04.sql} | 1 + .../rev_1777072262285005362.sql => db_world/2026_04_29_05.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/rev_1776197344536353914.sql => db_world/2026_04_29_04.sql} (90%) rename data/sql/updates/{pending_db_world/rev_1777072262285005362.sql => db_world/2026_04_29_05.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1776197344536353914.sql b/data/sql/updates/db_world/2026_04_29_04.sql similarity index 90% rename from data/sql/updates/pending_db_world/rev_1776197344536353914.sql rename to data/sql/updates/db_world/2026_04_29_04.sql index a991dfbb8..751083abb 100644 --- a/data/sql/updates/pending_db_world/rev_1776197344536353914.sql +++ b/data/sql/updates/db_world/2026_04_29_04.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_29_03 -> 2026_04_29_04 -- -- Unit flags observed: 8, 2304 (256 | 2048) -- +256 IMMUNE_TO_PC - disables combat/assistance with PlayerCharacters (PC) diff --git a/data/sql/updates/pending_db_world/rev_1777072262285005362.sql b/data/sql/updates/db_world/2026_04_29_05.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1777072262285005362.sql rename to data/sql/updates/db_world/2026_04_29_05.sql index d1f531f1f..12008d9c0 100644 --- a/data/sql/updates/pending_db_world/rev_1777072262285005362.sql +++ b/data/sql/updates/db_world/2026_04_29_05.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_29_04 -> 2026_04_29_05 -- -- Sartharion 25M drops 2x T7.5 Gloves SET @REF := 34377; From 0bb5bb3010c429e6b6666efa3c0730f71f95a193 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Wed, 29 Apr 2026 17:18:53 +0200 Subject: [PATCH 59/92] fix(DB/ModelInfo): Remove wrong Other Gender from a couple of DisplayIDs. (#25650) --- data/sql/updates/pending_db_world/rev_1777471055.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777471055.sql diff --git a/data/sql/updates/pending_db_world/rev_1777471055.sql b/data/sql/updates/pending_db_world/rev_1777471055.sql new file mode 100644 index 000000000..9c71b9f2e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777471055.sql @@ -0,0 +1,3 @@ + +-- Remove DisplayID_Other_Gender. +UPDATE `creature_model_info` SET `DisplayID_Other_Gender` = 0 WHERE (`DisplayID` IN (12829, 12830)); From 5be8a8a8acb1f5b7523be6e173e91c8c80e9e3bf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 15:21:59 +0000 Subject: [PATCH 60/92] chore(DB): import pending files Referenced commit(s): 0bb5bb3010c429e6b6666efa3c0730f71f95a193 --- .../rev_1777471055.sql => db_world/2026_04_29_06.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777471055.sql => db_world/2026_04_29_06.sql} (75%) diff --git a/data/sql/updates/pending_db_world/rev_1777471055.sql b/data/sql/updates/db_world/2026_04_29_06.sql similarity index 75% rename from data/sql/updates/pending_db_world/rev_1777471055.sql rename to data/sql/updates/db_world/2026_04_29_06.sql index 9c71b9f2e..45acd6935 100644 --- a/data/sql/updates/pending_db_world/rev_1777471055.sql +++ b/data/sql/updates/db_world/2026_04_29_06.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_29_05 -> 2026_04_29_06 -- Remove DisplayID_Other_Gender. UPDATE `creature_model_info` SET `DisplayID_Other_Gender` = 0 WHERE (`DisplayID` IN (12829, 12830)); From 6eafa116cd93a83ce3dc730cf9d40b3204e538f6 Mon Sep 17 00:00:00 2001 From: sogladev Date: Thu, 30 Apr 2026 00:06:22 +0200 Subject: [PATCH 61/92] fix(Scripts/SmartAI): Warlock Doomguard quest Suppression (#25655) --- .../rev_1777486974840031498.sql | 24 +++++++++++++++++++ .../game/AI/SmartScripts/SmartScript.cpp | 4 ++-- 2 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1777486974840031498.sql diff --git a/data/sql/updates/pending_db_world/rev_1777486974840031498.sql b/data/sql/updates/pending_db_world/rev_1777486974840031498.sql new file mode 100644 index 000000000..ad84b33f5 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777486974840031498.sql @@ -0,0 +1,24 @@ +-- +-- workaround to apply gobject scaling of 23020 Crystal Imprisonment (SPELL_AURA_MOD_SCALE), value = -90 +UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 179644; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 179644) AND (`source_type` = 1) 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 +(179644, 1, 0, 0, 63, 0, 100, 0, 0, 0, 0, 0, 0, 0, 227, 10, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Imprisoned Doomguard - On Just Created - Set Scale to 10%'); + +DELETE FROM `creature_text` WHERE (`CreatureID` = 14452); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(14452, 0, 0, 'A sacrifice will be due if I am ever called upon, mortal...', 12, 8, 100, 0, 0, 0, 9590, 0, ''); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `id` IN (4, 5) AND `entryorguid` = 12396); +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 +(12396, 0, 4, 0, 8, 0, 100, 1, 23019, 0, 0, 0, 0, 0, 80, 1239600, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Doomguard Commander - On Spellhit \'Crystal Prison Dummy DND\' - Run Script (No Repeat)'); + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 1239600) AND (`source_type` = 9); +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 +(1239600, 9, 0, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 36, 14452, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Doomguard Commander - Actionlist - Update Template To \'Enslaved Doomguard Commander\''), +(1239600, 9, 1, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 23020, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Doomguard Commander - Actionlist - Cast \'Crystal Imprisonment\''), +(1239600, 9, 2, 0, 0, 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, 'Doomguard Commander - Actionlist - Set Flags Immune To Players & Immune To NPC\'s'), +(1239600, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Doomguard Commander - Actionlist - Stop Combat'), +(1239600, 9, 4, 0, 0, 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, 'Doomguard Commander - Actionlist - Say Line 0'), +(1239600, 9, 5, 0, 0, 0, 100, 0, 5000, 5000, 0, 0, 0, 0, 11, 23022, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Doomguard Commander - Actionlist - Cast \'Serverside - Crystal Prison Conjure DND\''), +(1239600, 9, 6, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Doomguard Commander - Actionlist - Despawn Instant'); diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index fcd03bd24..81bfa0c04 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -3082,9 +3082,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u for (WorldObject* target : targets) { if (IsUnit(target)) - { target->ToUnit()->SetObjectScale(scale); - } + else if (IsGameObject(target)) + target->ToGameObject()->SetObjectScale(scale); } break; } From b87da279876a3da0149237f03350bf2d9240f345 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Apr 2026 22:07:33 +0000 Subject: [PATCH 62/92] chore(DB): import pending files Referenced commit(s): 6eafa116cd93a83ce3dc730cf9d40b3204e538f6 --- .../rev_1777486974840031498.sql => db_world/2026_04_29_07.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777486974840031498.sql => db_world/2026_04_29_07.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1777486974840031498.sql b/data/sql/updates/db_world/2026_04_29_07.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1777486974840031498.sql rename to data/sql/updates/db_world/2026_04_29_07.sql index ad84b33f5..d699d74d9 100644 --- a/data/sql/updates/pending_db_world/rev_1777486974840031498.sql +++ b/data/sql/updates/db_world/2026_04_29_07.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_29_06 -> 2026_04_29_07 -- -- workaround to apply gobject scaling of 23020 Crystal Imprisonment (SPELL_AURA_MOD_SCALE), value = -90 UPDATE `gameobject_template` SET `AIName` = 'SmartGameObjectAI' WHERE `entry` = 179644; From ce26dabafd3e83860050e9b19e9a2d536687acf9 Mon Sep 17 00:00:00 2001 From: sogladev Date: Thu, 30 Apr 2026 02:48:24 +0200 Subject: [PATCH 63/92] fix(Scripts/Northrend): KTZ Void Blast can now fail Immortal criteria (#25652) --- .../rev_1777468016237312188.sql | 4 ++++ .../Northrend/Naxxramas/boss_kelthuzad.cpp | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777468016237312188.sql diff --git a/data/sql/updates/pending_db_world/rev_1777468016237312188.sql b/data/sql/updates/pending_db_world/rev_1777468016237312188.sql new file mode 100644 index 000000000..5ec5fee84 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777468016237312188.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id` = 27812 AND `ScriptName` = 'spell_kelthuzad_void_blast'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(27812, 'spell_kelthuzad_void_blast'); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index abb53e6e2..7f28e2301 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -680,10 +680,34 @@ class spell_kelthuzad_detonate_mana_aura : public AuraScript } }; +class spell_kelthuzad_void_blast : public SpellScript +{ + PrepareSpellScript(spell_kelthuzad_void_blast); + + void HandleAfterHit() + { + Player* player = GetHitPlayer(); + if (!player) + return; + + if (player->IsAlive()) + return; + + if (InstanceScript* instance = player->GetInstanceScript()) + instance->StorePersistentData(PERSISTENT_DATA_IMMORTAL_FAIL, 1); + } + + void Register() override + { + AfterHit += SpellHitFn(spell_kelthuzad_void_blast::HandleAfterHit); + } +}; + void AddSC_boss_kelthuzad() { new boss_kelthuzad(); new boss_kelthuzad_minion(); RegisterSpellScript(spell_kelthuzad_frost_blast); RegisterSpellScript(spell_kelthuzad_detonate_mana_aura); + RegisterSpellScript(spell_kelthuzad_void_blast); } From 429c621fb703c25eb2d0b92ff08e2b3efc877c5c Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 29 Apr 2026 21:48:48 -0300 Subject: [PATCH 64/92] fix(DB/SAI): BRD Hurley Blackbreath group stuck immune to PCs (#25659) --- .../rev_1777506833363521200.sql | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777506833363521200.sql diff --git a/data/sql/updates/pending_db_world/rev_1777506833363521200.sql b/data/sql/updates/pending_db_world/rev_1777506833363521200.sql new file mode 100644 index 000000000..10aa3672e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777506833363521200.sql @@ -0,0 +1,38 @@ +-- +-- BRD: Hurley Blackbreath (9537) and Blackbreath Cronies (per-spawn +-- overrides -71997/-71998/-71999) start with UNIT_FLAG_UNK_6 | +-- UNIT_FLAG_IMMUNE_TO_PC (320) and only clear it via "OnReachedWP 3". +-- The encounter propagates aggro: any mob entering combat fires +-- OnAggro -> SetData 1=2 on every member, which triggers OnDataSet 1=2 +-- -> Attack on the rest. If a peer is still walking its WP path when +-- this happens, its movement is interrupted, WP 3 is never reached, +-- and the immunity flag is never cleared -> the mob stays in combat +-- but cannot be damaged or targeted. +-- +-- Failsafe: any combat-entry path also clears flag 320 on the unit. +-- Two new rows per entity: one on OnAggro, one on OnDataSet 1=2. +-- + +-- Hurley Blackbreath +DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` = 9537) AND (`id` IN (19, 20)); +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 +(9537, 0, 19, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 19, 320, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Hurley Blackbreath - On Aggro - Remove Unit Flags Immune To PC (failsafe if pulled before reaching WP 3)'), +(9537, 0, 20, 0, 38, 0, 100, 0, 1, 2, 0, 0, 0, 0, 19, 320, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Hurley Blackbreath - On Data Set 1=2 - Remove Unit Flags Immune To PC (failsafe if pulled before reaching WP 3)'); + +-- Blackbreath Crony spawn 71997 +DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` = -71997) AND (`id` IN (14, 15)); +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 +(-71997, 0, 14, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 19, 320, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - On Aggro - Remove Unit Flags Immune To PC (failsafe)'), +(-71997, 0, 15, 0, 38, 0, 100, 0, 1, 2, 0, 0, 0, 0, 19, 320, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - On Data Set 1=2 - Remove Unit Flags Immune To PC (failsafe)'); + +-- Blackbreath Crony spawn 71998 +DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` = -71998) AND (`id` IN (14, 15)); +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 +(-71998, 0, 14, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 19, 320, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - On Aggro - Remove Unit Flags Immune To PC (failsafe)'), +(-71998, 0, 15, 0, 38, 0, 100, 0, 1, 2, 0, 0, 0, 0, 19, 320, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - On Data Set 1=2 - Remove Unit Flags Immune To PC (failsafe)'); + +-- Blackbreath Crony spawn 71999 +DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` = -71999) AND (`id` IN (14, 15)); +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 +(-71999, 0, 14, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 19, 320, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - On Aggro - Remove Unit Flags Immune To PC (failsafe)'), +(-71999, 0, 15, 0, 38, 0, 100, 0, 1, 2, 0, 0, 0, 0, 19, 320, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Blackbreath Crony - On Data Set 1=2 - Remove Unit Flags Immune To PC (failsafe)'); From 510ecb4324d76341a45c1c35bf013752fecb5548 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 30 Apr 2026 00:49:35 +0000 Subject: [PATCH 65/92] chore(DB): import pending files Referenced commit(s): ce26dabafd3e83860050e9b19e9a2d536687acf9 --- .../rev_1777468016237312188.sql => db_world/2026_04_30_00.sql} | 1 + .../rev_1777506833363521200.sql => db_world/2026_04_30_01.sql} | 1 + 2 files changed, 2 insertions(+) rename data/sql/updates/{pending_db_world/rev_1777468016237312188.sql => db_world/2026_04_30_00.sql} (83%) rename data/sql/updates/{pending_db_world/rev_1777506833363521200.sql => db_world/2026_04_30_01.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1777468016237312188.sql b/data/sql/updates/db_world/2026_04_30_00.sql similarity index 83% rename from data/sql/updates/pending_db_world/rev_1777468016237312188.sql rename to data/sql/updates/db_world/2026_04_30_00.sql index 5ec5fee84..0ece16300 100644 --- a/data/sql/updates/pending_db_world/rev_1777468016237312188.sql +++ b/data/sql/updates/db_world/2026_04_30_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_29_07 -> 2026_04_30_00 -- DELETE FROM `spell_script_names` WHERE `spell_id` = 27812 AND `ScriptName` = 'spell_kelthuzad_void_blast'; INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES diff --git a/data/sql/updates/pending_db_world/rev_1777506833363521200.sql b/data/sql/updates/db_world/2026_04_30_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1777506833363521200.sql rename to data/sql/updates/db_world/2026_04_30_01.sql index 10aa3672e..8f4b0602e 100644 --- a/data/sql/updates/pending_db_world/rev_1777506833363521200.sql +++ b/data/sql/updates/db_world/2026_04_30_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_30_00 -> 2026_04_30_01 -- -- BRD: Hurley Blackbreath (9537) and Blackbreath Cronies (per-spawn -- overrides -71997/-71998/-71999) start with UNIT_FLAG_UNK_6 | From e1c35f72fa1511f19c55db1f659dd6e9144badba Mon Sep 17 00:00:00 2001 From: EricksOliveira Date: Thu, 30 Apr 2026 00:54:05 +0000 Subject: [PATCH 66/92] fix(DB/Gameobject): Fixed Lever Spawn Position (#25636) Co-authored-by: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> --- .../rev_1777384200135224600.sql | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777384200135224600.sql diff --git a/data/sql/updates/pending_db_world/rev_1777384200135224600.sql b/data/sql/updates/pending_db_world/rev_1777384200135224600.sql new file mode 100644 index 000000000..966351e9f --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777384200135224600.sql @@ -0,0 +1,18 @@ +-- +DELETE FROM `gameobject` WHERE (`id` = 18901) AND (`guid` IN (34006)); +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 +(34006, 18901, 33, 0, 0, 1, 1, -249.22014, 2123.1018, 82.80518, 6.0923543, -0.1318922, -0.6946964, -0.1318922, 0.69469833, 0, 100, 1, '', 43400, NULL); + +DELETE FROM `gameobject` WHERE (`id` = 18900) AND (`guid` IN (32442)); +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 +(32442, 18900, 33, 0, 0, 1, 1, -252.69586, 2114.2246, 82.80518, 6.0923543, -0.1318922, -0.6946964, -0.1318922, 0.69469833, 0, 100, 1, '', 43400, NULL); + +DELETE FROM `gameobject` WHERE (`id` = 101811) AND (`guid` IN (32444)); +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 +(32444, 101811, 33, 0, 0, 1, 1, -245.59846, 2132.319, 82.80518, 6.0923543, -0.1318922, -0.6946964, -0.1318922, 0.69469833, 0, 100, 1, '', 43400, NULL); + +DELETE FROM `gameobject_addon` WHERE `guid` IN (32442, 34006, 32444); +INSERT INTO `gameobject_addon` (`guid`, `parent_rotation0`, `parent_rotation1`, `parent_rotation2`, `parent_rotation3`, `invisibilityType`, `invisibilityValue`) VALUES +(32442, 0, 0, -0.82658964, 0.56280506, 0, 0), +(34006, 0, 0, -0.82658964, 0.56280506, 0, 0), +(32444, 0, 0, -0.82658964, 0.56280506, 0, 0); From 6956fd2db8702a00772a89f3522de47e822bc3c7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 30 Apr 2026 00:55:12 +0000 Subject: [PATCH 67/92] chore(DB): import pending files Referenced commit(s): e1c35f72fa1511f19c55db1f659dd6e9144badba --- .../rev_1777384200135224600.sql => db_world/2026_04_30_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777384200135224600.sql => db_world/2026_04_30_02.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1777384200135224600.sql b/data/sql/updates/db_world/2026_04_30_02.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1777384200135224600.sql rename to data/sql/updates/db_world/2026_04_30_02.sql index 966351e9f..fc375ee73 100644 --- a/data/sql/updates/pending_db_world/rev_1777384200135224600.sql +++ b/data/sql/updates/db_world/2026_04_30_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_30_01 -> 2026_04_30_02 -- DELETE FROM `gameobject` WHERE (`id` = 18901) AND (`guid` IN (34006)); 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 From 0b32c50fb2b132a5e8248b1127ad746d1764def7 Mon Sep 17 00:00:00 2001 From: kb31 Date: Thu, 30 Apr 2026 13:16:53 +0100 Subject: [PATCH 68/92] fix(Core/Spells): Don't remove shield-dependant auras in Dismantle (#25658) --- src/server/game/Entities/Player/Player.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 4b5804c29..0739c1d3b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -12551,8 +12551,7 @@ bool Player::HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item cons return true; // Keep active non-passive auras (e.g. Shield Wall) when disarmed - Item* offhand = GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!spellInfo->IsPassive() && offhand && offhand != ignoreItem) + if (!spellInfo->IsPassive() && HasAuraType(SPELL_AURA_MOD_DISARM_OFFHAND) && GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND)) { for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) if (spellInfo->Effects[i].IsAura()) From b1f3eb00734382049818417c962c84b99c04447e Mon Sep 17 00:00:00 2001 From: Steffen Schmidt Date: Thu, 30 Apr 2026 22:02:09 +0200 Subject: [PATCH 69/92] fix(DB/Creature): Fix Reanimated Exarch unit_flags after spawn (#25548) Co-authored-by: Steffen Schmidt --- .../pending_db_world/npc_22452_quest_10915.sql | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 data/sql/updates/pending_db_world/npc_22452_quest_10915.sql diff --git a/data/sql/updates/pending_db_world/npc_22452_quest_10915.sql b/data/sql/updates/pending_db_world/npc_22452_quest_10915.sql new file mode 100644 index 000000000..ea52fd4ff --- /dev/null +++ b/data/sql/updates/pending_db_world/npc_22452_quest_10915.sql @@ -0,0 +1,13 @@ + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2245200); +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 +(2245200, 9, 0, 0, 0, 0, 100, 0, 500, 500, 0, 0, 0, 0, 1, 0, 3500, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Exarch - Actionlist - Say Line 0'), +(2245200, 9, 1, 0, 0, 0, 100, 0, 4000, 4000, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 21, 20, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Exarch - Actionlist - Set Orientation Closest Player'), +(2245200, 9, 2, 0, 0, 0, 100, 0, 1000, 1000, 0, 0, 0, 0, 19, 768, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Exarch - Actionlist - Remove Flags Immune To Players & Immune To NPC\'s'), +(2245200, 9, 3, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 21, 20, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Exarch - Actionlist - Start Attacking'); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 22452; +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 22452); +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 +(22452, 0, 0, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 2245200, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Exarch - On Just Summoned - Run Script'), +(22452, 0, 1, 0, 0, 0, 100, 1, 2000, 4000, 0, 0, 0, 0, 11, 8258, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Reanimated Exarch - In Combat - Cast \'Devotion Aura\' (No Repeat)'); From ab9d0a074d6020e192a091790bee71a3c9048669 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 30 Apr 2026 20:03:21 +0000 Subject: [PATCH 70/92] chore(DB): import pending files Referenced commit(s): b1f3eb00734382049818417c962c84b99c04447e --- .../npc_22452_quest_10915.sql => db_world/2026_04_30_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/npc_22452_quest_10915.sql => db_world/2026_04_30_03.sql} (98%) diff --git a/data/sql/updates/pending_db_world/npc_22452_quest_10915.sql b/data/sql/updates/db_world/2026_04_30_03.sql similarity index 98% rename from data/sql/updates/pending_db_world/npc_22452_quest_10915.sql rename to data/sql/updates/db_world/2026_04_30_03.sql index ea52fd4ff..aebce7674 100644 --- a/data/sql/updates/pending_db_world/npc_22452_quest_10915.sql +++ b/data/sql/updates/db_world/2026_04_30_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_30_02 -> 2026_04_30_03 DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2245200); 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 a783e6c436f188de8782d5d1ea37d6b5341c2a3f Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 1 May 2026 12:58:10 +0200 Subject: [PATCH 71/92] fix(Scripts/ObsidianSanctum): fix race where Drake does not land (#25674) --- .../ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp index 9ed4f962d..fb1670616 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/ObsidianSanctum/boss_sartharion.cpp @@ -924,6 +924,7 @@ struct boss_sartharion_dragonAI : public BossAI // Dragon speaks and starts flying to landing position Talk(SAY_DRAKE_RESPOND); me->GetMotionMaster()->Clear(); + extraEvents.CancelEvent(EVENT_DRAGON_PATROL_WAYPOINT); me->SetDisableGravity(true); me->SetHover(true); me->SetAnimTier(AnimTier::Fly); From 779331fc4b1d35577c34923c90459764f5834c04 Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Fri, 1 May 2026 07:19:30 -0500 Subject: [PATCH 72/92] feat(Core/RBAC): Role Based Access Controls (#24641) Co-authored-by: blinkysc Co-authored-by: Spp Co-authored-by: Spp Co-authored-by: Claude Opus 4.6 (1M context) Co-authored-by: Andrew <47818697+Nyeriah@users.noreply.github.com> --- .../rev_1770173499134350791.sql | 1006 ++++++++ .../rev_1770173911371830386.sql | 20 + .../rev_1777553106653644140.sql | 12 + .../Database/Implementation/LoginDatabase.cpp | 8 + .../Database/Implementation/LoginDatabase.h | 8 + src/server/game/Accounts/AccountMgr.cpp | 748 +++--- src/server/game/Accounts/AccountMgr.h | 78 +- src/server/game/Accounts/RBAC.cpp | 283 +++ src/server/game/Accounts/RBAC.h | 895 +++++++ .../game/Achievements/AchievementMgr.cpp | 9 +- .../game/Battlegrounds/Battleground.cpp | 3 +- src/server/game/Chat/Channels/Channel.cpp | 61 +- src/server/game/Chat/Chat.cpp | 16 +- src/server/game/Chat/Chat.h | 1 + .../game/Chat/ChatCommands/ChatCommand.cpp | 5 + src/server/game/DungeonFinding/LFGMgr.cpp | 17 +- src/server/game/Entities/Player/Player.cpp | 35 +- src/server/game/Entities/Player/Player.h | 3 +- .../game/Entities/Player/PlayerMisc.cpp | 3 +- .../game/Entities/Player/PlayerStorage.cpp | 7 +- src/server/game/Entities/Player/SocialMgr.cpp | 25 +- src/server/game/Groups/Group.cpp | 2 +- src/server/game/Guilds/Guild.cpp | 20 + .../game/Handlers/AuctionHouseHandler.cpp | 29 + .../game/Handlers/BattleGroundHandler.cpp | 4 +- src/server/game/Handlers/CharacterHandler.cpp | 50 +- src/server/game/Handlers/ChatHandler.cpp | 16 +- src/server/game/Handlers/MailHandler.cpp | 38 +- src/server/game/Handlers/MiscHandler.cpp | 30 +- src/server/game/Handlers/Socialhandler.cpp | 5 +- src/server/game/Handlers/TradeHandler.cpp | 34 +- src/server/game/Instances/InstanceScript.cpp | 6 + src/server/game/Instances/InstanceScript.h | 2 + src/server/game/Miscellaneous/Language.h | 5 +- src/server/game/Server/WorldSession.cpp | 57 +- src/server/game/Server/WorldSession.h | 11 + src/server/game/Server/WorldSessionMgr.cpp | 5 +- src/server/game/Server/WorldSocket.cpp | 3 +- src/server/game/Spells/SpellEffects.cpp | 26 + src/server/game/World/IWorld.h | 1 + src/server/game/World/World.cpp | 13 + src/server/game/World/World.h | 1 + src/server/scripts/Commands/cs_account.cpp | 45 +- .../scripts/Commands/cs_achievement.cpp | 5 +- src/server/scripts/Commands/cs_arena.cpp | 23 +- src/server/scripts/Commands/cs_bag.cpp | 3 +- src/server/scripts/Commands/cs_ban.cpp | 29 +- src/server/scripts/Commands/cs_bf.cpp | 15 +- src/server/scripts/Commands/cs_cache.cpp | 7 +- src/server/scripts/Commands/cs_cast.cpp | 13 +- src/server/scripts/Commands/cs_character.cpp | 40 +- src/server/scripts/Commands/cs_cheat.cpp | 18 +- src/server/scripts/Commands/cs_debug.cpp | 107 +- src/server/scripts/Commands/cs_deserter.cpp | 13 +- src/server/scripts/Commands/cs_disable.cpp | 26 +- src/server/scripts/Commands/cs_event.cpp | 9 +- src/server/scripts/Commands/cs_gear.cpp | 6 +- src/server/scripts/Commands/cs_gm.cpp | 17 +- src/server/scripts/Commands/cs_go.cpp | 27 +- src/server/scripts/Commands/cs_gobject.cpp | 31 +- src/server/scripts/Commands/cs_group.cpp | 13 +- src/server/scripts/Commands/cs_guild.cpp | 15 +- src/server/scripts/Commands/cs_honor.cpp | 8 +- src/server/scripts/Commands/cs_instance.cpp | 13 +- src/server/scripts/Commands/cs_inventory.cpp | 4 +- src/server/scripts/Commands/cs_item.cpp | 9 +- src/server/scripts/Commands/cs_learn.cpp | 25 +- src/server/scripts/Commands/cs_lfg.cpp | 13 +- src/server/scripts/Commands/cs_list.cpp | 17 +- src/server/scripts/Commands/cs_lookup.cpp | 39 +- src/server/scripts/Commands/cs_message.cpp | 15 +- src/server/scripts/Commands/cs_misc.cpp | 115 +- src/server/scripts/Commands/cs_mmaps.cpp | 11 +- src/server/scripts/Commands/cs_modify.cpp | 57 +- src/server/scripts/Commands/cs_npc.cpp | 69 +- src/server/scripts/Commands/cs_pet.cpp | 7 +- src/server/scripts/Commands/cs_player.cpp | 5 +- .../scripts/Commands/cs_player_settings.cpp | 3 +- src/server/scripts/Commands/cs_pooltools.cpp | 13 +- src/server/scripts/Commands/cs_quest.cpp | 11 +- src/server/scripts/Commands/cs_rbac.cpp | 289 +++ src/server/scripts/Commands/cs_reload.cpp | 236 +- src/server/scripts/Commands/cs_reset.cpp | 33 +- .../scripts/Commands/cs_script_loader.cpp | 2 + src/server/scripts/Commands/cs_send.cpp | 9 +- src/server/scripts/Commands/cs_server.cpp | 33 +- src/server/scripts/Commands/cs_spectator.cpp | 13 +- src/server/scripts/Commands/cs_tele.cpp | 17 +- src/server/scripts/Commands/cs_ticket.cpp | 39 +- src/server/scripts/Commands/cs_titles.cpp | 9 +- src/server/scripts/Commands/cs_worldstate.cpp | 21 +- src/server/scripts/Commands/cs_wp.cpp | 15 +- .../BlackwingLair/instance_blackwing_lair.cpp | 33 +- .../instance_icecrown_citadel.cpp | 4 +- src/test/mocks/WorldMock.h | 1 + src/test/server/game/Accounts/RBACTest.cpp | 2059 +++++++++++++++++ .../game/Commands/GmVisibleCommandTest.cpp | 55 +- 97 files changed, 6276 insertions(+), 1089 deletions(-) create mode 100644 data/sql/updates/pending_db_auth/rev_1770173499134350791.sql create mode 100644 data/sql/updates/pending_db_world/rev_1770173911371830386.sql create mode 100644 data/sql/updates/pending_db_world/rev_1777553106653644140.sql create mode 100644 src/server/game/Accounts/RBAC.cpp create mode 100644 src/server/game/Accounts/RBAC.h create mode 100644 src/server/scripts/Commands/cs_rbac.cpp create mode 100644 src/test/server/game/Accounts/RBACTest.cpp diff --git a/data/sql/updates/pending_db_auth/rev_1770173499134350791.sql b/data/sql/updates/pending_db_auth/rev_1770173499134350791.sql new file mode 100644 index 000000000..1f86ccdd8 --- /dev/null +++ b/data/sql/updates/pending_db_auth/rev_1770173499134350791.sql @@ -0,0 +1,1006 @@ +-- RBAC System for AzerothCore +-- Ported from TrinityCore + +-- Permission List table +DROP TABLE IF EXISTS `rbac_account_permissions`; +DROP TABLE IF EXISTS `rbac_default_permissions`; +DROP TABLE IF EXISTS `rbac_linked_permissions`; +DROP TABLE IF EXISTS `rbac_permissions`; + +CREATE TABLE `rbac_permissions` ( + `id` int unsigned NOT NULL DEFAULT '0' COMMENT 'Permission id', + `name` varchar(100) NOT NULL COMMENT 'Permission name', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Permission List'; + +-- Permission to Permission relation (for role inheritance) +CREATE TABLE `rbac_linked_permissions` ( + `id` int unsigned NOT NULL COMMENT 'Permission id', + `linkedId` int unsigned NOT NULL COMMENT 'Linked Permission id', + PRIMARY KEY (`id`,`linkedId`), + KEY `fk__rbac_linked_permissions__rbac_permissions1` (`id`), + KEY `fk__rbac_linked_permissions__rbac_permissions2` (`linkedId`), + CONSTRAINT `fk__rbac_linked_permissions__rbac_permissions1` FOREIGN KEY (`id`) REFERENCES `rbac_permissions` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk__rbac_linked_permissions__rbac_permissions2` FOREIGN KEY (`linkedId`) REFERENCES `rbac_permissions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Permission - Linked Permission relation'; + +-- Default permissions by security level +CREATE TABLE `rbac_default_permissions` ( + `secId` int unsigned NOT NULL COMMENT 'Security Level id', + `permissionId` int unsigned NOT NULL COMMENT 'permission id', + `realmId` int NOT NULL DEFAULT '-1' COMMENT 'Realm Id, -1 means all', + PRIMARY KEY (`secId`,`permissionId`,`realmId`), + KEY `fk__rbac_default_permissions__rbac_permissions` (`permissionId`), + CONSTRAINT `fk__rbac_default_permissions__rbac_permissions` FOREIGN KEY (`permissionId`) REFERENCES `rbac_permissions` (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Default permission to assign to different account security levels'; + +-- Account specific permissions +CREATE TABLE `rbac_account_permissions` ( + `accountId` int unsigned NOT NULL COMMENT 'Account id', + `permissionId` int unsigned NOT NULL COMMENT 'Permission id', + `granted` tinyint(1) NOT NULL DEFAULT '1' COMMENT 'Granted = 1, Denied = 0', + `realmId` int NOT NULL DEFAULT '-1' COMMENT 'Realm Id, -1 means all', + PRIMARY KEY (`accountId`,`permissionId`,`realmId`), + KEY `fk__rbac_account_roles__rbac_permissions` (`permissionId`), + CONSTRAINT `fk__rbac_account_permissions__account` FOREIGN KEY (`accountId`) REFERENCES `account` (`id`) ON DELETE CASCADE, + CONSTRAINT `fk__rbac_account_roles__rbac_permissions` FOREIGN KEY (`permissionId`) REFERENCES `rbac_permissions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Account-Permission relation'; + +-- ============================================================================ +-- Insert permissions +-- ============================================================================ +INSERT INTO `rbac_permissions` VALUES +-- Core permissions (1-53) +(1,'Instant logout'), +(2,'Skip Queue'), +(3,'Join Normal Battleground'), +(4,'Join Random Battleground'), +(5,'Join Arenas'), +(6,'Join Dungeon Finder'), +(7,'Skip idle connection check'), +(8,'Cannot earn achievements'), +(9,'Cannot earn realm first achievements'), +(11,'Log GM trades'), +(13,'Skip Instance required bosses check'), +(14,'Skip character creation team mask check'), +(15,'Skip character creation class mask check'), +(16,'Skip character creation race mask check'), +(17,'Skip character creation reserved name check'), +(18,'Skip character creation death knight min level check'), +(19,'Skip needed requirements to use channel check'), +(20,'Skip disable map check'), +(21,'Skip reset talents when used more than allowed check'), +(22,'Skip spam chat check'), +(23,'Skip over-speed ping check'), +(24,'Two side faction characters on the same account'), +(25,'Allow say chat between factions'), +(26,'Allow channel chat between factions'), +(27,'Two side mail interaction'), +(28,'See two side who list'), +(29,'Add friends of other faction'), +(30,'Save character without delay with .save command'), +(31,'Use params with .unstuck command'), +(32,'Can be assigned tickets with .assign ticket command'), +(33,'Notify if a command was not found'), +(34,'Check if should appear in list using .gm ingame command'), +(35,'See all security levels with who command'), +(36,'Filter whispers'), +(37,'Use staff badge in chat'), +(38,'Resurrect with full Health Points'), +(39,'Restore saved gm setting states'), +(40,'Allows to add a gm to friend list'), +(41,'Use Config option START_GM_LEVEL to assign new character level'), +(42,'Allows to use CMSG_WORLD_TELEPORT opcode'), +(43,'Allows to use CMSG_WHOIS opcode'), +(44,'Receive global GM messages/texts'), +(45,'Join channels without announce'), +(46,'Change channel settings without being channel moderator'), +(47,'Can ignore lower security checks'), +(48,'Enable IP, Last Login and EMail output in pinfo'), +(49,'Forces to enter the email for confirmation on password change'), +(50,'Allow user to check his own email with .account'), +(51,'Allow trading between factions'), +(52,'No battleground deserter debuff'), +(53,'Can be AFK on the battleground'), +-- Roles (192-199) +(192,'Role: Sec Level Administrator'), +(193,'Role: Sec Level Gamemaster'), +(194,'Role: Sec Level Moderator'), +(195,'Role: Sec Level Player'), +(196,'Role: Administrator Commands'), +(197,'Role: Gamemaster Commands'), +(198,'Role: Moderator Commands'), +(199,'Role: Player Commands'), +-- RBAC commands (200-206) +(200,'Command: rbac'), +(201,'Command: rbac account'), +(202,'Command: rbac account list'), +(203,'Command: rbac account grant'), +(204,'Command: rbac account deny'), +(205,'Command: rbac account revoke'), +(206,'Command: rbac list'), +-- Account commands (217-229) +(217,'Command: account'), +(218,'Command: account addon'), +(219,'Command: account create'), +(220,'Command: account delete'), +(221,'Command: account lock'), +(222,'Command: account lock country'), +(223,'Command: account lock ip'), +(224,'Command: account onlinelist'), +(225,'Command: account password'), +(226,'Command: account set'), +(227,'Command: account set addon'), +(228,'Command: account set gmlevel'), +(229,'Command: account set password'), +-- Achievement commands (231-232) +(231,'Command: achievement add'), +(232,'Command: achievement checkall'), +-- Arena commands (233-238) +(233,'Command: arena captain'), +(234,'Command: arena create'), +(235,'Command: arena disband'), +(236,'Command: arena info'), +(237,'Command: arena lookup'), +(238,'Command: arena rename'), +-- Ban commands (240-256) +(240,'Command: ban account'), +(241,'Command: ban character'), +(242,'Command: ban ip'), +(243,'Command: ban playeraccount'), +(245,'Command: baninfo account'), +(246,'Command: baninfo character'), +(247,'Command: baninfo ip'), +(249,'Command: banlist account'), +(250,'Command: banlist character'), +(251,'Command: banlist ip'), +(253,'Command: unban account'), +(254,'Command: unban character'), +(255,'Command: unban ip'), +(256,'Command: unban playeraccount'), +-- Battlefield commands (258-262) +(258,'Command: bf start'), +(259,'Command: bf stop'), +(260,'Command: bf switch'), +(261,'Command: bf timer'), +(262,'Command: bf enable'), +-- Account email commands (263-266) +(263,'Command: account email'), +(265,'Command: account set sec email'), +(266,'Command: account set sec regmail'), +-- Cast commands (267-272) +(267,'Command: cast'), +(268,'Command: cast back'), +(269,'Command: cast dist'), +(270,'Command: cast self'), +(271,'Command: cast target'), +(272,'Command: cast dest'), +-- Character commands (274-290) +(274,'Command: character customize'), +(275,'Command: character changefaction'), +(276,'Command: character changerace'), +(278,'Command: character deleted delete'), +(279,'Command: character deleted list'), +(280,'Command: character deleted restore'), +(281,'Command: character deleted old'), +(282,'Command: character erase'), +(283,'Command: character level'), +(284,'Command: character rename'), +(285,'Command: character reputation'), +(286,'Command: character titles'), +(287,'Command: levelup'), +(289,'Command: pdump load'), +(290,'Command: pdump write'), +-- Cheat commands (292-299) +(292,'Command: cheat casttime'), +(293,'Command: cheat cooldown'), +(294,'Command: cheat explore'), +(295,'Command: cheat god'), +(296,'Command: cheat power'), +(297,'Command: cheat status'), +(298,'Command: cheat taxi'), +(299,'Command: cheat waterwalk'), +-- Debug command (300) +(300,'Command: debug'), +-- Deserter commands (343-347) +(343,'Command: deserter bg add'), +(344,'Command: deserter bg remove'), +(346,'Command: deserter instance add'), +(347,'Command: deserter instance remove'), +-- Disable commands (350-366) +(350,'Command: disable add achievement_criteria'), +(351,'Command: disable add battleground'), +(352,'Command: disable add map'), +(353,'Command: disable add mmap'), +(354,'Command: disable add outdoorpvp'), +(355,'Command: disable add quest'), +(356,'Command: disable add spell'), +(357,'Command: disable add vmap'), +(359,'Command: disable remove achievement_criteria'), +(360,'Command: disable remove battleground'), +(361,'Command: disable remove map'), +(362,'Command: disable remove mmap'), +(363,'Command: disable remove outdoorpvp'), +(364,'Command: disable remove quest'), +(365,'Command: disable remove spell'), +(366,'Command: disable remove vmap'), +-- Event commands (367-370) +(367,'Command: event info'), +(368,'Command: event activelist'), +(369,'Command: event start'), +(370,'Command: event stop'), +-- GM commands (371-376) +(371,'Command: gm'), +(372,'Command: gm chat'), +(373,'Command: gm fly'), +(374,'Command: gm ingame'), +(375,'Command: gm list'), +(376,'Command: gm visible'), +-- Go command (377) +(377,'Command: go'), +-- Gobject commands (388-399) +(388,'Command: gobject activate'), +(389,'Command: gobject add'), +(390,'Command: gobject add temp'), +(391,'Command: gobject delete'), +(392,'Command: gobject info'), +(393,'Command: gobject move'), +(394,'Command: gobject near'), +(396,'Command: gobject set phase'), +(397,'Command: gobject set state'), +(398,'Command: gobject target'), +(399,'Command: gobject turn'), +-- Guild commands (401-407) +(401,'Command: guild'), +(402,'Command: guild create'), +(403,'Command: guild delete'), +(404,'Command: guild invite'), +(405,'Command: guild uninvite'), +(406,'Command: guild rank'), +(407,'Command: guild rename'), +-- Honor commands (409-411) +(409,'Command: honor add'), +(410,'Command: honor add kill'), +(411,'Command: honor update'), +-- Instance commands (413-416) +(413,'Command: instance listbinds'), +(414,'Command: instance unbind'), +(415,'Command: instance stats'), +(416,'Command: instance savedata'), +-- Learn commands (417-429) +(417,'Command: learn'), +(419,'Command: learn all my'), +(420,'Command: learn all my class'), +(421,'Command: learn my pettalents'), +(422,'Command: learn all my spells'), +(423,'Command: learn all talents'), +(424,'Command: learn all gm'), +(425,'Command: learn all crafts'), +(426,'Command: learn all default'), +(427,'Command: learn all lang'), +(428,'Command: learn all recipes'), +(429,'Command: unlearn'), +-- LFG commands (431-436) +(431,'Command: lfg player'), +(432,'Command: lfg group'), +(433,'Command: lfg queue'), +(434,'Command: lfg clean'), +(435,'Command: lfg options'), +(436,'Command: lfg cooldown'), +-- List commands (437-441) +(437,'Command: list creature'), +(438,'Command: list item'), +(439,'Command: list object'), +(440,'Command: list auras'), +(441,'Command: list mail'), +-- Lookup commands (442-461) +(442,'Command: lookup'), +(443,'Command: lookup area'), +(444,'Command: lookup creature'), +(445,'Command: lookup event'), +(446,'Command: lookup faction'), +(447,'Command: lookup item'), +(448,'Command: lookup itemset'), +(449,'Command: lookup object'), +(450,'Command: lookup quest'), +(451,'Command: lookup player'), +(452,'Command: lookup player ip'), +(453,'Command: lookup player account'), +(454,'Command: lookup player email'), +(455,'Command: lookup skill'), +(456,'Command: lookup spell'), +(457,'Command: lookup spell id'), +(458,'Command: lookup taxinode'), +(459,'Command: lookup tele'), +(460,'Command: lookup title'), +(461,'Command: lookup map'), +-- Announce/Channel commands (462-471) +(462,'Command: announce'), +(463,'Command: channel'), +(464,'Command: channel set'), +(465,'Command: channel set ownership'), +(466,'Command: gmannounce'), +(467,'Command: gmnameannounce'), +(468,'Command: gmnotify'), +(469,'Command: nameannounce'), +(470,'Command: notify'), +(471,'Command: whispers'), +-- Group commands (472-478) +(472,'Command: group'), +(473,'Command: group leader'), +(474,'Command: group disband'), +(475,'Command: group remove'), +(476,'Command: group join'), +(477,'Command: group list'), +(478,'Command: group summon'), +-- Pet commands (479-482) +(479,'Command: pet'), +(480,'Command: pet create'), +(481,'Command: pet learn'), +(482,'Command: pet unlearn'), +-- Send commands (483-487) +(483,'Command: send'), +(484,'Command: send items'), +(485,'Command: send mail'), +(486,'Command: send message'), +(487,'Command: send money'), +-- Misc commands (488-535) +(488,'Command: additem'), +(489,'Command: additemset'), +(490,'Command: appear'), +(491,'Command: aura'), +(492,'Command: bank'), +(493,'Command: bindsight'), +(494,'Command: combatstop'), +(495,'Command: cometome'), +(496,'Command: commands'), +(497,'Command: cooldown'), +(498,'Command: damage'), +(499,'Command: dev'), +(500,'Command: die'), +(501,'Command: dismount'), +(502,'Command: distance'), +(503,'Command: flusharenapoints'), +(504,'Command: freeze'), +(505,'Command: gps'), +(506,'Command: guid'), +(507,'Command: help'), +(508,'Command: hidearea'), +(509,'Command: itemmove'), +(510,'Command: kick'), +(511,'Command: linkgrave'), +(512,'Command: listfreeze'), +(513,'Command: maxskill'), +(514,'Command: movegens'), +(515,'Command: mute'), +(516,'Command: neargrave'), +(517,'Command: pinfo'), +(518,'Command: playall'), +(519,'Command: possess'), +(520,'Command: recall'), +(521,'Command: repairitems'), +(522,'Command: respawn'), +(523,'Command: revive'), +(524,'Command: saveall'), +(525,'Command: save'), +(526,'Command: setskill'), +(527,'Command: showarea'), +(528,'Command: summon'), +(529,'Command: unaura'), +(530,'Command: unbindsight'), +(531,'Command: unfreeze'), +(532,'Command: unmute'), +(533,'Command: unpossess'), +(534,'Command: unstuck'), +(535,'Command: wchange'), +-- MMap commands (536-541) +(536,'Command: mmap'), +(537,'Command: mmap loadedtiles'), +(538,'Command: mmap loc'), +(539,'Command: mmap path'), +(540,'Command: mmap stats'), +(541,'Command: mmap testarea'), +-- Morph commands (542-543) +(542,'Command: morph'), +(543,'Command: demorph'), +-- Modify commands (544-569) +(544,'Command: modify'), +(545,'Command: modify arenapoints'), +(546,'Command: modify bit'), +(547,'Command: modify drunk'), +(548,'Command: modify energy'), +(549,'Command: modify faction'), +(550,'Command: modify gender'), +(551,'Command: modify honor'), +(552,'Command: modify hp'), +(553,'Command: modify mana'), +(554,'Command: modify money'), +(555,'Command: modify mount'), +(556,'Command: modify phase'), +(557,'Command: modify rage'), +(558,'Command: modify reputation'), +(559,'Command: modify runicpower'), +(560,'Command: modify scale'), +(561,'Command: modify speed'), +(562,'Command: modify speed all'), +(563,'Command: modify speed backwalk'), +(564,'Command: modify speed fly'), +(565,'Command: modify speed walk'), +(566,'Command: modify speed swim'), +(567,'Command: modify spell'), +(568,'Command: modify standstate'), +(569,'Command: modify talentpoints'), +-- NPC commands (571-601) +(571,'Command: npc add'), +(572,'Command: npc add formation'), +(573,'Command: npc add item'), +(574,'Command: npc add move'), +(575,'Command: npc add temp'), +(576,'Command: npc delete'), +(577,'Command: npc delete item'), +(578,'Command: npc follow'), +(579,'Command: npc follow stop'), +(580,'Command: npc set'), +(581,'Command: npc set allowmove'), +(582,'Command: npc set entry'), +(583,'Command: npc set factionid'), +(584,'Command: npc set flag'), +(585,'Command: npc set level'), +(586,'Command: npc set link'), +(587,'Command: npc set model'), +(588,'Command: npc set movetype'), +(589,'Command: npc set phase'), +(590,'Command: npc set spawndist'), +(591,'Command: npc set spawntime'), +(592,'Command: npc set data'), +(593,'Command: npc info'), +(594,'Command: npc near'), +(595,'Command: npc move'), +(596,'Command: npc playemote'), +(597,'Command: npc say'), +(598,'Command: npc textemote'), +(599,'Command: npc whisper'), +(600,'Command: npc yell'), +(601,'Command: npc tame'), +-- Quest commands (602-606) +(602,'Command: quest'), +(603,'Command: quest add'), +(604,'Command: quest complete'), +(605,'Command: quest remove'), +(606,'Command: quest reward'), +-- Reload commands (607-709) +(607,'Command: reload'), +(608,'Command: reload access_requirement'), +(609,'Command: reload achievement_criteria_data'), +(610,'Command: reload achievement_reward'), +(611,'Command: reload all'), +(612,'Command: reload all achievement'), +(613,'Command: reload all area'), +(614,'Command: reload broadcast_text'), +(615,'Command: reload all gossips'), +(616,'Command: reload all item'), +(617,'Command: reload all locales'), +(618,'Command: reload all loot'), +(619,'Command: reload all npc'), +(620,'Command: reload all quest'), +(621,'Command: reload all scripts'), +(622,'Command: reload all spell'), +(623,'Command: reload areatrigger_involvedrelation'), +(624,'Command: reload areatrigger_tavern'), +(625,'Command: reload areatrigger_teleport'), +(626,'Command: reload auctions'), +(627,'Command: reload autobroadcast'), +(629,'Command: reload conditions'), +(630,'Command: reload config'), +(631,'Command: reload battleground_template'), +(632,'Command: mutehistory'), +(633,'Command: reload creature_linked_respawn'), +(634,'Command: reload creature_loot_template'), +(635,'Command: reload creature_onkill_reputation'), +(636,'Command: reload creature_questender'), +(637,'Command: reload creature_queststarter'), +(638,'Command: reload creature_summon_groups'), +(639,'Command: reload creature_template'), +(640,'Command: reload creature_text'), +(641,'Command: reload disables'), +(642,'Command: reload disenchant_loot_template'), +(643,'Command: reload event_scripts'), +(644,'Command: reload fishing_loot_template'), +(645,'Command: reload graveyard_zone'), +(646,'Command: reload game_tele'), +(647,'Command: reload gameobject_questender'), +(648,'Command: reload gameobject_quest_loot_template'), +(649,'Command: reload gameobject_queststarter'), +(650,'Command: reload gm_tickets'), +(651,'Command: reload gossip_menu'), +(652,'Command: reload gossip_menu_option'), +(653,'Command: reload item_enchantment_template'), +(654,'Command: reload item_loot_template'), +(655,'Command: reload item_set_names'), +(656,'Command: reload lfg_dungeon_rewards'), +(657,'Command: reload achievement_reward_locale'), +(658,'Command: reload creature_template_locale'), +(659,'Command: reload creature_text_locale'), +(660,'Command: reload gameobject_template_locale'), +(661,'Command: reload gossip_menu_option_locale'), +(662,'Command: reload item_template_locale'), +(663,'Command: reload item_set_name_locale'), +(664,'Command: reload npc_text_locale'), +(665,'Command: reload page_text_locale'), +(666,'Command: reload points_of_interest_locale'), +(667,'Command: reload quest_template_locale'), +(668,'Command: reload mail_level_reward'), +(669,'Command: reload mail_loot_template'), +(670,'Command: reload milling_loot_template'), +(671,'Command: reload npc_spellclick_spells'), +(672,'Command: reload trainer'), +(673,'Command: reload npc_vendor'), +(674,'Command: reload page_text'), +(675,'Command: reload pickpocketing_loot_template'), +(676,'Command: reload points_of_interest'), +(677,'Command: reload prospecting_loot_template'), +(678,'Command: reload quest_poi'), +(679,'Command: reload quest_template'), +(680,'Command: reload rbac'), +(681,'Command: reload reference_loot_template'), +(682,'Command: reload reserved_name'), +(683,'Command: reload reputation_reward_rate'), +(684,'Command: reload reputation_spillover_template'), +(685,'Command: reload skill_discovery_template'), +(686,'Command: reload skill_extra_item_template'), +(687,'Command: reload skill_fishing_base_level'), +(688,'Command: reload skinning_loot_template'), +(689,'Command: reload smart_scripts'), +(690,'Command: reload spell_required'), +(691,'Command: reload spell_area'), +(692,'Command: reload spell_bonus_data'), +(693,'Command: reload spell_group'), +(694,'Command: reload spell_learn_spell'), +(695,'Command: reload spell_loot_template'), +(696,'Command: reload spell_linked_spell'), +(697,'Command: reload spell_pet_auras'), +(698,'Command: character changeaccount'), +(699,'Command: reload spell_proc'), +(701,'Command: reload spell_target_position'), +(702,'Command: reload spell_threats'), +(703,'Command: reload spell_group_stack_rules'), +(704,'Command: reload acore_string'), +(706,'Command: reload waypoint_scripts'), +(707,'Command: reload waypoint_data'), +(708,'Command: reload vehicle_accessory'), +(709,'Command: reload vehicle_template_accessory'), +-- Reset commands (710-717) +(710,'Command: reset'), +(711,'Command: reset achievements'), +(712,'Command: reset honor'), +(713,'Command: reset level'), +(714,'Command: reset spells'), +(715,'Command: reset stats'), +(716,'Command: reset talents'), +(717,'Command: reset all'), +-- Server commands (718-736) +(718,'Command: server'), +(719,'Command: server corpses'), +(720,'Command: server exit'), +(721,'Command: server idlerestart'), +(722,'Command: server idlerestart cancel'), +(723,'Command: server idleshutdown'), +(724,'Command: server idleshutdown cancel'), +(725,'Command: server info'), +(726,'Command: server plimit'), +(727,'Command: server restart'), +(728,'Command: server restart cancel'), +(729,'Command: server set'), +(730,'Command: server set closed'), +(731,'Command: server set difftime'), +(732,'Command: server set loglevel'), +(733,'Command: server set motd'), +(734,'Command: server shutdown'), +(735,'Command: server shutdown cancel'), +(736,'Command: server motd'), +-- Tele commands (737-741) +(737,'Command: tele'), +(738,'Command: tele add'), +(739,'Command: tele del'), +(740,'Command: tele name'), +(741,'Command: tele group'), +-- Ticket commands (742-760) +(742,'Command: ticket'), +(743,'Command: ticket assign'), +(744,'Command: ticket close'), +(745,'Command: ticket closedlist'), +(746,'Command: ticket comment'), +(747,'Command: ticket complete'), +(748,'Command: ticket delete'), +(749,'Command: ticket escalate'), +(750,'Command: ticket escalatedlist'), +(751,'Command: ticket list'), +(752,'Command: ticket onlinelist'), +(753,'Command: ticket reset'), +(754,'Command: ticket response'), +(755,'Command: ticket response append'), +(756,'Command: ticket response appendln'), +(757,'Command: ticket togglesystem'), +(758,'Command: ticket unassign'), +(759,'Command: ticket viewid'), +(760,'Command: ticket viewname'), +-- Titles commands (762-766) +(762,'Command: titles add'), +(763,'Command: titles current'), +(764,'Command: titles remove'), +(766,'Command: titles set mask'), +-- WP commands (767-774) +(767,'Command: wp'), +(768,'Command: wp add'), +(769,'Command: wp event'), +(770,'Command: wp load'), +(771,'Command: wp modify'), +(772,'Command: wp unload'), +(773,'Command: wp reload'), +(774,'Command: wp show'), +-- Mailbox command (777) +(777,'Command: mailbox'), +-- AHBot commands (779-793) +(779,'Command: ahbot items'), +(780,'Command: ahbot items gray'), +(781,'Command: ahbot items white'), +(782,'Command: ahbot items green'), +(783,'Command: ahbot items blue'), +(784,'Command: ahbot items purple'), +(785,'Command: ahbot items orange'), +(786,'Command: ahbot items yellow'), +(787,'Command: ahbot ratio'), +(788,'Command: ahbot ratio alliance'), +(789,'Command: ahbot ratio horde'), +(790,'Command: ahbot ratio neutral'), +(791,'Command: ahbot rebuild'), +(792,'Command: ahbot reload'), +(793,'Command: ahbot status'), +-- Guild info / Instance boss / PVPStats / Modify XP (794-798) +(794,'Command: guild info'), +(795,'Command: instance setbossstate'), +(796,'Command: instance getbossstate'), +(797,'Command: pvpstats'), +(798,'Command: modify xp'), +-- Additional commands (837-843) +(837,'Command: npc evade'), +(838,'Command: pet level'), +(839,'Command: server shutdown force'), +(840,'Command: server restart force'), +(841,'Command: neargraveyard'), +(843,'Command: reload quest_greeting'), +-- Spawngroup / Group / NPC / List commands (856-868) +(856,'Command: npc spawngroup'), +(857,'Command: npc despawngroup'), +(858,'Command: gobject spawngroup'), +(859,'Command: gobject despawngroup'), +(860,'Command: list respawns'), +(861,'Command: group set'), +(862,'Command: group set assistant'), +(863,'Command: group set maintank'), +(864,'Command: group set mainassist'), +(865,'Command: npc showloot'), +(866,'Command: list spawnpoints'), +(867,'Command: reload quest_greeting_locale'), +(868,'Command: group revive'), +-- Server debug / Reload / Settings / Lookup (872-877) +(872,'Command: server debug'), +(873,'Command: reload creature_movement_override'), +(874,'Command: settings announcer'), +(875,'Command: lookup map id'), +(876,'Command: lookup item id'), +(877,'Command: lookup quest id'), +-- PDump copy / Reload vehicle template (880-881) +(880,'Command: pdump copy'), +(881,'Command: reload vehicle_template'), +-- BG start/stop (884-885) +(884,'Command: bg start'), +(885,'Command: bg stop'), +-- AC-specific commands (886-896) +(886,'Command: item restore'), +(887,'Command: item restore list'), +(888,'Command: item refund'), +(889,'Command: commentator'), +(890,'Command: skirmish'), +(891,'Command: string'), +(892,'Command: opendoor'), +(893,'Command: beastmaster'), +(894,'Command: packetlog'), +(895,'Command: aura stack'), +(896,'Command: respawn all'), +-- Gear / Spectator commands (897-904) +(897,'Command: gear repair'), +(898,'Command: gear stats'), +(899,'Command: spect'), +(900,'Command: spect version'), +(901,'Command: spect reset'), +(902,'Command: spect spectate'), +(903,'Command: spect watch'), +(904,'Command: spect leave'), +-- Arena season commands (905-908) +(905,'Command: arena season state'), +(906,'Command: arena season reward'), +(907,'Command: arena season deleteteams'), +(908,'Command: arena season start'), +-- Character check commands (909-911) +(909,'Command: character check bank'), +(910,'Command: character check bag'), +(911,'Command: character check profession'), +(912,'Command: gobject load'), +(913,'Command: bf queue'); + +-- ============================================================================ +-- Link roles to permissions +-- ============================================================================ +INSERT INTO `rbac_linked_permissions` VALUES +-- --------------------------------------------------------------------------- +-- Role 192 (Administrator): Core admin permissions + links to GM role and Admin commands +-- --------------------------------------------------------------------------- +(192, 7), -- Skip idle check +(192, 21), -- Skip talent reset check +(192, 42), -- World teleport opcode +(192, 43), -- Whois opcode +(192, 193), -- Links to GM role +(192, 196), -- Admin commands role +-- --------------------------------------------------------------------------- +-- Role 193 (Gamemaster): GM-specific core permissions + links to Mod role and GM commands +-- --------------------------------------------------------------------------- +(193, 45), -- Join channels without announce +(193, 48), -- Pinfo personal data +(193, 52), -- No BG deserter debuff +(193, 53), -- Can AFK in BG +(193, 194), -- Links to Moderator role +(193, 197), -- GM commands role +-- --------------------------------------------------------------------------- +-- Role 194 (Moderator): Moderator core permissions + links to Player role and Mod commands +-- --------------------------------------------------------------------------- +(194, 1), -- Instant logout +(194, 2), -- Skip queue +(194, 9), -- Cannot earn realm first achievements +(194, 11), -- Log GM trades +(194, 13), -- Skip instance required bosses check +(194, 14), -- Skip character creation team mask check +(194, 15), -- Skip character creation class mask check +(194, 16), -- Skip character creation race mask check +(194, 17), -- Skip character creation reserved name check +(194, 18), -- Skip character creation death knight check +(194, 19), -- Skip channel requirements check +(194, 20), -- Skip disable map check +(194, 22), -- Skip spam chat check +(194, 23), -- Skip over-speed ping check +(194, 25), -- Allow say chat between factions +(194, 26), -- Allow channel chat between factions +(194, 27), -- Two side mail interaction +(194, 28), -- See two side who list +(194, 29), -- Add friends of other faction +(194, 30), -- Save without delay +(194, 31), -- Unstuck with args +(194, 32), -- Can be assigned tickets +(194, 33), -- Notify command not found +(194, 34), -- Appear in GM list +(194, 35), -- See all sec levels +(194, 36), -- Filter whispers +(194, 37), -- Staff badge +(194, 38), -- Resurrect full HP +(194, 39), -- Restore GM state +(194, 40), -- Allow GM friend +(194, 41), -- Start GM level +(194, 44), -- Receive global GM messages +(194, 46), -- Change channel settings +(194, 47), -- Ignore lower security +(194, 51), -- Allow two side trade +(194, 195), -- Links to Player role +(194, 198), -- Mod commands role +(194, 632), -- Mutehistory +(194, 798), -- Modify XP +-- --------------------------------------------------------------------------- +-- Role 195 (Player): Basic player permissions + player commands +-- --------------------------------------------------------------------------- +(195, 3), -- Join BG +(195, 4), -- Join Random BG +(195, 5), -- Join Arenas +(195, 6), -- Join Dungeon Finder +(195, 24), -- Two side character creation +(195, 49), -- Email confirm for pass change +(195, 199), -- Player commands role +-- --------------------------------------------------------------------------- +-- Role 196 (Admin Commands): Server management, reload, reset, disable, etc. +-- --------------------------------------------------------------------------- +-- RBAC commands +(196, 200), (196, 201), (196, 202), (196, 203), (196, 204), (196, 205), (196, 206), +-- Account administration +(196, 219), (196, 220), (196, 224), (196, 226), (196, 227), (196, 228), (196, 229), +(196, 265), (196, 266), +-- Battlefield management +(196, 258), (196, 259), (196, 260), (196, 261), (196, 262), +-- Character admin (destructive operations) +(196, 278), (196, 281), (196, 282), (196, 698), +-- PDump +(196, 289), (196, 290), (196, 880), +-- Disable commands +(196, 350), (196, 351), (196, 352), (196, 353), (196, 354), (196, 355), (196, 356), (196, 357), +(196, 359), (196, 360), (196, 361), (196, 362), (196, 363), (196, 364), (196, 365), (196, 366), +-- Channel management +(196, 463), (196, 464), (196, 465), +-- Mass operations +(196, 503), (196, 511), (196, 524), +-- Reload commands +(196, 607), (196, 608), (196, 609), (196, 610), (196, 611), (196, 612), (196, 613), (196, 614), +(196, 615), (196, 616), (196, 617), (196, 618), (196, 619), (196, 620), (196, 621), (196, 622), +(196, 623), (196, 624), (196, 625), (196, 626), (196, 627), (196, 629), (196, 630), (196, 631), +(196, 633), (196, 634), (196, 635), (196, 636), (196, 637), (196, 638), (196, 639), (196, 640), +(196, 641), (196, 642), (196, 643), (196, 644), (196, 645), (196, 646), (196, 647), (196, 648), +(196, 649), (196, 650), (196, 651), (196, 652), (196, 653), (196, 654), (196, 655), (196, 656), +(196, 657), (196, 658), (196, 659), (196, 660), (196, 661), (196, 662), (196, 663), (196, 664), +(196, 665), (196, 666), (196, 667), (196, 668), (196, 669), (196, 670), (196, 671), (196, 672), +(196, 673), (196, 674), (196, 675), (196, 676), (196, 677), (196, 678), (196, 679), (196, 680), +(196, 681), (196, 682), (196, 683), (196, 684), (196, 685), (196, 686), (196, 687), (196, 688), +(196, 689), (196, 690), (196, 691), (196, 692), (196, 693), (196, 694), (196, 695), (196, 696), +(196, 697), (196, 699), (196, 701), (196, 702), (196, 703), (196, 704), (196, 706), (196, 707), +(196, 708), (196, 709), +(196, 843), (196, 867), (196, 873), (196, 881), +-- Reset commands +(196, 710), (196, 711), (196, 712), (196, 713), (196, 714), (196, 715), (196, 716), (196, 717), +-- Server commands +(196, 718), (196, 719), (196, 720), (196, 721), (196, 722), (196, 723), (196, 724), +(196, 726), (196, 727), (196, 728), (196, 729), (196, 730), (196, 731), (196, 732), +(196, 733), (196, 734), (196, 735), (196, 736), +(196, 839), (196, 840), (196, 872), +-- Ticket admin (destructive) +(196, 748), (196, 753), (196, 757), +-- WP reload +(196, 773), +-- Mailbox +(196, 777), +-- AHBot +(196, 779), (196, 780), (196, 781), (196, 782), (196, 783), (196, 784), (196, 785), +(196, 786), (196, 787), (196, 788), (196, 789), (196, 790), (196, 791), (196, 792), (196, 793), +-- Gobject load / List spawnpoints +(196, 912), (196, 866), +-- Arena season commands +(196, 905), (196, 906), (196, 907), (196, 908), +-- AC-specific admin commands +(196, 886), (196, 887), (196, 888), (196, 891), (196, 894), (196, 896), +-- --------------------------------------------------------------------------- +-- Role 197 (GM Commands): Game manipulation, character, NPC, gobject, quest, etc. +-- --------------------------------------------------------------------------- +-- Achievement +(197, 231), (197, 232), +-- Arena +(197, 233), (197, 234), (197, 235), (197, 236), (197, 237), (197, 238), +-- Cast +(197, 267), (197, 268), (197, 269), (197, 270), (197, 271), (197, 272), +-- Character +(197, 274), (197, 275), (197, 276), (197, 279), (197, 280), +(197, 283), (197, 284), (197, 285), (197, 286), (197, 287), +(197, 909), (197, 910), (197, 911), +-- Cheat +(197, 292), (197, 293), (197, 294), (197, 295), (197, 296), (197, 297), (197, 298), (197, 299), +-- Debug +(197, 300), +-- Deserter +(197, 343), (197, 344), (197, 346), (197, 347), +-- Event +(197, 367), (197, 368), (197, 369), (197, 370), +-- GM / Go +(197, 371), (197, 372), (197, 373), (197, 376), (197, 377), +-- Gobject +(197, 388), (197, 389), (197, 390), (197, 391), (197, 392), (197, 393), (197, 394), +(197, 396), (197, 397), (197, 398), (197, 399), +-- Guild +(197, 401), (197, 402), (197, 403), (197, 404), (197, 405), (197, 406), (197, 407), +-- Honor +(197, 409), (197, 410), (197, 411), +-- Instance +(197, 413), (197, 414), (197, 415), (197, 416), +-- Learn +(197, 417), (197, 419), (197, 420), (197, 421), (197, 422), (197, 423), (197, 424), +(197, 425), (197, 426), (197, 427), (197, 428), (197, 429), +-- LFG +(197, 431), (197, 432), (197, 433), (197, 434), (197, 435), (197, 436), +-- List +(197, 437), (197, 438), (197, 439), (197, 440), (197, 441), +-- Lookup (GM-level) +(197, 445), (197, 448), (197, 449), (197, 451), (197, 452), (197, 453), (197, 454), +(197, 457), (197, 458), (197, 460), (197, 461), +-- Group +(197, 472), (197, 473), (197, 474), (197, 475), (197, 476), (197, 477), (197, 478), +-- Pet +(197, 479), (197, 480), (197, 481), (197, 482), +-- Send +(197, 483), (197, 484), (197, 485), (197, 486), (197, 487), +-- Misc game commands +(197, 488), (197, 489), (197, 490), (197, 491), (197, 492), (197, 493), (197, 494), (197, 495), +(197, 497), (197, 498), (197, 499), (197, 500), +(197, 502), (197, 504), (197, 506), (197, 508), (197, 509), +(197, 512), (197, 513), (197, 514), (197, 516), (197, 518), (197, 519), (197, 520), +(197, 521), (197, 522), (197, 523), (197, 526), (197, 527), (197, 528), (197, 529), (197, 530), (197, 531), +(197, 533), (197, 535), +-- MMap +(197, 536), (197, 537), (197, 538), (197, 539), (197, 540), (197, 541), +-- Morph +(197, 542), (197, 543), +-- Modify +(197, 544), (197, 545), (197, 546), (197, 547), (197, 548), (197, 549), (197, 550), (197, 551), +(197, 552), (197, 553), (197, 554), (197, 555), (197, 556), (197, 557), (197, 558), (197, 559), +(197, 560), (197, 561), (197, 562), (197, 563), (197, 564), (197, 565), (197, 566), (197, 567), +(197, 568), (197, 569), +-- NPC +(197, 571), (197, 572), (197, 573), (197, 574), (197, 575), (197, 576), (197, 577), (197, 578), +(197, 579), (197, 580), (197, 581), (197, 582), (197, 583), (197, 584), (197, 585), (197, 586), +(197, 587), (197, 588), (197, 589), (197, 590), (197, 591), (197, 592), (197, 593), (197, 594), +(197, 595), (197, 596), (197, 597), (197, 598), (197, 599), (197, 600), (197, 601), +-- Quest +(197, 602), (197, 603), (197, 604), (197, 605), (197, 606), +-- Tele +(197, 737), (197, 738), (197, 739), (197, 740), (197, 741), +-- Titles +(197, 762), (197, 763), (197, 764), (197, 766), +-- WP (except reload which is in 196) +(197, 767), (197, 768), (197, 769), (197, 770), (197, 771), (197, 772), (197, 774), +-- Guild info / Instance boss state / Modify XP +(197, 794), (197, 795), (197, 796), (197, 798), +-- NPC evade / Pet level / Neargraveyard +(197, 837), (197, 838), (197, 841), +-- Spawngroup / List respawns +(197, 856), (197, 857), (197, 858), (197, 859), (197, 860), +-- Group set / NPC showloot +(197, 861), (197, 862), (197, 863), (197, 864), (197, 865), +-- Group revive / Settings announcer +(197, 868), (197, 874), +-- Lookup map/item/quest id +(197, 875), (197, 876), (197, 877), +-- BG start/stop / BF queue +(197, 884), (197, 885), (197, 913), +-- AC-specific GM commands +(197, 889), (197, 890), (197, 892), (197, 893), (197, 895), +-- Gear repair +(197, 897), +-- --------------------------------------------------------------------------- +-- Role 198 (Mod Commands): Community management - ban, mute, kick, tickets, announce +-- --------------------------------------------------------------------------- +-- Ban/Unban +(198, 240), (198, 241), (198, 242), (198, 243), +(198, 245), (198, 246), (198, 247), +(198, 249), (198, 250), (198, 251), +(198, 253), (198, 254), (198, 255), (198, 256), +-- Announce/Notify +(198, 462), (198, 466), (198, 467), (198, 468), (198, 469), (198, 470), (198, 471), +-- Kick/Mute/Pinfo/Unmute +(198, 510), (198, 515), (198, 517), (198, 532), +-- Mutehistory +(198, 632), +-- Ticket commands (excluding admin-only 748 delete, 753 reset, 757 togglesystem) +(198, 742), (198, 743), (198, 744), (198, 745), (198, 746), (198, 747), +(198, 749), (198, 750), (198, 751), (198, 752), +(198, 754), (198, 755), (198, 756), (198, 758), (198, 759), (198, 760), +-- --------------------------------------------------------------------------- +-- Role 199 (Player Commands): Self-service account management, basic lookups +-- --------------------------------------------------------------------------- +-- Account self-management +(199, 217), (199, 218), (199, 221), (199, 222), (199, 223), (199, 225), (199, 263), +-- GM info +(199, 374), (199, 375), +-- Lookup (basic) +(199, 442), (199, 443), (199, 444), (199, 446), (199, 447), (199, 450), (199, 455), (199, 456), (199, 459), +-- Basic commands +(199, 496), (199, 501), (199, 505), (199, 507), (199, 525), (199, 534), +-- Server info / PVPStats +(199, 725), (199, 797), +-- Gear stats +(199, 898), +-- Spectator +(199, 899), (199, 900), (199, 901), (199, 902), (199, 903), (199, 904); + +-- ============================================================================ +-- Default permissions by security level +-- ============================================================================ +-- SEC_PLAYER = 0 gets Player role (195) +-- SEC_MODERATOR = 1 gets Moderator role (194, which includes 195) +-- SEC_GAMEMASTER = 2 gets GM role (193, which includes 194) +-- SEC_ADMINISTRATOR = 3 gets Admin role (192, which includes 193) +INSERT INTO `rbac_default_permissions` VALUES +(3, 192, -1), -- Admin gets Admin role +(2, 193, -1), -- GM gets GM role +(1, 194, -1), -- Moderator gets Mod role +(0, 195, -1); -- Player gets Player role diff --git a/data/sql/updates/pending_db_world/rev_1770173911371830386.sql b/data/sql/updates/pending_db_world/rev_1770173911371830386.sql new file mode 100644 index 000000000..c91c489bc --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1770173911371830386.sql @@ -0,0 +1,20 @@ +-- RBAC Language strings (uses {} format placeholders for Acore::StringFormat) +DELETE FROM `acore_string` WHERE `entry` BETWEEN 63 AND 79; +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES +(63, 'Wrong parameter id: {}, does not exist'), +(64, 'Wrong parameter realmId: {}'), +(65, 'Account {} ({}) granted permissions:'), +(66, 'Account {} ({}) denied permissions:'), +(67, 'Account {} ({}) inherited permissions by sec level {} ({}):'), +(68, 'Permissions:'), +(69, 'Linked permissions:'), +(70, 'Empty List'), +(71, '- {} ({})'), +(72, 'Could not grant permission {} ({}) realmId {}. Account {} ({}) already has that permission'), +(73, 'Could not grant permission {} ({}) realmId {}. Account {} ({}) has that permission in deny list'), +(74, 'Granted permission {} ({}) realmId {} to account {} ({})'), +(75, 'Could not deny permission {} ({}) realmId {}. Account {} ({}) already has that permission'), +(76, 'Could not deny permission {} ({}) realmId {}. Account {} ({}) has that permission in grant list'), +(77, 'Denied permission {} ({}) realmId {} to account {} ({})'), +(78, 'Revoked permission {} ({}) realmId {} to account {} ({})'), +(79, 'Could not revoke permission {} ({}) realmId {}. Account {} ({}) does not have that permission'); diff --git a/data/sql/updates/pending_db_world/rev_1777553106653644140.sql b/data/sql/updates/pending_db_world/rev_1777553106653644140.sql new file mode 100644 index 000000000..b1bd2ea86 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777553106653644140.sql @@ -0,0 +1,12 @@ +-- RBAC command help strings +DELETE FROM `acore_string` WHERE `entry` BETWEEN 83 AND 86; +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES +(83, 'Syntax: .rbac account list $account\nList granted, denied, and inherited permissions for the account.'), +(84, 'Syntax: .rbac account grant $account $permissionId [$realmId]\nGrant a permission to the account. Optional realmId (-1 = all realms).'), +(85, 'Syntax: .rbac account deny $account $permissionId [$realmId]\nDeny a permission for the account. Optional realmId (-1 = all realms).'), +(86, 'Syntax: .rbac account revoke $account $permissionId [$realmId]\nRevoke a previously granted or denied permission. Optional realmId (-1 = all realms).'); + +-- Update inherited permissions header to include sec level name +DELETE FROM `acore_string` WHERE `entry` = 67; +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES +(67, 'Account {} ({}) inherited permissions by sec level {} ({}):'); diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 256664672..a90e1bcd7 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -153,6 +153,14 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_UPD_ACCOUNT_TOTP_SECRET, "UPDATE account SET totp_secret = ? WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_UPTIME, "INSERT INTO uptime (realmid, starttime, uptime, revision) VALUES (?, ?, 0, ?)", CONNECTION_ASYNC); + + PrepareStatement(LOGIN_GET_EMAIL_BY_ID, "SELECT email FROM account WHERE id = ?", CONNECTION_SYNCH); + + // RBAC + PrepareStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, "SELECT permissionId, granted FROM rbac_account_permissions WHERE accountId = ? AND (realmId = ? OR realmId = -1) ORDER BY permissionId, realmId", CONNECTION_BOTH); + PrepareStatement(LOGIN_INS_RBAC_ACCOUNT_PERMISSION, "INSERT INTO rbac_account_permissions (accountId, permissionId, granted, realmId) VALUES (?, ?, ?, ?) ON DUPLICATE KEY UPDATE granted = VALUES(granted)", CONNECTION_ASYNC); + PrepareStatement(LOGIN_DEL_RBAC_ACCOUNT_PERMISSION, "DELETE FROM rbac_account_permissions WHERE accountId = ? AND permissionId = ? AND (realmId = ? OR realmId = -1)", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_RBAC_DEFAULT_PERMISSIONS, "SELECT secId, permissionId FROM rbac_default_permissions WHERE (realmId = ? OR realmId = -1) ORDER BY secId ASC", CONNECTION_SYNCH); } LoginDatabaseConnection::LoginDatabaseConnection(MySQLConnectionInfo& connInfo) : MySQLConnection(connInfo) diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index 6b6debb81..140166dda 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -133,6 +133,14 @@ enum LoginDatabaseStatements : uint32 LOGIN_INS_UPTIME, + LOGIN_GET_EMAIL_BY_ID, + + // RBAC + LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS, + LOGIN_INS_RBAC_ACCOUNT_PERMISSION, + LOGIN_DEL_RBAC_ACCOUNT_PERMISSION, + LOGIN_SEL_RBAC_DEFAULT_PERMISSIONS, + MAX_LOGINDATABASE_STATEMENTS }; diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 8be9d5168..83e689fa9 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -1,10 +1,10 @@ /* * 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 General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 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 @@ -18,315 +18,525 @@ #include "AccountMgr.h" #include "Common.h" #include "DatabaseEnv.h" +#include "Log.h" #include "ObjectAccessor.h" #include "Player.h" +#include "Realm.h" #include "SRP6.h" #include "ScriptMgr.h" #include "Util.h" #include "WorldSession.h" -namespace AccountMgr +AccountMgr::AccountMgr() { } + +AccountMgr::~AccountMgr() { + ClearRBAC(); +} - AccountOpResult CreateAccount(std::string username, std::string password, std::string email /*= ""*/) +AccountMgr* AccountMgr::instance() +{ + static AccountMgr instance; + return &instance; +} + +AccountOpResult AccountMgr::CreateAccount(std::string username, std::string password, std::string email /*= ""*/) +{ + if (utf8length(username) > MAX_ACCOUNT_STR) + return AOR_NAME_TOO_LONG; // username's too long + + if (utf8length(password) > MAX_PASS_STR) + return AOR_PASS_TOO_LONG; // password's too long + + if (utf8length(email) > MAX_EMAIL_STR) + return AOR_EMAIL_TOO_LONG; // email is too long + + Utf8ToUpperOnlyLatin(username); + Utf8ToUpperOnlyLatin(password); + Utf8ToUpperOnlyLatin(email); + + if (GetId(username)) + return AOR_NAME_ALREADY_EXIST; // username does already exist + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT); + + stmt->SetData(0, username); + auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, password); + stmt->SetData(1, salt); + stmt->SetData(2, verifier); + stmt->SetData(3, uint8(sWorld->getIntConfig(CONFIG_EXPANSION))); + stmt->SetData(4, email); + stmt->SetData(5, email); + + LoginDatabase.Execute(stmt); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS_INIT); + + LoginDatabase.Execute(stmt); + + return AOR_OK; // everything's fine +} + +AccountOpResult AccountMgr::ChangeEmail(uint32 accountId, std::string newEmail) +{ + std::string username; + + if (!GetName(accountId, username)) { - if (utf8length(username) > MAX_ACCOUNT_STR) - return AOR_NAME_TOO_LONG; // username's too long - - if (utf8length(password) > MAX_PASS_STR) - return AOR_PASS_TOO_LONG; // password's too long - - if (utf8length(email) > MAX_EMAIL_STR) - return AOR_EMAIL_TOO_LONG; // email is too long - - Utf8ToUpperOnlyLatin(username); - Utf8ToUpperOnlyLatin(password); - Utf8ToUpperOnlyLatin(email); - - if (GetId(username)) - return AOR_NAME_ALREADY_EXIST; // username does already exist - - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT); - - stmt->SetData(0, username); - auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, password); - stmt->SetData(1, salt); - stmt->SetData(2, verifier); - stmt->SetData(3, uint8(sWorld->getIntConfig(CONFIG_EXPANSION))); - stmt->SetData(4, email); - stmt->SetData(5, email); - - LoginDatabase.Execute(stmt); - - stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS_INIT); - - LoginDatabase.Execute(stmt); - - return AOR_OK; // everything's fine + sScriptMgr->OnFailedEmailChange(accountId); + return AOR_NAME_NOT_EXIST; // account doesn't exist } - AccountOpResult ChangeEmail(uint32 accountId, std::string newEmail) + if (utf8length(newEmail) > MAX_EMAIL_STR) { - std::string username; - - if (!GetName(accountId, username)) - { - sScriptMgr->OnFailedEmailChange(accountId); - return AOR_NAME_NOT_EXIST; // account doesn't exist - } - - if (utf8length(newEmail) > MAX_EMAIL_STR) - { - sScriptMgr->OnFailedEmailChange(accountId); - return AOR_EMAIL_TOO_LONG; // email's too long - } - - Utf8ToUpperOnlyLatin(newEmail); - - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EMAIL); - stmt->SetData(0, newEmail); - stmt->SetData(1, accountId); - LoginDatabase.Execute(stmt); - - sScriptMgr->OnEmailChange(accountId); - return AOR_OK; + sScriptMgr->OnFailedEmailChange(accountId); + return AOR_EMAIL_TOO_LONG; // email's too long } - AccountOpResult DeleteAccount(uint32 accountId) + Utf8ToUpperOnlyLatin(newEmail); + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_EMAIL); + stmt->SetData(0, newEmail); + stmt->SetData(1, accountId); + LoginDatabase.Execute(stmt); + + sScriptMgr->OnEmailChange(accountId); + return AOR_OK; +} + +AccountOpResult AccountMgr::DeleteAccount(uint32 accountId) +{ + // Check if accounts exists + LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); + loginStmt->SetData(0, accountId); + + PreparedQueryResult result = LoginDatabase.Query(loginStmt); + if (!result) + return AOR_NAME_NOT_EXIST; + + sScriptMgr->OnBeforeAccountDelete(accountId); + + // Obtain accounts characters + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID); + stmt->SetData(0, accountId); + + result = CharacterDatabase.Query(stmt); + + if (result) { - // Check if accounts exists - LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); - loginStmt->SetData(0, accountId); - - PreparedQueryResult result = LoginDatabase.Query(loginStmt); - if (!result) - return AOR_NAME_NOT_EXIST; - - sScriptMgr->OnBeforeAccountDelete(accountId); - - // Obtain accounts characters - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARS_BY_ACCOUNT_ID); - stmt->SetData(0, accountId); - - result = CharacterDatabase.Query(stmt); - - if (result) + do { - do + ObjectGuid guid = ObjectGuid::Create((*result)[0].Get()); + + // Kick if player is online + if (Player* p = ObjectAccessor::FindPlayer(guid)) { - ObjectGuid guid = ObjectGuid::Create((*result)[0].Get()); + WorldSession* s = p->GetSession(); + s->KickPlayer("Delete account"); // mark session to remove at next session list update + s->LogoutPlayer(false); // logout player without waiting next session list update + } - // Kick if player is online - if (Player* p = ObjectAccessor::FindPlayer(guid)) - { - WorldSession* s = p->GetSession(); - s->KickPlayer("Delete account"); // mark session to remove at next session list update - s->LogoutPlayer(false); // logout player without waiting next session list update - } - - Player::DeleteFromDB(guid.GetCounter(), accountId, false, true); // no need to update realm characters - } while (result->NextRow()); - } - - // table realm specific but common for all characters of account for realm - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_TUTORIALS); - stmt->SetData(0, accountId); - CharacterDatabase.Execute(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_DATA); - stmt->SetData(0, accountId); - CharacterDatabase.Execute(stmt); - - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_BAN); - stmt->SetData(0, accountId); - CharacterDatabase.Execute(stmt); - - LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); - - loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT); - loginStmt->SetData(0, accountId); - trans->Append(loginStmt); - - loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); - loginStmt->SetData(0, accountId); - trans->Append(loginStmt); - - loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS); - loginStmt->SetData(0, accountId); - trans->Append(loginStmt); - - loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_BANNED); - loginStmt->SetData(0, accountId); - trans->Append(loginStmt); - - loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_MUTED); - loginStmt->SetData(0, accountId); - trans->Append(loginStmt); - - LoginDatabase.CommitTransaction(trans); - - return AOR_OK; + Player::DeleteFromDB(guid.GetCounter(), accountId, false, true); // no need to update realm characters + } while (result->NextRow()); } - AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword) + // table realm specific but common for all characters of account for realm + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_TUTORIALS); + stmt->SetData(0, accountId); + CharacterDatabase.Execute(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ACCOUNT_DATA); + stmt->SetData(0, accountId); + CharacterDatabase.Execute(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_BAN); + stmt->SetData(0, accountId); + CharacterDatabase.Execute(stmt); + + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); + + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT); + loginStmt->SetData(0, accountId); + trans->Append(loginStmt); + + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); + loginStmt->SetData(0, accountId); + trans->Append(loginStmt); + + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS); + loginStmt->SetData(0, accountId); + trans->Append(loginStmt); + + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_BANNED); + loginStmt->SetData(0, accountId); + trans->Append(loginStmt); + + loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_MUTED); + loginStmt->SetData(0, accountId); + trans->Append(loginStmt); + + LoginDatabase.CommitTransaction(trans); + + return AOR_OK; +} + +AccountOpResult AccountMgr::ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword) +{ + // Check if accounts exists + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); + stmt->SetData(0, accountId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (!result) + return AOR_NAME_NOT_EXIST; + + if (utf8length(newUsername) > MAX_ACCOUNT_STR) + return AOR_NAME_TOO_LONG; + + if (utf8length(newPassword) > MAX_PASS_STR) + return AOR_PASS_TOO_LONG; // password's too long + + Utf8ToUpperOnlyLatin(newUsername); + Utf8ToUpperOnlyLatin(newPassword); + + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_USERNAME); + stmt->SetData(0, newUsername); + stmt->SetData(1, accountId); + LoginDatabase.Execute(stmt); + + auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(newUsername, newPassword); + stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); + stmt->SetData(0, salt); + stmt->SetData(1, verifier); + stmt->SetData(2, accountId); + LoginDatabase.Execute(stmt); + + return AOR_OK; +} + +AccountOpResult AccountMgr::ChangePassword(uint32 accountId, std::string newPassword) +{ + std::string username; + + if (!GetName(accountId, username)) { - // Check if accounts exists - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BY_ID); + sScriptMgr->OnFailedPasswordChange(accountId); + return AOR_NAME_NOT_EXIST; // account doesn't exist + } + + if (utf8length(newPassword) > MAX_PASS_STR) + { + sScriptMgr->OnFailedEmailChange(accountId); + return AOR_PASS_TOO_LONG; // password's too long + } + + Utf8ToUpperOnlyLatin(username); + Utf8ToUpperOnlyLatin(newPassword); + + auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, newPassword); + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); + stmt->SetData(0, salt); + stmt->SetData(1, verifier); + stmt->SetData(2, accountId); + LoginDatabase.Execute(stmt); + + sScriptMgr->OnPasswordChange(accountId); + return AOR_OK; +} + +uint32 AccountMgr::GetId(std::string const& username) +{ + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); + stmt->SetData(0, username); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + return (result) ? (*result)[0].Get() : 0; +} + +uint32 AccountMgr::GetSecurity(uint32 accountId) +{ + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL); + stmt->SetData(0, accountId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + return (result) ? (*result)[0].Get() : uint32(SEC_PLAYER); +} + +uint32 AccountMgr::GetSecurity(uint32 accountId, int32 realmId) +{ + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); + stmt->SetData(0, accountId); + stmt->SetData(1, realmId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + return (result) ? (*result)[0].Get() : uint32(SEC_PLAYER); +} + +bool AccountMgr::GetName(uint32 accountId, std::string& name) +{ + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID); + stmt->SetData(0, accountId); + PreparedQueryResult result = LoginDatabase.Query(stmt); + + if (result) + { + name = (*result)[0].Get(); + return true; + } + + return false; +} + +bool AccountMgr::CheckPassword(uint32 accountId, std::string password) +{ + std::string username; + + if (!GetName(accountId, username)) + return false; + + Utf8ToUpperOnlyLatin(username); + Utf8ToUpperOnlyLatin(password); + + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD); + stmt->SetData(0, accountId); + if (PreparedQueryResult result = LoginDatabase.Query(stmt)) + { + Acore::Crypto::SRP6::Salt salt = (*result)[0].Get(); + Acore::Crypto::SRP6::Verifier verifier = (*result)[1].Get(); + if (Acore::Crypto::SRP6::CheckLogin(username, password, salt, verifier)) + return true; + } + + return false; +} + +uint32 AccountMgr::GetCharactersCount(uint32 accountId) +{ + // check character count + CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); + stmt->SetData(0, accountId); + PreparedQueryResult result = CharacterDatabase.Query(stmt); + + return (result) ? (*result)[0].Get() : 0; +} + +bool AccountMgr::IsPlayerAccount(uint32 gmlevel) +{ + return gmlevel == SEC_PLAYER; +} + +bool AccountMgr::IsGMAccount(uint32 gmlevel) +{ + return gmlevel >= SEC_GAMEMASTER; +} + +bool AccountMgr::IsAdminAccount(uint32 gmlevel) +{ + return gmlevel >= SEC_ADMINISTRATOR && gmlevel <= SEC_CONSOLE; +} + +bool AccountMgr::IsConsoleAccount(uint32 gmlevel) +{ + return gmlevel == SEC_CONSOLE; +} + +bool AccountMgr::HasPermission(uint32 accountId, uint32 permission, uint32 realmId) +{ + if (!accountId) + { + LOG_ERROR("rbac", "AccountMgr::HasPermission: Wrong accountId 0"); + return false; + } + + rbac::RBACData rbac(accountId, "", realmId, GetSecurity(accountId, realmId)); + rbac.LoadFromDB(); + bool hasPermission = rbac.HasPermission(permission); + + LOG_DEBUG("rbac", "AccountMgr::HasPermission [AccountId: {}, PermissionId: {}, realmId: {}]: {}", + accountId, permission, realmId, hasPermission); + + return hasPermission; +} + +void AccountMgr::UpdateAccountAccess(rbac::RBACData* rbac, uint32 accountId, uint8 securityLevel, int32 realmId) +{ + if (rbac && securityLevel != rbac->GetSecurityLevel()) + rbac->SetSecurityLevel(securityLevel); + + LoginDatabaseTransaction trans = LoginDatabase.BeginTransaction(); + + // Delete old security level from DB + if (realmId == -1) + { + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); stmt->SetData(0, accountId); - PreparedQueryResult result = LoginDatabase.Query(stmt); - - if (!result) - return AOR_NAME_NOT_EXIST; - - if (utf8length(newUsername) > MAX_ACCOUNT_STR) - return AOR_NAME_TOO_LONG; - - if (utf8length(newPassword) > MAX_PASS_STR) - return AOR_PASS_TOO_LONG; // password's too long - - Utf8ToUpperOnlyLatin(newUsername); - Utf8ToUpperOnlyLatin(newPassword); - - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_USERNAME); - stmt->SetData(0, newUsername); - stmt->SetData(1, accountId); - LoginDatabase.Execute(stmt); - - auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(newUsername, newPassword); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); - stmt->SetData(0, salt); - stmt->SetData(1, verifier); - stmt->SetData(2, accountId); - LoginDatabase.Execute(stmt); - - return AOR_OK; + trans->Append(stmt); } - - AccountOpResult ChangePassword(uint32 accountId, std::string newPassword) + else { - std::string username; - - if (!GetName(accountId, username)) - { - sScriptMgr->OnFailedPasswordChange(accountId); - return AOR_NAME_NOT_EXIST; // account doesn't exist - } - - if (utf8length(newPassword) > MAX_PASS_STR) - { - sScriptMgr->OnFailedEmailChange(accountId); - return AOR_PASS_TOO_LONG; // password's too long - } - - Utf8ToUpperOnlyLatin(username); - Utf8ToUpperOnlyLatin(newPassword); - - auto [salt, verifier] = Acore::Crypto::SRP6::MakeRegistrationData(username, newPassword); - - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGON); - stmt->SetData(0, salt); - stmt->SetData(1, verifier); - stmt->SetData(2, accountId); - LoginDatabase.Execute(stmt); - - sScriptMgr->OnPasswordChange(accountId); - return AOR_OK; - } - - uint32 GetId(std::string const& username) - { - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ID_BY_USERNAME); - stmt->SetData(0, username); - PreparedQueryResult result = LoginDatabase.Query(stmt); - - return (result) ? (*result)[0].Get() : 0; - } - - uint32 GetSecurity(uint32 accountId) - { - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_ACCOUNT_ACCESS_GMLEVEL); - stmt->SetData(0, accountId); - PreparedQueryResult result = LoginDatabase.Query(stmt); - - return (result) ? (*result)[0].Get() : uint32(SEC_PLAYER); - } - - uint32 GetSecurity(uint32 accountId, int32 realmId) - { - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_GMLEVEL_BY_REALMID); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM); stmt->SetData(0, accountId); stmt->SetData(1, realmId); - PreparedQueryResult result = LoginDatabase.Query(stmt); - - return (result) ? (*result)[0].Get() : uint32(SEC_PLAYER); + trans->Append(stmt); } - bool GetName(uint32 accountId, std::string& name) + // Add new security level + if (securityLevel) { - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_USERNAME_BY_ID); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_ACCESS); stmt->SetData(0, accountId); - PreparedQueryResult result = LoginDatabase.Query(stmt); + stmt->SetData(1, securityLevel); + stmt->SetData(2, realmId); + trans->Append(stmt); + } - if (result) + LoginDatabase.CommitTransaction(trans); +} + +void AccountMgr::LoadRBAC() +{ + ClearRBAC(); + + LOG_DEBUG("rbac", "AccountMgr::LoadRBAC"); + uint32 oldMSTime = getMSTime(); + uint32 count1 = 0; + uint32 count2 = 0; + uint32 count3 = 0; + + LOG_DEBUG("rbac", "AccountMgr::LoadRBAC: Loading permissions"); + QueryResult result = LoginDatabase.Query("SELECT id, name FROM rbac_permissions"); + if (!result) + { + LOG_INFO("server.loading", ">> Loaded 0 RBAC permissions. DB table `rbac_permissions` is empty."); + LOG_INFO("server.loading", " "); + return; + } + + do + { + Field* field = result->Fetch(); + uint32 id = field[0].Get(); + _permissions[id] = new rbac::RBACPermission(id, field[1].Get()); + ++count1; + } + while (result->NextRow()); + + LOG_DEBUG("rbac", "AccountMgr::LoadRBAC: Loading linked permissions"); + result = LoginDatabase.Query("SELECT id, linkedId FROM rbac_linked_permissions ORDER BY id ASC"); + if (!result) + { + LOG_INFO("server.loading", ">> Loaded {} RBAC permissions", count1); + LOG_INFO("server.loading", " "); + } + else + { + uint32 permissionId = 0; + rbac::RBACPermission* permission = nullptr; + + do { - name = (*result)[0].Get(); - return true; + Field* field = result->Fetch(); + uint32 newId = field[0].Get(); + if (permissionId != newId) + { + permissionId = newId; + permission = _permissions[newId]; + } + + uint32 linkedPermissionId = field[1].Get(); + if (linkedPermissionId == permissionId) + { + LOG_ERROR("sql.sql", "RBAC Permission {} has itself as linked permission. Ignored", permissionId); + continue; + } + permission->AddLinkedPermission(linkedPermissionId); + ++count2; + } + while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} RBAC permissions and {} linked permissions in {} ms", count1, count2, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", " "); + } + + LOG_DEBUG("rbac", "AccountMgr::LoadRBAC: Loading default permissions"); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RBAC_DEFAULT_PERMISSIONS); + stmt->SetData(0, int32(realm.Id.Realm)); + PreparedQueryResult defaultPermResult = LoginDatabase.Query(stmt); + if (!defaultPermResult) + { + LOG_INFO("server.loading", ">> Loaded 0 RBAC default permissions. DB table `rbac_default_permissions` is empty."); + LOG_INFO("server.loading", " "); + return; + } + + uint8 secId = 255; + rbac::RBACPermissionContainer* permissions = nullptr; + do + { + Field* field = defaultPermResult->Fetch(); + uint8 newId = field[0].Get(); + if (secId != newId || permissions == nullptr) + { + secId = newId; + permissions = &_defaultPermissions[secId]; } - return false; + permissions->insert(field[1].Get()); + ++count3; } + while (defaultPermResult->NextRow()); - bool CheckPassword(uint32 accountId, std::string password) - { - std::string username; + LOG_INFO("server.loading", ">> Loaded {} RBAC default permissions in {} ms", count3, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", " "); +} - if (!GetName(accountId, username)) - return false; +rbac::RBACPermission const* AccountMgr::GetRBACPermission(uint32 permissionId) const +{ + LOG_TRACE("rbac", "AccountMgr::GetRBACPermission: {}", permissionId); + rbac::RBACPermissionsContainer::const_iterator it = _permissions.find(permissionId); + if (it != _permissions.end()) + return it->second; - Utf8ToUpperOnlyLatin(username); - Utf8ToUpperOnlyLatin(password); + return nullptr; +} - LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_CHECK_PASSWORD); - stmt->SetData(0, accountId); - if (PreparedQueryResult result = LoginDatabase.Query(stmt)) - { - Acore::Crypto::SRP6::Salt salt = (*result)[0].Get(); - Acore::Crypto::SRP6::Verifier verifier = (*result)[1].Get(); - if (Acore::Crypto::SRP6::CheckLogin(username, password, salt, verifier)) - return true; - } +rbac::RBACPermissionContainer const& AccountMgr::GetRBACDefaultPermissions(uint8 secLevel) +{ + LOG_TRACE("rbac", "AccountMgr::GetRBACDefaultPermissions: secLevel {} - size: {}", secLevel, uint32(_defaultPermissions[secLevel].size())); + return _defaultPermissions[secLevel]; +} - return false; - } +void AccountMgr::ClearRBAC() +{ + for (std::pair& permission : _permissions) + delete permission.second; - uint32 GetCharactersCount(uint32 accountId) - { - // check character count - CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_SUM_CHARS); - stmt->SetData(0, accountId); - PreparedQueryResult result = CharacterDatabase.Query(stmt); + _permissions.clear(); + _defaultPermissions.clear(); +} - return (result) ? (*result)[0].Get() : 0; - } +void AccountMgr::AddPermissionForTest(uint32 permissionId, std::string const& name) +{ + if (_permissions.find(permissionId) != _permissions.end()) + return; - bool IsPlayerAccount(uint32 gmlevel) - { - return gmlevel == SEC_PLAYER; - } + _permissions[permissionId] = new rbac::RBACPermission(permissionId, name); +} - bool IsGMAccount(uint32 gmlevel) - { - return gmlevel >= SEC_GAMEMASTER; - } +void AccountMgr::AddLinkedPermissionForTest(uint32 permissionId, uint32 linkedPermissionId) +{ + auto it = _permissions.find(permissionId); + if (it != _permissions.end()) + it->second->AddLinkedPermission(linkedPermissionId); +} - bool IsAdminAccount(uint32 gmlevel) - { - return gmlevel >= SEC_ADMINISTRATOR && gmlevel <= SEC_CONSOLE; - } +void AccountMgr::AddDefaultPermissionForTest(uint8 secLevel, uint32 permissionId) +{ + _defaultPermissions[secLevel].insert(permissionId); +} - bool IsConsoleAccount(uint32 gmlevel) - { - return gmlevel == SEC_CONSOLE; - } - -} // Namespace AccountMgr +void AccountMgr::ClearPermissionsForTest() +{ + ClearRBAC(); +} diff --git a/src/server/game/Accounts/AccountMgr.h b/src/server/game/Accounts/AccountMgr.h index c6fbbea31..d0d7ccfc9 100644 --- a/src/server/game/Accounts/AccountMgr.h +++ b/src/server/game/Accounts/AccountMgr.h @@ -1,10 +1,10 @@ /* * 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 General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 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 @@ -18,9 +18,7 @@ #ifndef _ACCMGR_H #define _ACCMGR_H -#include "Define.h" -#include "Common.h" -#include +#include "RBAC.h" enum AccountOpResult { @@ -37,25 +35,61 @@ enum AccountOpResult #define MAX_PASS_STR 16 #define MAX_EMAIL_STR 255 -namespace AccountMgr +namespace rbac { +typedef std::map RBACPermissionsContainer; +typedef std::map RBACDefaultPermissionsContainer; +} + +class AC_GAME_API AccountMgr +{ +private: + AccountMgr(); + ~AccountMgr(); + +public: + static AccountMgr* instance(); + AccountOpResult CreateAccount(std::string username, std::string password, std::string email = ""); - AccountOpResult DeleteAccount(uint32 accountId); - AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword); - AccountOpResult ChangePassword(uint32 accountId, std::string newPassword); - AccountOpResult ChangeEmail(uint32 accountId, std::string email); - bool CheckPassword(uint32 accountId, std::string password); + static AccountOpResult DeleteAccount(uint32 accountId); + static AccountOpResult ChangeUsername(uint32 accountId, std::string newUsername, std::string newPassword); + static AccountOpResult ChangePassword(uint32 accountId, std::string newPassword); + static AccountOpResult ChangeEmail(uint32 accountId, std::string email); + static bool CheckPassword(uint32 accountId, std::string password); - uint32 GetId(std::string const& username); - uint32 GetSecurity(uint32 accountId); - uint32 GetSecurity(uint32 accountId, int32 realmId); - bool GetName(uint32 accountId, std::string& name); - uint32 GetCharactersCount(uint32 accountId); + static uint32 GetId(std::string const& username); + static uint32 GetSecurity(uint32 accountId); + static uint32 GetSecurity(uint32 accountId, int32 realmId); + static bool GetName(uint32 accountId, std::string& name); + static uint32 GetCharactersCount(uint32 accountId); - bool IsPlayerAccount(uint32 gmlevel); - bool IsGMAccount(uint32 gmlevel); - bool IsAdminAccount(uint32 gmlevel); - bool IsConsoleAccount(uint32 gmlevel); + static bool IsPlayerAccount(uint32 gmlevel); + static bool IsGMAccount(uint32 gmlevel); + static bool IsAdminAccount(uint32 gmlevel); + static bool IsConsoleAccount(uint32 gmlevel); + + static bool HasPermission(uint32 accountId, uint32 permission, uint32 realmId); + + void UpdateAccountAccess(rbac::RBACData* rbac, uint32 accountId, uint8 securityLevel, int32 realmId); + + void LoadRBAC(); + rbac::RBACPermission const* GetRBACPermission(uint32 permission) const; + + rbac::RBACPermissionsContainer const& GetRBACPermissionList() const { return _permissions; } + rbac::RBACPermissionContainer const& GetRBACDefaultPermissions(uint8 secLevel); + + // For unit testing - allows adding permissions without database + void AddPermissionForTest(uint32 permissionId, std::string const& name); + void AddLinkedPermissionForTest(uint32 permissionId, uint32 linkedPermissionId); + void AddDefaultPermissionForTest(uint8 secLevel, uint32 permissionId); + void ClearPermissionsForTest(); + +private: + void ClearRBAC(); + rbac::RBACPermissionsContainer _permissions; + rbac::RBACDefaultPermissionsContainer _defaultPermissions; }; +#define sAccountMgr AccountMgr::instance() + #endif diff --git a/src/server/game/Accounts/RBAC.cpp b/src/server/game/Accounts/RBAC.cpp new file mode 100644 index 000000000..99f80ef32 --- /dev/null +++ b/src/server/game/Accounts/RBAC.cpp @@ -0,0 +1,283 @@ +/* + * 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 General Public License as published by the + * Free Software Foundation; either version 2 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 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 "RBAC.h" +#include "AccountMgr.h" +#include "DatabaseEnv.h" +#include "Log.h" +#include "QueryResult.h" +#include + +namespace rbac +{ + +std::string GetDebugPermissionString(RBACPermissionContainer const& perms) +{ + std::string str = ""; + if (!perms.empty()) + { + std::ostringstream o; + RBACPermissionContainer::const_iterator itr = perms.begin(); + o << (*itr); + for (++itr; itr != perms.end(); ++itr) + o << ", " << uint32(*itr); + str = o.str(); + } + + return str; +} + +RBACCommandResult RBACData::GrantPermission(uint32 permissionId, int32 realmId /* = 0*/) +{ + // Check if permission Id exists + RBACPermission const* perm = sAccountMgr->GetRBACPermission(permissionId); + if (!perm) + { + LOG_TRACE("rbac", "RBACData::GrantPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Permission does not exists", + GetId(), GetName(), permissionId, realmId); + return RBAC_ID_DOES_NOT_EXISTS; + } + + // Check if already added in denied list + if (HasDeniedPermission(permissionId)) + { + LOG_TRACE("rbac", "RBACData::GrantPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Permission in deny list", + GetId(), GetName(), permissionId, realmId); + return RBAC_IN_DENIED_LIST; + } + + // Already added? + if (HasGrantedPermission(permissionId)) + { + LOG_TRACE("rbac", "RBACData::GrantPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Permission already granted", + GetId(), GetName(), permissionId, realmId); + return RBAC_CANT_ADD_ALREADY_ADDED; + } + + AddGrantedPermission(permissionId); + + // Do not save to db when loading data from DB (realmId = 0) + if (realmId) + { + LOG_TRACE("rbac", "RBACData::GrantPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Ok and DB updated", + GetId(), GetName(), permissionId, realmId); + SavePermission(permissionId, true, realmId); + CalculateNewPermissions(); + } + else + LOG_TRACE("rbac", "RBACData::GrantPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Ok", + GetId(), GetName(), permissionId, realmId); + + return RBAC_OK; +} + +RBACCommandResult RBACData::DenyPermission(uint32 permissionId, int32 realmId /* = 0*/) +{ + // Check if permission Id exists + RBACPermission const* perm = sAccountMgr->GetRBACPermission(permissionId); + if (!perm) + { + LOG_TRACE("rbac", "RBACData::DenyPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Permission does not exists", + GetId(), GetName(), permissionId, realmId); + return RBAC_ID_DOES_NOT_EXISTS; + } + + // Check if already added in granted list + if (HasGrantedPermission(permissionId)) + { + LOG_TRACE("rbac", "RBACData::DenyPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Permission in grant list", + GetId(), GetName(), permissionId, realmId); + return RBAC_IN_GRANTED_LIST; + } + + // Already added? + if (HasDeniedPermission(permissionId)) + { + LOG_TRACE("rbac", "RBACData::DenyPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Permission already denied", + GetId(), GetName(), permissionId, realmId); + return RBAC_CANT_ADD_ALREADY_ADDED; + } + + AddDeniedPermission(permissionId); + + // Do not save to db when loading data from DB (realmId = 0) + if (realmId) + { + LOG_TRACE("rbac", "RBACData::DenyPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Ok and DB updated", + GetId(), GetName(), permissionId, realmId); + SavePermission(permissionId, false, realmId); + CalculateNewPermissions(); + } + else + LOG_TRACE("rbac", "RBACData::DenyPermission [Id: {} Name: {}] (Permission {}, RealmId {}). Ok", + GetId(), GetName(), permissionId, realmId); + + return RBAC_OK; +} + +void RBACData::SavePermission(uint32 permission, bool granted, int32 realmId) +{ + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_RBAC_ACCOUNT_PERMISSION); + stmt->SetData(0, GetId()); + stmt->SetData(1, permission); + stmt->SetData(2, granted); + stmt->SetData(3, realmId); + LoginDatabase.Execute(stmt); +} + +RBACCommandResult RBACData::RevokePermission(uint32 permissionId, int32 realmId /* = 0*/) +{ + // Check if it's present in any list + if (!HasGrantedPermission(permissionId) && !HasDeniedPermission(permissionId)) + { + LOG_TRACE("rbac", "RBACData::RevokePermission [Id: {} Name: {}] (Permission {}, RealmId {}). Not granted or revoked", + GetId(), GetName(), permissionId, realmId); + return RBAC_CANT_REVOKE_NOT_IN_LIST; + } + + RemoveGrantedPermission(permissionId); + RemoveDeniedPermission(permissionId); + + // Do not save to db when loading data from DB (realmId = 0) + if (realmId) + { + LOG_TRACE("rbac", "RBACData::RevokePermission [Id: {} Name: {}] (Permission {}, RealmId {}). Ok and DB updated", + GetId(), GetName(), permissionId, realmId); + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_RBAC_ACCOUNT_PERMISSION); + stmt->SetData(0, GetId()); + stmt->SetData(1, permissionId); + stmt->SetData(2, realmId); + LoginDatabase.Execute(stmt); + + CalculateNewPermissions(); + } + else + LOG_TRACE("rbac", "RBACData::RevokePermission [Id: {} Name: {}] (Permission {}, RealmId {}). Ok", + GetId(), GetName(), permissionId, realmId); + + return RBAC_OK; +} + +void RBACData::LoadFromDB() +{ + ClearData(); + + LOG_DEBUG("rbac", "RBACData::LoadFromDB [Id: {} Name: {}]: Loading permissions", GetId(), GetName()); + // Load account permissions (granted and denied) that affect current realm + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS); + stmt->SetData(0, GetId()); + stmt->SetData(1, GetRealmId()); + + LoadFromDBCallback(LoginDatabase.Query(stmt)); +} + +QueryCallback RBACData::LoadFromDBAsync() +{ + ClearData(); + + LOG_DEBUG("rbac", "RBACData::LoadFromDB [Id: {} Name: {}]: Loading permissions", GetId(), GetName()); + // Load account permissions (granted and denied) that affect current realm + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RBAC_ACCOUNT_PERMISSIONS); + stmt->SetData(0, GetId()); + stmt->SetData(1, GetRealmId()); + + return LoginDatabase.AsyncQuery(stmt); +} + +void RBACData::LoadFromDBCallback(PreparedQueryResult result) +{ + if (result) + { + do + { + Field* fields = result->Fetch(); + if (fields[1].Get()) + GrantPermission(fields[0].Get()); + else + DenyPermission(fields[0].Get()); + } while (result->NextRow()); + } + + // Add default permissions + RBACPermissionContainer const& permissions = sAccountMgr->GetRBACDefaultPermissions(_secLevel); + for (uint32 permission : permissions) + GrantPermission(permission); + + // Force calculation of permissions + CalculateNewPermissions(); +} + +void RBACData::CalculateNewPermissions() +{ + LOG_TRACE("rbac", "RBACData::CalculateNewPermissions [Id: {} Name: {}]", GetId(), GetName()); + + // Get the list of granted permissions + _globalPerms = GetGrantedPermissions(); + ExpandPermissions(_globalPerms); + RBACPermissionContainer revoked = GetDeniedPermissions(); + ExpandPermissions(revoked); + RemovePermissions(_globalPerms, revoked); +} + +void RBACData::AddPermissions(RBACPermissionContainer const& permsFrom, RBACPermissionContainer& permsTo) +{ + for (uint32 permission : permsFrom) + permsTo.insert(permission); +} + +void RBACData::RemovePermissions(RBACPermissionContainer& permsFrom, RBACPermissionContainer const& permsToRemove) +{ + for (uint32 permission: permsToRemove) + permsFrom.erase(permission); +} + +void RBACData::ExpandPermissions(RBACPermissionContainer& permissions) +{ + RBACPermissionContainer toCheck = permissions; + permissions.clear(); + + while (!toCheck.empty()) + { + // remove the permission from original list + uint32 permissionId = *toCheck.begin(); + toCheck.erase(toCheck.begin()); + + RBACPermission const* permission = sAccountMgr->GetRBACPermission(permissionId); + if (!permission) + continue; + + // insert into the final list (expanded list) + permissions.insert(permissionId); + + // add all linked permissions (that are not already expanded) to the list of permissions to be checked + RBACPermissionContainer const& linkedPerms = permission->GetLinkedPermissions(); + for (uint32 linkedPerm : linkedPerms) + if (permissions.find(linkedPerm) == permissions.end()) + toCheck.insert(linkedPerm); + } + + LOG_DEBUG("rbac", "RBACData::ExpandPermissions: Expanded: {}", GetDebugPermissionString(permissions)); +} + +void RBACData::ClearData() +{ + _grantedPerms.clear(); + _deniedPerms.clear(); + _globalPerms.clear(); +} + +} diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h new file mode 100644 index 000000000..c4af7947e --- /dev/null +++ b/src/server/game/Accounts/RBAC.h @@ -0,0 +1,895 @@ +/* + * 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 General Public License as published by the + * Free Software Foundation; either version 2 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 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 . + */ + +/** +* @file RBAC.h +* @brief Role Based Access Control related classes definition +* +* This file contains all the classes and enums used to implement +* Role Based Access Control +* +* RBAC Rules: +* - Permission: Defines an authorization to perform certain operation. +* - Role: Set of permissions. +* - An Account can have multiple roles and permissions. +* - Account Roles and Permissions can be granted, denied or revoked +* - Grant: Assignment of the object (role/permission) and allow it +* - Deny: Assignment of the object (role/permission) and deny it +* - Revoke: Removal of the object (role/permission) no matter if it was granted or denied +* - Global Permissions are computed as: +* Role Grants + User Grants - Role Denies - User Denies +* - Roles and Permissions can be assigned by realm +*/ + +#ifndef _RBAC_H +#define _RBAC_H + +#include "Define.h" +#include "DatabaseEnvFwd.h" +#include +#include +#include + +namespace rbac +{ + +enum RBACPermissions +{ + RBAC_PERM_INSTANT_LOGOUT = 1, + RBAC_PERM_SKIP_QUEUE = 2, + RBAC_PERM_JOIN_NORMAL_BG = 3, + RBAC_PERM_JOIN_RANDOM_BG = 4, + RBAC_PERM_JOIN_ARENAS = 5, + RBAC_PERM_JOIN_DUNGEON_FINDER = 6, + RBAC_PERM_IGNORE_IDLE_CONNECTION = 7, + RBAC_PERM_CANNOT_EARN_ACHIEVEMENTS = 8, + RBAC_PERM_CANNOT_EARN_REALM_FIRST_ACHIEVEMENTS = 9, + // 10 not on 3.3.5a (character templates) + RBAC_PERM_LOG_GM_TRADE = 11, + // 12 not on 3.3.5a (demon hunter) + RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES = 13, + RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_TEAMMASK = 14, + RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_CLASSMASK = 15, + RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RACEMASK = 16, + RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME = 17, + RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_DEATH_KNIGHT = 18, + RBAC_PERM_SKIP_CHECK_CHAT_CHANNEL_REQ = 19, + RBAC_PERM_SKIP_CHECK_DISABLE_MAP = 20, + RBAC_PERM_SKIP_CHECK_MORE_TALENTS_THAN_ALLOWED = 21, + RBAC_PERM_SKIP_CHECK_CHAT_SPAM = 22, + RBAC_PERM_SKIP_CHECK_OVERSPEED_PING = 23, + RBAC_PERM_TWO_SIDE_CHARACTER_CREATION = 24, + RBAC_PERM_TWO_SIDE_INTERACTION_CHAT = 25, + RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL = 26, + RBAC_PERM_TWO_SIDE_INTERACTION_MAIL = 27, + RBAC_PERM_TWO_SIDE_WHO_LIST = 28, + RBAC_PERM_TWO_SIDE_ADD_FRIEND = 29, + RBAC_PERM_COMMANDS_SAVE_WITHOUT_DELAY = 30, + RBAC_PERM_COMMANDS_USE_UNSTUCK_WITH_ARGS = 31, + RBAC_PERM_COMMANDS_BE_ASSIGNED_TICKET = 32, + RBAC_PERM_COMMANDS_NOTIFY_COMMAND_NOT_FOUND_ERROR = 33, + RBAC_PERM_COMMANDS_APPEAR_IN_GM_LIST = 34, + RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS = 35, + RBAC_PERM_CAN_FILTER_WHISPERS = 36, + RBAC_PERM_CHAT_USE_STAFF_BADGE = 37, + RBAC_PERM_RESURRECT_WITH_FULL_HPS = 38, + RBAC_PERM_RESTORE_SAVED_GM_STATE = 39, + RBAC_PERM_ALLOW_GM_FRIEND = 40, + RBAC_PERM_USE_START_GM_LEVEL = 41, + RBAC_PERM_OPCODE_WORLD_TELEPORT = 42, + RBAC_PERM_OPCODE_WHOIS = 43, + RBAC_PERM_RECEIVE_GLOBAL_GM_TEXTMESSAGE = 44, + RBAC_PERM_SILENTLY_JOIN_CHANNEL = 45, + RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR = 46, + RBAC_PERM_CAN_IGNORE_LOWER_SECURITY_CHECK = 47, + RBAC_PERM_COMMANDS_PINFO_CHECK_PERSONAL_DATA = 48, + RBAC_PERM_EMAIL_CONFIRM_FOR_PASS_CHANGE = 49, + RBAC_PERM_MAY_CHECK_OWN_EMAIL = 50, + RBAC_PERM_ALLOW_TWO_SIDE_TRADE = 51, + RBAC_PERM_NO_BATTLEGROUND_DESERTER_DEBUFF = 52, + RBAC_PERM_CAN_AFK_ON_BATTLEGROUND = 53, + + // Roles (Permissions with delegated permissions) use 199 and descending + RBAC_ROLE_ADMINISTRATOR = 196, + RBAC_ROLE_GAMEMASTER = 197, + RBAC_ROLE_MODERATOR = 198, + RBAC_ROLE_PLAYER = 199, + + // Command permissions start at 200 + RBAC_PERM_COMMAND_RBAC = 200, + RBAC_PERM_COMMAND_RBAC_ACC = 201, + RBAC_PERM_COMMAND_RBAC_ACC_PERM_LIST = 202, + RBAC_PERM_COMMAND_RBAC_ACC_PERM_GRANT = 203, + RBAC_PERM_COMMAND_RBAC_ACC_PERM_DENY = 204, + RBAC_PERM_COMMAND_RBAC_ACC_PERM_REVOKE = 205, + RBAC_PERM_COMMAND_RBAC_LIST = 206, + // 207-216 not on 3.3.5a (bnet) + RBAC_PERM_COMMAND_ACCOUNT = 217, + RBAC_PERM_COMMAND_ACCOUNT_ADDON = 218, + RBAC_PERM_COMMAND_ACCOUNT_CREATE = 219, + RBAC_PERM_COMMAND_ACCOUNT_DELETE = 220, + RBAC_PERM_COMMAND_ACCOUNT_LOCK = 221, + RBAC_PERM_COMMAND_ACCOUNT_LOCK_COUNTRY = 222, + RBAC_PERM_COMMAND_ACCOUNT_LOCK_IP = 223, + RBAC_PERM_COMMAND_ACCOUNT_ONLINE_LIST = 224, + RBAC_PERM_COMMAND_ACCOUNT_PASSWORD = 225, + RBAC_PERM_COMMAND_ACCOUNT_SET = 226, + RBAC_PERM_COMMAND_ACCOUNT_SET_ADDON = 227, + RBAC_PERM_COMMAND_ACCOUNT_SET_SECLEVEL = 228, + RBAC_PERM_COMMAND_ACCOUNT_SET_PASSWORD = 229, + RBAC_PERM_COMMAND_ACHIEVEMENT_ADD = 231, + RBAC_PERM_COMMAND_ACHIEVEMENT_CHECKALL = 232, + RBAC_PERM_COMMAND_ARENA_CAPTAIN = 233, + RBAC_PERM_COMMAND_ARENA_CREATE = 234, + RBAC_PERM_COMMAND_ARENA_DISBAND = 235, + RBAC_PERM_COMMAND_ARENA_INFO = 236, + RBAC_PERM_COMMAND_ARENA_LOOKUP = 237, + RBAC_PERM_COMMAND_ARENA_RENAME = 238, + RBAC_PERM_COMMAND_BAN_ACCOUNT = 240, + RBAC_PERM_COMMAND_BAN_CHARACTER = 241, + RBAC_PERM_COMMAND_BAN_IP = 242, + RBAC_PERM_COMMAND_BAN_PLAYERACCOUNT = 243, + RBAC_PERM_COMMAND_BANINFO_ACCOUNT = 245, + RBAC_PERM_COMMAND_BANINFO_CHARACTER = 246, + RBAC_PERM_COMMAND_BANINFO_IP = 247, + RBAC_PERM_COMMAND_BANLIST_ACCOUNT = 249, + RBAC_PERM_COMMAND_BANLIST_CHARACTER = 250, + RBAC_PERM_COMMAND_BANLIST_IP = 251, + RBAC_PERM_COMMAND_UNBAN_ACCOUNT = 253, + RBAC_PERM_COMMAND_UNBAN_CHARACTER = 254, + RBAC_PERM_COMMAND_UNBAN_IP = 255, + RBAC_PERM_COMMAND_UNBAN_PLAYERACCOUNT = 256, + RBAC_PERM_COMMAND_BF_START = 258, + RBAC_PERM_COMMAND_BF_STOP = 259, + RBAC_PERM_COMMAND_BF_SWITCH = 260, + RBAC_PERM_COMMAND_BF_TIMER = 261, + RBAC_PERM_COMMAND_BF_ENABLE = 262, + RBAC_PERM_COMMAND_ACCOUNT_EMAIL = 263, + RBAC_PERM_COMMAND_ACCOUNT_SET_SEC_EMAIL = 265, + RBAC_PERM_COMMAND_ACCOUNT_SET_SEC_REGMAIL = 266, + RBAC_PERM_COMMAND_CAST = 267, + RBAC_PERM_COMMAND_CAST_BACK = 268, + RBAC_PERM_COMMAND_CAST_DIST = 269, + RBAC_PERM_COMMAND_CAST_SELF = 270, + RBAC_PERM_COMMAND_CAST_TARGET = 271, + RBAC_PERM_COMMAND_CAST_DEST = 272, + RBAC_PERM_COMMAND_CHARACTER_CUSTOMIZE = 274, + RBAC_PERM_COMMAND_CHARACTER_CHANGEFACTION = 275, + RBAC_PERM_COMMAND_CHARACTER_CHANGERACE = 276, + RBAC_PERM_COMMAND_CHARACTER_DELETED_DELETE = 278, + RBAC_PERM_COMMAND_CHARACTER_DELETED_LIST = 279, + RBAC_PERM_COMMAND_CHARACTER_DELETED_RESTORE = 280, + RBAC_PERM_COMMAND_CHARACTER_DELETED_OLD = 281, + RBAC_PERM_COMMAND_CHARACTER_ERASE = 282, + RBAC_PERM_COMMAND_CHARACTER_LEVEL = 283, + RBAC_PERM_COMMAND_CHARACTER_RENAME = 284, + RBAC_PERM_COMMAND_CHARACTER_REPUTATION = 285, + RBAC_PERM_COMMAND_CHARACTER_TITLES = 286, + RBAC_PERM_COMMAND_LEVELUP = 287, + RBAC_PERM_COMMAND_PDUMP_LOAD = 289, + RBAC_PERM_COMMAND_PDUMP_WRITE = 290, + RBAC_PERM_COMMAND_CHEAT_CASTTIME = 292, + RBAC_PERM_COMMAND_CHEAT_COOLDOWN = 293, + RBAC_PERM_COMMAND_CHEAT_EXPLORE = 294, + RBAC_PERM_COMMAND_CHEAT_GOD = 295, + RBAC_PERM_COMMAND_CHEAT_POWER = 296, + RBAC_PERM_COMMAND_CHEAT_STATUS = 297, + RBAC_PERM_COMMAND_CHEAT_TAXI = 298, + RBAC_PERM_COMMAND_CHEAT_WATERWALK = 299, + RBAC_PERM_COMMAND_DEBUG = 300, + // 301-342 previously used, do not reuse + RBAC_PERM_COMMAND_DESERTER_BG_ADD = 343, + RBAC_PERM_COMMAND_DESERTER_BG_REMOVE = 344, + RBAC_PERM_COMMAND_DESERTER_INSTANCE_ADD = 346, + RBAC_PERM_COMMAND_DESERTER_INSTANCE_REMOVE = 347, + RBAC_PERM_COMMAND_DISABLE_ADD_ACHIEVEMENT_CRITERIA = 350, + RBAC_PERM_COMMAND_DISABLE_ADD_BATTLEGROUND = 351, + RBAC_PERM_COMMAND_DISABLE_ADD_MAP = 352, + RBAC_PERM_COMMAND_DISABLE_ADD_MMAP = 353, + RBAC_PERM_COMMAND_DISABLE_ADD_OUTDOORPVP = 354, + RBAC_PERM_COMMAND_DISABLE_ADD_QUEST = 355, + RBAC_PERM_COMMAND_DISABLE_ADD_SPELL = 356, + RBAC_PERM_COMMAND_DISABLE_ADD_VMAP = 357, + RBAC_PERM_COMMAND_DISABLE_REMOVE_ACHIEVEMENT_CRITERIA = 359, + RBAC_PERM_COMMAND_DISABLE_REMOVE_BATTLEGROUND = 360, + RBAC_PERM_COMMAND_DISABLE_REMOVE_MAP = 361, + RBAC_PERM_COMMAND_DISABLE_REMOVE_MMAP = 362, + RBAC_PERM_COMMAND_DISABLE_REMOVE_OUTDOORPVP = 363, + RBAC_PERM_COMMAND_DISABLE_REMOVE_QUEST = 364, + RBAC_PERM_COMMAND_DISABLE_REMOVE_SPELL = 365, + RBAC_PERM_COMMAND_DISABLE_REMOVE_VMAP = 366, + RBAC_PERM_COMMAND_EVENT_INFO = 367, + RBAC_PERM_COMMAND_EVENT_ACTIVELIST = 368, + RBAC_PERM_COMMAND_EVENT_START = 369, + RBAC_PERM_COMMAND_EVENT_STOP = 370, + RBAC_PERM_COMMAND_GM = 371, + RBAC_PERM_COMMAND_GM_CHAT = 372, + RBAC_PERM_COMMAND_GM_FLY = 373, + RBAC_PERM_COMMAND_GM_INGAME = 374, + RBAC_PERM_COMMAND_GM_LIST = 375, + RBAC_PERM_COMMAND_GM_VISIBLE = 376, + RBAC_PERM_COMMAND_GO = 377, + RBAC_PERM_COMMAND_GOBJECT_ACTIVATE = 388, + RBAC_PERM_COMMAND_GOBJECT_ADD = 389, + RBAC_PERM_COMMAND_GOBJECT_ADD_TEMP = 390, + RBAC_PERM_COMMAND_GOBJECT_DELETE = 391, + RBAC_PERM_COMMAND_GOBJECT_INFO = 392, + RBAC_PERM_COMMAND_GOBJECT_MOVE = 393, + RBAC_PERM_COMMAND_GOBJECT_NEAR = 394, + RBAC_PERM_COMMAND_GOBJECT_SET_PHASE = 396, + RBAC_PERM_COMMAND_GOBJECT_SET_STATE = 397, + RBAC_PERM_COMMAND_GOBJECT_TARGET = 398, + RBAC_PERM_COMMAND_GOBJECT_TURN = 399, + RBAC_PERM_COMMAND_GUILD = 401, + RBAC_PERM_COMMAND_GUILD_CREATE = 402, + RBAC_PERM_COMMAND_GUILD_DELETE = 403, + RBAC_PERM_COMMAND_GUILD_INVITE = 404, + RBAC_PERM_COMMAND_GUILD_UNINVITE = 405, + RBAC_PERM_COMMAND_GUILD_RANK = 406, + RBAC_PERM_COMMAND_GUILD_RENAME = 407, + RBAC_PERM_COMMAND_HONOR_ADD = 409, + RBAC_PERM_COMMAND_HONOR_ADD_KILL = 410, + RBAC_PERM_COMMAND_HONOR_UPDATE = 411, + RBAC_PERM_COMMAND_INSTANCE_LISTBINDS = 413, + RBAC_PERM_COMMAND_INSTANCE_UNBIND = 414, + RBAC_PERM_COMMAND_INSTANCE_STATS = 415, + RBAC_PERM_COMMAND_INSTANCE_SAVEDATA = 416, + RBAC_PERM_COMMAND_LEARN = 417, + RBAC_PERM_COMMAND_LEARN_ALL_MY = 419, + RBAC_PERM_COMMAND_LEARN_ALL_MY_CLASS = 420, + RBAC_PERM_COMMAND_LEARN_MY_PETTALENTS = 421, + RBAC_PERM_COMMAND_LEARN_ALL_MY_SPELLS = 422, + RBAC_PERM_COMMAND_LEARN_ALL_TALENTS = 423, + RBAC_PERM_COMMAND_LEARN_ALL_GM = 424, + RBAC_PERM_COMMAND_LEARN_ALL_CRAFTS = 425, + RBAC_PERM_COMMAND_LEARN_ALL_DEFAULT = 426, + RBAC_PERM_COMMAND_LEARN_ALL_LANG = 427, + RBAC_PERM_COMMAND_LEARN_ALL_RECIPES = 428, + RBAC_PERM_COMMAND_UNLEARN = 429, + RBAC_PERM_COMMAND_LFG_PLAYER = 431, + RBAC_PERM_COMMAND_LFG_GROUP = 432, + RBAC_PERM_COMMAND_LFG_QUEUE = 433, + RBAC_PERM_COMMAND_LFG_CLEAN = 434, + RBAC_PERM_COMMAND_LFG_OPTIONS = 435, + RBAC_PERM_COMMAND_LFG_COOLDOWN = 436, + RBAC_PERM_COMMAND_LIST_CREATURE = 437, + RBAC_PERM_COMMAND_LIST_ITEM = 438, + RBAC_PERM_COMMAND_LIST_OBJECT = 439, + RBAC_PERM_COMMAND_LIST_AURAS = 440, + RBAC_PERM_COMMAND_LIST_MAIL = 441, + RBAC_PERM_COMMAND_LOOKUP = 442, + RBAC_PERM_COMMAND_LOOKUP_AREA = 443, + RBAC_PERM_COMMAND_LOOKUP_CREATURE = 444, + RBAC_PERM_COMMAND_LOOKUP_EVENT = 445, + RBAC_PERM_COMMAND_LOOKUP_FACTION = 446, + RBAC_PERM_COMMAND_LOOKUP_ITEM = 447, + RBAC_PERM_COMMAND_LOOKUP_ITEMSET = 448, + RBAC_PERM_COMMAND_LOOKUP_OBJECT = 449, + RBAC_PERM_COMMAND_LOOKUP_QUEST = 450, + RBAC_PERM_COMMAND_LOOKUP_PLAYER = 451, + RBAC_PERM_COMMAND_LOOKUP_PLAYER_IP = 452, + RBAC_PERM_COMMAND_LOOKUP_PLAYER_ACCOUNT = 453, + RBAC_PERM_COMMAND_LOOKUP_PLAYER_EMAIL = 454, + RBAC_PERM_COMMAND_LOOKUP_SKILL = 455, + RBAC_PERM_COMMAND_LOOKUP_SPELL = 456, + RBAC_PERM_COMMAND_LOOKUP_SPELL_ID = 457, + RBAC_PERM_COMMAND_LOOKUP_TAXINODE = 458, + RBAC_PERM_COMMAND_LOOKUP_TELE = 459, + RBAC_PERM_COMMAND_LOOKUP_TITLE = 460, + RBAC_PERM_COMMAND_LOOKUP_MAP = 461, + RBAC_PERM_COMMAND_ANNOUNCE = 462, + RBAC_PERM_COMMAND_CHANNEL = 463, + RBAC_PERM_COMMAND_CHANNEL_SET = 464, + RBAC_PERM_COMMAND_CHANNEL_SET_OWNERSHIP = 465, + RBAC_PERM_COMMAND_GMANNOUNCE = 466, + RBAC_PERM_COMMAND_GMNAMEANNOUNCE = 467, + RBAC_PERM_COMMAND_GMNOTIFY = 468, + RBAC_PERM_COMMAND_NAMEANNOUNCE = 469, + RBAC_PERM_COMMAND_NOTIFY = 470, + RBAC_PERM_COMMAND_WHISPERS = 471, + RBAC_PERM_COMMAND_GROUP = 472, + RBAC_PERM_COMMAND_GROUP_LEADER = 473, + RBAC_PERM_COMMAND_GROUP_DISBAND = 474, + RBAC_PERM_COMMAND_GROUP_REMOVE = 475, + RBAC_PERM_COMMAND_GROUP_JOIN = 476, + RBAC_PERM_COMMAND_GROUP_LIST = 477, + RBAC_PERM_COMMAND_GROUP_SUMMON = 478, + RBAC_PERM_COMMAND_PET = 479, + RBAC_PERM_COMMAND_PET_CREATE = 480, + RBAC_PERM_COMMAND_PET_LEARN = 481, + RBAC_PERM_COMMAND_PET_UNLEARN = 482, + RBAC_PERM_COMMAND_SEND = 483, + RBAC_PERM_COMMAND_SEND_ITEMS = 484, + RBAC_PERM_COMMAND_SEND_MAIL = 485, + RBAC_PERM_COMMAND_SEND_MESSAGE = 486, + RBAC_PERM_COMMAND_SEND_MONEY = 487, + RBAC_PERM_COMMAND_ADDITEM = 488, + RBAC_PERM_COMMAND_ADDITEMSET = 489, + RBAC_PERM_COMMAND_APPEAR = 490, + RBAC_PERM_COMMAND_AURA = 491, + RBAC_PERM_COMMAND_BANK = 492, + RBAC_PERM_COMMAND_BINDSIGHT = 493, + RBAC_PERM_COMMAND_COMBATSTOP = 494, + RBAC_PERM_COMMAND_COMETOME = 495, + RBAC_PERM_COMMAND_COMMANDS = 496, + RBAC_PERM_COMMAND_COOLDOWN = 497, + RBAC_PERM_COMMAND_DAMAGE = 498, + RBAC_PERM_COMMAND_DEV = 499, + RBAC_PERM_COMMAND_DIE = 500, + RBAC_PERM_COMMAND_DISMOUNT = 501, + RBAC_PERM_COMMAND_DISTANCE = 502, + RBAC_PERM_COMMAND_FLUSHARENAPOINTS = 503, + RBAC_PERM_COMMAND_FREEZE = 504, + RBAC_PERM_COMMAND_GPS = 505, + RBAC_PERM_COMMAND_GUID = 506, + RBAC_PERM_COMMAND_HELP = 507, + RBAC_PERM_COMMAND_HIDEAREA = 508, + RBAC_PERM_COMMAND_ITEMMOVE = 509, + RBAC_PERM_COMMAND_KICK = 510, + RBAC_PERM_COMMAND_LINKGRAVE = 511, + RBAC_PERM_COMMAND_LISTFREEZE = 512, + RBAC_PERM_COMMAND_MAXSKILL = 513, + RBAC_PERM_COMMAND_MOVEGENS = 514, + RBAC_PERM_COMMAND_MUTE = 515, + RBAC_PERM_COMMAND_NEARGRAVE = 516, + RBAC_PERM_COMMAND_PINFO = 517, + RBAC_PERM_COMMAND_PLAYALL = 518, + RBAC_PERM_COMMAND_POSSESS = 519, + RBAC_PERM_COMMAND_RECALL = 520, + RBAC_PERM_COMMAND_REPAIRITEMS = 521, + RBAC_PERM_COMMAND_RESPAWN = 522, + RBAC_PERM_COMMAND_REVIVE = 523, + RBAC_PERM_COMMAND_SAVEALL = 524, + RBAC_PERM_COMMAND_SAVE = 525, + RBAC_PERM_COMMAND_SETSKILL = 526, + RBAC_PERM_COMMAND_SHOWAREA = 527, + RBAC_PERM_COMMAND_SUMMON = 528, + RBAC_PERM_COMMAND_UNAURA = 529, + RBAC_PERM_COMMAND_UNBINDSIGHT = 530, + RBAC_PERM_COMMAND_UNFREEZE = 531, + RBAC_PERM_COMMAND_UNMUTE = 532, + RBAC_PERM_COMMAND_UNPOSSESS = 533, + RBAC_PERM_COMMAND_UNSTUCK = 534, + RBAC_PERM_COMMAND_WCHANGE = 535, + RBAC_PERM_COMMAND_MMAP = 536, + RBAC_PERM_COMMAND_MMAP_LOADEDTILES = 537, + RBAC_PERM_COMMAND_MMAP_LOC = 538, + RBAC_PERM_COMMAND_MMAP_PATH = 539, + RBAC_PERM_COMMAND_MMAP_STATS = 540, + RBAC_PERM_COMMAND_MMAP_TESTAREA = 541, + RBAC_PERM_COMMAND_MORPH = 542, + RBAC_PERM_COMMAND_DEMORPH = 543, + RBAC_PERM_COMMAND_MODIFY = 544, + RBAC_PERM_COMMAND_MODIFY_ARENAPOINTS = 545, + RBAC_PERM_COMMAND_MODIFY_BIT = 546, + RBAC_PERM_COMMAND_MODIFY_DRUNK = 547, + RBAC_PERM_COMMAND_MODIFY_ENERGY = 548, + RBAC_PERM_COMMAND_MODIFY_FACTION = 549, + RBAC_PERM_COMMAND_MODIFY_GENDER = 550, + RBAC_PERM_COMMAND_MODIFY_HONOR = 551, + RBAC_PERM_COMMAND_MODIFY_HP = 552, + RBAC_PERM_COMMAND_MODIFY_MANA = 553, + RBAC_PERM_COMMAND_MODIFY_MONEY = 554, + RBAC_PERM_COMMAND_MODIFY_MOUNT = 555, + RBAC_PERM_COMMAND_MODIFY_PHASE = 556, + RBAC_PERM_COMMAND_MODIFY_RAGE = 557, + RBAC_PERM_COMMAND_MODIFY_REPUTATION = 558, + RBAC_PERM_COMMAND_MODIFY_RUNICPOWER = 559, + RBAC_PERM_COMMAND_MODIFY_SCALE = 560, + RBAC_PERM_COMMAND_MODIFY_SPEED = 561, + RBAC_PERM_COMMAND_MODIFY_SPEED_ALL = 562, + RBAC_PERM_COMMAND_MODIFY_SPEED_BACKWALK = 563, + RBAC_PERM_COMMAND_MODIFY_SPEED_FLY = 564, + RBAC_PERM_COMMAND_MODIFY_SPEED_WALK = 565, + RBAC_PERM_COMMAND_MODIFY_SPEED_SWIM = 566, + RBAC_PERM_COMMAND_MODIFY_SPELL = 567, + RBAC_PERM_COMMAND_MODIFY_STANDSTATE = 568, + RBAC_PERM_COMMAND_MODIFY_TALENTPOINTS = 569, + RBAC_PERM_COMMAND_NPC_ADD = 571, + RBAC_PERM_COMMAND_NPC_ADD_FORMATION = 572, + RBAC_PERM_COMMAND_NPC_ADD_ITEM = 573, + RBAC_PERM_COMMAND_NPC_ADD_MOVE = 574, + RBAC_PERM_COMMAND_NPC_ADD_TEMP = 575, + RBAC_PERM_COMMAND_NPC_DELETE = 576, + RBAC_PERM_COMMAND_NPC_DELETE_ITEM = 577, + RBAC_PERM_COMMAND_NPC_FOLLOW = 578, + RBAC_PERM_COMMAND_NPC_FOLLOW_STOP = 579, + RBAC_PERM_COMMAND_NPC_SET = 580, + RBAC_PERM_COMMAND_NPC_SET_ALLOWMOVE = 581, + RBAC_PERM_COMMAND_NPC_SET_ENTRY = 582, + RBAC_PERM_COMMAND_NPC_SET_FACTIONID = 583, + RBAC_PERM_COMMAND_NPC_SET_FLAG = 584, + RBAC_PERM_COMMAND_NPC_SET_LEVEL = 585, + RBAC_PERM_COMMAND_NPC_SET_LINK = 586, + RBAC_PERM_COMMAND_NPC_SET_MODEL = 587, + RBAC_PERM_COMMAND_NPC_SET_MOVETYPE = 588, + RBAC_PERM_COMMAND_NPC_SET_PHASE = 589, + RBAC_PERM_COMMAND_NPC_SET_SPAWNDIST = 590, + RBAC_PERM_COMMAND_NPC_SET_SPAWNTIME = 591, + RBAC_PERM_COMMAND_NPC_SET_DATA = 592, + RBAC_PERM_COMMAND_NPC_INFO = 593, + RBAC_PERM_COMMAND_NPC_NEAR = 594, + RBAC_PERM_COMMAND_NPC_MOVE = 595, + RBAC_PERM_COMMAND_NPC_PLAYEMOTE = 596, + RBAC_PERM_COMMAND_NPC_SAY = 597, + RBAC_PERM_COMMAND_NPC_TEXTEMOTE = 598, + RBAC_PERM_COMMAND_NPC_WHISPER = 599, + RBAC_PERM_COMMAND_NPC_YELL = 600, + RBAC_PERM_COMMAND_NPC_TAME = 601, + RBAC_PERM_COMMAND_QUEST = 602, + RBAC_PERM_COMMAND_QUEST_ADD = 603, + RBAC_PERM_COMMAND_QUEST_COMPLETE = 604, + RBAC_PERM_COMMAND_QUEST_REMOVE = 605, + RBAC_PERM_COMMAND_QUEST_REWARD = 606, + RBAC_PERM_COMMAND_RELOAD = 607, + RBAC_PERM_COMMAND_RELOAD_ACCESS_REQUIREMENT = 608, + RBAC_PERM_COMMAND_RELOAD_ACHIEVEMENT_CRITERIA_DATA = 609, + RBAC_PERM_COMMAND_RELOAD_ACHIEVEMENT_REWARD = 610, + RBAC_PERM_COMMAND_RELOAD_ALL = 611, + RBAC_PERM_COMMAND_RELOAD_ALL_ACHIEVEMENT = 612, + RBAC_PERM_COMMAND_RELOAD_ALL_AREA = 613, + RBAC_PERM_COMMAND_RELOAD_BROADCAST_TEXT = 614, + RBAC_PERM_COMMAND_RELOAD_ALL_GOSSIP = 615, + RBAC_PERM_COMMAND_RELOAD_ALL_ITEM = 616, + RBAC_PERM_COMMAND_RELOAD_ALL_LOCALES = 617, + RBAC_PERM_COMMAND_RELOAD_ALL_LOOT = 618, + RBAC_PERM_COMMAND_RELOAD_ALL_NPC = 619, + RBAC_PERM_COMMAND_RELOAD_ALL_QUEST = 620, + RBAC_PERM_COMMAND_RELOAD_ALL_SCRIPTS = 621, + RBAC_PERM_COMMAND_RELOAD_ALL_SPELL = 622, + RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_INVOLVEDRELATION = 623, + RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TAVERN = 624, + RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TELEPORT = 625, + RBAC_PERM_COMMAND_RELOAD_AUCTIONS = 626, + RBAC_PERM_COMMAND_RELOAD_AUTOBROADCAST = 627, + RBAC_PERM_COMMAND_RELOAD_CONDITIONS = 629, + RBAC_PERM_COMMAND_RELOAD_CONFIG = 630, + RBAC_PERM_COMMAND_RELOAD_BATTLEGROUND_TEMPLATE = 631, + RBAC_PERM_COMMAND_MUTEHISTORY = 632, + RBAC_PERM_COMMAND_RELOAD_CREATURE_LINKED_RESPAWN = 633, + RBAC_PERM_COMMAND_RELOAD_CREATURE_LOOT_TEMPLATE = 634, + RBAC_PERM_COMMAND_RELOAD_CREATURE_ONKILL_REPUTATION = 635, + RBAC_PERM_COMMAND_RELOAD_CREATURE_QUESTENDER = 636, + RBAC_PERM_COMMAND_RELOAD_CREATURE_QUESTSTARTER = 637, + RBAC_PERM_COMMAND_RELOAD_CREATURE_SUMMON_GROUPS = 638, + RBAC_PERM_COMMAND_RELOAD_CREATURE_TEMPLATE = 639, + RBAC_PERM_COMMAND_RELOAD_CREATURE_TEXT = 640, + RBAC_PERM_COMMAND_RELOAD_DISABLES = 641, + RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE = 642, + RBAC_PERM_COMMAND_RELOAD_EVENT_SCRIPTS = 643, + RBAC_PERM_COMMAND_RELOAD_FISHING_LOOT_TEMPLATE = 644, + RBAC_PERM_COMMAND_RELOAD_GRAVEYARD_ZONE = 645, + RBAC_PERM_COMMAND_RELOAD_GAME_TELE = 646, + RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER = 647, + RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE = 648, + RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTSTARTER = 649, + RBAC_PERM_COMMAND_RELOAD_GM_TICKETS = 650, + RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU = 651, + RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU_OPTION = 652, + RBAC_PERM_COMMAND_RELOAD_ITEM_ENCHANTMENT_TEMPLATE = 653, + RBAC_PERM_COMMAND_RELOAD_ITEM_LOOT_TEMPLATE = 654, + RBAC_PERM_COMMAND_RELOAD_ITEM_SET_NAMES = 655, + RBAC_PERM_COMMAND_RELOAD_LFG_DUNGEON_REWARDS = 656, + RBAC_PERM_COMMAND_RELOAD_ACHIEVEMENT_REWARD_LOCALE = 657, + RBAC_PERM_COMMAND_RELOAD_CREATURE_TEMPLATE_LOCALE = 658, + RBAC_PERM_COMMAND_RELOAD_CREATURE_TEXT_LOCALE = 659, + RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_TEMPLATE_LOCALE = 660, + RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU_OPTION_LOCALE = 661, + RBAC_PERM_COMMAND_RELOAD_ITEM_TEMPLATE_LOCALE = 662, + RBAC_PERM_COMMAND_RELOAD_ITEM_SET_NAME_LOCALE = 663, + RBAC_PERM_COMMAND_RELOAD_NPC_TEXT_LOCALE = 664, + RBAC_PERM_COMMAND_RELOAD_PAGE_TEXT_LOCALE = 665, + RBAC_PERM_COMMAND_RELOAD_POINTS_OF_INTEREST_LOCALE = 666, + RBAC_PERM_COMMAND_RELOAD_QUEST_TEMPLATE_LOCALE = 667, + RBAC_PERM_COMMAND_RELOAD_MAIL_LEVEL_REWARD = 668, + RBAC_PERM_COMMAND_RELOAD_MAIL_LOOT_TEMPLATE = 669, + RBAC_PERM_COMMAND_RELOAD_MILLING_LOOT_TEMPLATE = 670, + RBAC_PERM_COMMAND_RELOAD_NPC_SPELLCLICK_SPELLS = 671, + RBAC_PERM_COMMAND_RELOAD_TRAINER = 672, + RBAC_PERM_COMMAND_RELOAD_NPC_VENDOR = 673, + RBAC_PERM_COMMAND_RELOAD_PAGE_TEXT = 674, + RBAC_PERM_COMMAND_RELOAD_PICKPOCKETING_LOOT_TEMPLATE = 675, + RBAC_PERM_COMMAND_RELOAD_POINTS_OF_INTEREST = 676, + RBAC_PERM_COMMAND_RELOAD_PROSPECTING_LOOT_TEMPLATE = 677, + RBAC_PERM_COMMAND_RELOAD_QUEST_POI = 678, + RBAC_PERM_COMMAND_RELOAD_QUEST_TEMPLATE = 679, + RBAC_PERM_COMMAND_RELOAD_RBAC = 680, + RBAC_PERM_COMMAND_RELOAD_REFERENCE_LOOT_TEMPLATE = 681, + RBAC_PERM_COMMAND_RELOAD_RESERVED_NAME = 682, + RBAC_PERM_COMMAND_RELOAD_REPUTATION_REWARD_RATE = 683, + RBAC_PERM_COMMAND_RELOAD_SPILLOVER_TEMPLATE = 684, + RBAC_PERM_COMMAND_RELOAD_SKILL_DISCOVERY_TEMPLATE = 685, + RBAC_PERM_COMMAND_RELOAD_SKILL_EXTRA_ITEM_TEMPLATE = 686, + RBAC_PERM_COMMAND_RELOAD_SKILL_FISHING_BASE_LEVEL = 687, + RBAC_PERM_COMMAND_RELOAD_SKINNING_LOOT_TEMPLATE = 688, + RBAC_PERM_COMMAND_RELOAD_SMART_SCRIPTS = 689, + RBAC_PERM_COMMAND_RELOAD_SPELL_REQUIRED = 690, + RBAC_PERM_COMMAND_RELOAD_SPELL_AREA = 691, + RBAC_PERM_COMMAND_RELOAD_SPELL_BONUS_DATA = 692, + RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP = 693, + RBAC_PERM_COMMAND_RELOAD_SPELL_LEARN_SPELL = 694, + RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE = 695, + RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL = 696, + RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS = 697, + RBAC_PERM_COMMAND_CHARACTER_CHANGEACCOUNT = 698, + RBAC_PERM_COMMAND_RELOAD_SPELL_PROC = 699, + RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION = 701, + RBAC_PERM_COMMAND_RELOAD_SPELL_THREATS = 702, + RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP_STACK_RULES = 703, + RBAC_PERM_COMMAND_RELOAD_ACORE_STRING = 704, + RBAC_PERM_COMMAND_RELOAD_WAYPOINT_SCRIPTS = 706, + RBAC_PERM_COMMAND_RELOAD_WAYPOINT_DATA = 707, + RBAC_PERM_COMMAND_RELOAD_VEHICLE_ACCESSORY = 708, + RBAC_PERM_COMMAND_RELOAD_VEHICLE_TEMPLATE_ACCESSORY = 709, + RBAC_PERM_COMMAND_RESET = 710, + RBAC_PERM_COMMAND_RESET_ACHIEVEMENTS = 711, + RBAC_PERM_COMMAND_RESET_HONOR = 712, + RBAC_PERM_COMMAND_RESET_LEVEL = 713, + RBAC_PERM_COMMAND_RESET_SPELLS = 714, + RBAC_PERM_COMMAND_RESET_STATS = 715, + RBAC_PERM_COMMAND_RESET_TALENTS = 716, + RBAC_PERM_COMMAND_RESET_ALL = 717, + RBAC_PERM_COMMAND_SERVER = 718, + RBAC_PERM_COMMAND_SERVER_CORPSES = 719, + RBAC_PERM_COMMAND_SERVER_EXIT = 720, + RBAC_PERM_COMMAND_SERVER_IDLERESTART = 721, + RBAC_PERM_COMMAND_SERVER_IDLERESTART_CANCEL = 722, + RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN = 723, + RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN_CANCEL = 724, + RBAC_PERM_COMMAND_SERVER_INFO = 725, + RBAC_PERM_COMMAND_SERVER_PLIMIT = 726, + RBAC_PERM_COMMAND_SERVER_RESTART = 727, + RBAC_PERM_COMMAND_SERVER_RESTART_CANCEL = 728, + RBAC_PERM_COMMAND_SERVER_SET = 729, + RBAC_PERM_COMMAND_SERVER_SET_CLOSED = 730, + RBAC_PERM_COMMAND_SERVER_SET_DIFFTIME = 731, + RBAC_PERM_COMMAND_SERVER_SET_LOGLEVEL = 732, + RBAC_PERM_COMMAND_SERVER_SET_MOTD = 733, + RBAC_PERM_COMMAND_SERVER_SHUTDOWN = 734, + RBAC_PERM_COMMAND_SERVER_SHUTDOWN_CANCEL = 735, + RBAC_PERM_COMMAND_SERVER_MOTD = 736, + RBAC_PERM_COMMAND_TELE = 737, + RBAC_PERM_COMMAND_TELE_ADD = 738, + RBAC_PERM_COMMAND_TELE_DEL = 739, + RBAC_PERM_COMMAND_TELE_NAME = 740, + RBAC_PERM_COMMAND_TELE_GROUP = 741, + RBAC_PERM_COMMAND_TICKET = 742, + RBAC_PERM_COMMAND_TICKET_ASSIGN = 743, + RBAC_PERM_COMMAND_TICKET_CLOSE = 744, + RBAC_PERM_COMMAND_TICKET_CLOSEDLIST = 745, + RBAC_PERM_COMMAND_TICKET_COMMENT = 746, + RBAC_PERM_COMMAND_TICKET_COMPLETE = 747, + RBAC_PERM_COMMAND_TICKET_DELETE = 748, + RBAC_PERM_COMMAND_TICKET_ESCALATE = 749, + RBAC_PERM_COMMAND_TICKET_ESCALATEDLIST = 750, + RBAC_PERM_COMMAND_TICKET_LIST = 751, + RBAC_PERM_COMMAND_TICKET_ONLINELIST = 752, + RBAC_PERM_COMMAND_TICKET_RESET = 753, + RBAC_PERM_COMMAND_TICKET_RESPONSE = 754, + RBAC_PERM_COMMAND_TICKET_RESPONSE_APPEND = 755, + RBAC_PERM_COMMAND_TICKET_RESPONSE_APPENDLN = 756, + RBAC_PERM_COMMAND_TICKET_TOGGLESYSTEM = 757, + RBAC_PERM_COMMAND_TICKET_UNASSIGN = 758, + RBAC_PERM_COMMAND_TICKET_VIEWID = 759, + RBAC_PERM_COMMAND_TICKET_VIEWNAME = 760, + RBAC_PERM_COMMAND_TITLES_ADD = 762, + RBAC_PERM_COMMAND_TITLES_CURRENT = 763, + RBAC_PERM_COMMAND_TITLES_REMOVE = 764, + RBAC_PERM_COMMAND_TITLES_SET_MASK = 766, + RBAC_PERM_COMMAND_WP = 767, + RBAC_PERM_COMMAND_WP_ADD = 768, + RBAC_PERM_COMMAND_WP_EVENT = 769, + RBAC_PERM_COMMAND_WP_LOAD = 770, + RBAC_PERM_COMMAND_WP_MODIFY = 771, + RBAC_PERM_COMMAND_WP_UNLOAD = 772, + RBAC_PERM_COMMAND_WP_RELOAD = 773, + RBAC_PERM_COMMAND_WP_SHOW = 774, + RBAC_PERM_COMMAND_MAILBOX = 777, + RBAC_PERM_COMMAND_AHBOT_ITEMS = 779, + RBAC_PERM_COMMAND_AHBOT_ITEMS_GRAY = 780, + RBAC_PERM_COMMAND_AHBOT_ITEMS_WHITE = 781, + RBAC_PERM_COMMAND_AHBOT_ITEMS_GREEN = 782, + RBAC_PERM_COMMAND_AHBOT_ITEMS_BLUE = 783, + RBAC_PERM_COMMAND_AHBOT_ITEMS_PURPLE = 784, + RBAC_PERM_COMMAND_AHBOT_ITEMS_ORANGE = 785, + RBAC_PERM_COMMAND_AHBOT_ITEMS_YELLOW = 786, + RBAC_PERM_COMMAND_AHBOT_RATIO = 787, + RBAC_PERM_COMMAND_AHBOT_RATIO_ALLIANCE = 788, + RBAC_PERM_COMMAND_AHBOT_RATIO_HORDE = 789, + RBAC_PERM_COMMAND_AHBOT_RATIO_NEUTRAL = 790, + RBAC_PERM_COMMAND_AHBOT_REBUILD = 791, + RBAC_PERM_COMMAND_AHBOT_RELOAD = 792, + RBAC_PERM_COMMAND_AHBOT_STATUS = 793, + RBAC_PERM_COMMAND_GUILD_INFO = 794, + RBAC_PERM_COMMAND_INSTANCE_SET_BOSS_STATE = 795, + RBAC_PERM_COMMAND_INSTANCE_GET_BOSS_STATE = 796, + RBAC_PERM_COMMAND_PVPSTATS = 797, + RBAC_PERM_COMMAND_MODIFY_XP = 798, + RBAC_PERM_COMMAND_NPC_EVADE = 837, + RBAC_PERM_COMMAND_PET_LEVEL = 838, + RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE = 839, + RBAC_PERM_COMMAND_SERVER_RESTART_FORCE = 840, + RBAC_PERM_COMMAND_NEARGRAVEYARD = 841, + RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING = 843, + RBAC_PERM_COMMAND_NPC_SPAWNGROUP = 856, + RBAC_PERM_COMMAND_NPC_DESPAWNGROUP = 857, + RBAC_PERM_COMMAND_GOBJECT_SPAWNGROUP = 858, + RBAC_PERM_COMMAND_GOBJECT_DESPAWNGROUP = 859, + RBAC_PERM_COMMAND_LIST_RESPAWNS = 860, + RBAC_PERM_COMMAND_GROUP_SET = 861, + RBAC_PERM_COMMAND_GROUP_ASSISTANT = 862, + RBAC_PERM_COMMAND_GROUP_MAINTANK = 863, + RBAC_PERM_COMMAND_GROUP_MAINASSIST = 864, + RBAC_PERM_COMMAND_GROUP_REVIVE = 868, + RBAC_PERM_COMMAND_NPC_SHOWLOOT = 865, + RBAC_PERM_COMMAND_LIST_SPAWNPOINTS = 866, + RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING_LOCALE = 867, + RBAC_PERM_COMMAND_SERVER_DEBUG = 872, + RBAC_PERM_COMMAND_RELOAD_CREATURE_MOVEMENT_OVERRIDE = 873, + RBAC_PERM_COMMAND_SETTINGS_ANNOUNCER = 874, + RBAC_PERM_COMMAND_LOOKUP_MAP_ID = 875, + RBAC_PERM_COMMAND_LOOKUP_ITEM_ID = 876, + RBAC_PERM_COMMAND_LOOKUP_QUEST_ID = 877, + RBAC_PERM_COMMAND_PDUMP_COPY = 880, + RBAC_PERM_COMMAND_RELOAD_VEHICLE_TEMPLATE = 881, + RBAC_PERM_COMMAND_BG_START = 884, + RBAC_PERM_COMMAND_BG_STOP = 885, + RBAC_PERM_COMMAND_ITEM_RESTORE = 886, + RBAC_PERM_COMMAND_ITEM_RESTORE_LIST = 887, + RBAC_PERM_COMMAND_ITEM_REFUND = 888, + RBAC_PERM_COMMAND_COMMENTATOR = 889, + RBAC_PERM_COMMAND_SKIRMISH = 890, + RBAC_PERM_COMMAND_STRING = 891, + RBAC_PERM_COMMAND_OPENDOOR = 892, + RBAC_PERM_COMMAND_BEASTMASTER = 893, + RBAC_PERM_COMMAND_PACKETLOG = 894, + RBAC_PERM_COMMAND_AURA_STACK = 895, + RBAC_PERM_COMMAND_RESPAWN_ALL = 896, + RBAC_PERM_COMMAND_GEAR_REPAIR = 897, + RBAC_PERM_COMMAND_GEAR_STATS = 898, + RBAC_PERM_COMMAND_SPECT = 899, + RBAC_PERM_COMMAND_SPECT_VERSION = 900, + RBAC_PERM_COMMAND_SPECT_RESET = 901, + RBAC_PERM_COMMAND_SPECT_SPECTATE = 902, + RBAC_PERM_COMMAND_SPECT_WATCH = 903, + RBAC_PERM_COMMAND_SPECT_LEAVE = 904, + RBAC_PERM_COMMAND_ARENA_SEASON = 905, + RBAC_PERM_COMMAND_ARENA_SEASON_REWARD = 906, + RBAC_PERM_COMMAND_ARENA_SEASON_DELETETEAMS = 907, + RBAC_PERM_COMMAND_ARENA_SEASON_START = 908, + RBAC_PERM_COMMAND_CHARACTER_CHECK_BANK = 909, + RBAC_PERM_COMMAND_CHARACTER_CHECK_BAG = 910, + RBAC_PERM_COMMAND_CHARACTER_CHECK_PROFESSION = 911, + RBAC_PERM_COMMAND_GOBJECT_LOAD = 912, + RBAC_PERM_COMMAND_BF_QUEUE = 913, + // custom permissions 1000+ + RBAC_PERM_MAX +}; + +enum RBACCommandResult +{ + RBAC_OK, + RBAC_CANT_ADD_ALREADY_ADDED, + RBAC_CANT_REVOKE_NOT_IN_LIST, + RBAC_IN_GRANTED_LIST, + RBAC_IN_DENIED_LIST, + RBAC_ID_DOES_NOT_EXISTS +}; + +typedef std::set RBACPermissionContainer; + +class AC_GAME_API RBACPermission +{ +public: + RBACPermission(uint32 id = 0, std::string const& name = ""): + _id(id), _name(name), _perms() { } + + /// Gets the Name of the Object + std::string const& GetName() const { return _name; } + /// Gets the Id of the Object + uint32 GetId() const { return _id; } + + /// Gets the Permissions linked to this permission + RBACPermissionContainer const& GetLinkedPermissions() const { return _perms; } + /// Adds a new linked Permission + void AddLinkedPermission(uint32 id) { _perms.insert(id); } + /// Removes a linked Permission + void RemoveLinkedPermission(uint32 id) { _perms.erase(id); } + +private: + uint32 _id; ///> id of the object + std::string _name; ///> name of the object + RBACPermissionContainer _perms; ///> Set of permissions +}; + +/** + * @name RBACData + * @brief Contains all needed information about the account + * + * This class contains all the data needed to calculate the account permissions. + * RBACData is formed by granted and denied permissions and all the inherited permissions + * + * Calculation of current Permissions: Granted permissions - Denied permissions + * - Granted permissions: through linked permissions and directly assigned + * - Denied permissions: through linked permissions and directly assigned + */ +class AC_GAME_API RBACData +{ +public: + RBACData(uint32 id, std::string const& name, int32 realmId, uint8 secLevel = 255): + _id(id), _name(name), _realmId(realmId), _secLevel(secLevel), + _grantedPerms(), _deniedPerms(), _globalPerms() { } + + /// Gets the Name of the Object + std::string const& GetName() const { return _name; } + /// Gets the Id of the Object + uint32 GetId() const { return _id; } + + /** + * @name HasPermission + * @brief Checks if certain action is allowed + * + * @return grant or deny action + */ + bool HasPermission(uint32 permission) const + { + return _globalPerms.find(permission) != _globalPerms.end(); + } + + // Functions enabled to be used by command system + /// Returns all the granted permissions (after computation) + RBACPermissionContainer const& GetPermissions() const { return _globalPerms; } + /// Returns all the granted permissions + RBACPermissionContainer const& GetGrantedPermissions() const { return _grantedPerms; } + /// Returns all the denied permissions + RBACPermissionContainer const& GetDeniedPermissions() const { return _deniedPerms; } + + /** + * @name GrantPermission + * @brief Grants a permission + * + * @param permissionId permission to be granted + * @param realmId realm affected + * + * @return Success or failure (with reason) to grant the permission + */ + RBACCommandResult GrantPermission(uint32 permissionId, int32 realmId = 0); + + /** + * @name DenyPermission + * @brief Denies a permission + * + * @param permissionId permission to be denied + * @param realmId realm affected + * + * @return Success or failure (with reason) to deny the permission + */ + RBACCommandResult DenyPermission(uint32 permissionId, int32 realmId = 0); + + /** + * @name RevokePermission + * @brief Removes a permission + * + * @param permissionId permission to be removed + * @param realmId realm affected + * + * @return Success or failure (with reason) to remove the permission + */ + RBACCommandResult RevokePermission(uint32 permissionId, int32 realmId = 0); + + /// Loads all permissions assigned to current account + void LoadFromDB(); + QueryCallback LoadFromDBAsync(); + void LoadFromDBCallback(PreparedQueryResult result); + + /// Sets security level + void SetSecurityLevel(uint8 id) + { + _secLevel = id; + LoadFromDB(); + } + + /// Returns the security level assigned + uint8 GetSecurityLevel() const { return _secLevel; } + + /// For unit testing - forces permission recalculation without database + void RecalculatePermissions() { CalculateNewPermissions(); } + + /// For unit testing - sets security level without database reload + void SetSecurityLevelForTest(uint8 id) { _secLevel = id; } + +private: + /// Saves a permission to DB, Granted or Denied + void SavePermission(uint32 role, bool granted, int32 realm); + /// Clears roles, groups and permissions - Used for reload + void ClearData(); + + /** + * @name CalculateNewPermissions + * @brief Calculates new permissions + * + * Calculates new permissions after some change + * The calculation is done Granted - Denied: + * - Granted permissions: through linked permissions and directly assigned + * - Denied permissions: through linked permissions and directly assigned + */ + void CalculateNewPermissions(); + + int32 GetRealmId() const { return _realmId; } + + /// Checks if a permission is granted + bool HasGrantedPermission(uint32 permissionId) const + { + return _grantedPerms.find(permissionId) != _grantedPerms.end(); + } + + /// Checks if a permission is denied + bool HasDeniedPermission(uint32 permissionId) const + { + return _deniedPerms.find(permissionId) != _deniedPerms.end(); + } + + /// Adds a new granted permission + void AddGrantedPermission(uint32 permissionId) + { + _grantedPerms.insert(permissionId); + } + + /// Removes a granted permission + void RemoveGrantedPermission(uint32 permissionId) + { + _grantedPerms.erase(permissionId); + } + + /// Adds a new denied permission + void AddDeniedPermission(uint32 permissionId) + { + _deniedPerms.insert(permissionId); + } + + /// Removes a denied permission + void RemoveDeniedPermission(uint32 permissionId) + { + _deniedPerms.erase(permissionId); + } + + /// Adds a list of permissions to another list + void AddPermissions(RBACPermissionContainer const& permsFrom, RBACPermissionContainer& permsTo); + + /// Removes a list of permissions from another list + void RemovePermissions(RBACPermissionContainer& permsFrom, RBACPermissionContainer const& permsToRemove); + + /** + * @name ExpandPermissions + * @brief Adds the list of linked permissions to the original list + * + * @param permissions The list of permissions to expand + */ + void ExpandPermissions(RBACPermissionContainer& permissions); + + uint32 _id; ///> Account id + std::string _name; ///> Account name + int32 _realmId; ///> RealmId Affected + uint8 _secLevel; ///> Account SecurityLevel + RBACPermissionContainer _grantedPerms; ///> Granted permissions + RBACPermissionContainer _deniedPerms; ///> Denied permissions + RBACPermissionContainer _globalPerms; ///> Calculated permissions +}; + +} + +#endif diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 29628f1f8..f0aa9da38 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -40,6 +40,7 @@ #include "MapMgr.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "RaceMgr.h" #include "ReputationMgr.h" #include "ScriptMgr.h" @@ -520,6 +521,8 @@ void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaCondition condi // disable for gamemasters with GM-mode enabled if (_player->IsGameMaster()) return; + if (_player->GetSession()->HasPermission(rbac::RBAC_PERM_CANNOT_EARN_ACHIEVEMENTS)) + return; LOG_DEBUG("achievement", "AchievementMgr::ResetAchievementCriteria({}, {}, {})", condition, value, evenIfCriteriaComplete); @@ -821,6 +824,8 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui // disable for gamemasters with GM-mode enabled if (_player->IsGameMaster()) return; + if (_player->GetSession()->HasPermission(rbac::RBAC_PERM_CANNOT_EARN_ACHIEVEMENTS)) + return; if (type >= ACHIEVEMENT_CRITERIA_TYPE_TOTAL) { @@ -2282,6 +2287,8 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) ChatHandler(_player->GetSession()).PSendSysMessage("Not available in GM mode"); return; } + if (_player->GetSession()->HasPermission(rbac::RBAC_PERM_CANNOT_EARN_ACHIEVEMENTS)) + return; if (!sScriptMgr->OnPlayerBeforeAchievementComplete(GetPlayer(), achievement)) { @@ -2323,7 +2330,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement) } } - if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL) && AccountMgr::IsPlayerAccount(_player->GetSession()->GetSecurity())) + if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL) && !_player->GetSession()->HasPermission(rbac::RBAC_PERM_CANNOT_EARN_REALM_FIRST_ACHIEVEMENTS)) sAchievementMgr->SetRealmCompleted(achievement); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT, achievement->ID); diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index c72f94394..ad6d74523 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -39,6 +39,7 @@ #include "ObjectMgr.h" #include "Pet.h" #include "Player.h" +#include "RBAC.h" #include "ReputationMgr.h" #include "ScriptMgr.h" #include "SpellAuras.h" @@ -1110,7 +1111,7 @@ void Battleground::RemovePlayerAtLeave(Player* player) SendPacketToTeam(teamId, &data, player, false); // cast deserter - if (isBattleground() && !player->IsGameMaster() && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_CAST_DESERTER)) + if (isBattleground() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_NO_BATTLEGROUND_DESERTER_DEBUFF) && sWorld->getBoolConfig(CONFIG_BATTLEGROUND_CAST_DESERTER)) if (status == STATUS_IN_PROGRESS || status == STATUS_WAIT_JOIN) player->ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER); diff --git a/src/server/game/Chat/Channels/Channel.cpp b/src/server/game/Chat/Channels/Channel.cpp index 05e887a6b..ad7b9dfc9 100644 --- a/src/server/game/Chat/Channels/Channel.cpp +++ b/src/server/game/Chat/Channels/Channel.cpp @@ -23,6 +23,7 @@ #include "GameTime.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "SocialMgr.h" #include "World.h" @@ -218,7 +219,7 @@ void Channel::JoinChannel(Player* player, std::string const& pass) JoinNotify(player); - playersStore[guid].SetOwnerGM(player->GetSession()->IsGMAccount()); + playersStore[guid].SetOwnerGM(player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)); // Custom channel handling if (!IsConstant()) @@ -296,11 +297,11 @@ void Channel::LeaveChannel(Player* player, bool send) for (Channel::PlayerContainer::const_iterator itr = playersStore.begin(); itr != playersStore.end(); ++itr) { newowner = itr->second.player; - if (itr->second.plrPtr->GetSession()->IsGMAccount()) + if (itr->second.plrPtr->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) _isOwnerGM = true; else _isOwnerGM = false; - if (!itr->second.plrPtr->GetSession()->GetSecurity()) + if (!itr->second.plrPtr->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) break; } SetOwner(newowner); @@ -324,7 +325,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b return; } - if (!playersStore[good].IsModerator() && !player->GetSession()->IsGMAccount()) + if (!playersStore[good].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -347,7 +348,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b bool isOnChannel = victim && IsOn(victim); if (!isOnChannel) { - if (ban && (player->GetSession()->IsGMAccount() || isGoodConstantModerator)) + if (ban && (player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) || isGoodConstantModerator)) { if (ObjectGuid guid = sCharacterCache->GetCharacterGuidByName(badname)) { @@ -387,7 +388,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b bool changeowner = _ownerGUID == victim; bool isBadConstantModerator = _channelRights.moderators.find(badAccId) != _channelRights.moderators.end(); - if (!player->GetSession()->IsGMAccount() && !isGoodConstantModerator) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && !isGoodConstantModerator) { if (changeowner && good != _ownerGUID) { @@ -405,7 +406,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b return; } - if (isBadConstantModerator || bad->GetSession()->IsGMAccount()) + if (isBadConstantModerator || bad->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -454,7 +455,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b for (Channel::PlayerContainer::const_iterator itr = playersStore.begin(); itr != playersStore.end(); ++itr) { newowner = itr->second.player; - if (!itr->second.plrPtr->GetSession()->GetSecurity()) + if (!itr->second.plrPtr->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) break; } SetOwner(newowner); @@ -476,7 +477,7 @@ void Channel::UnBan(Player const* player, std::string const& badname) return; } - if (!playersStore[good].IsModerator() && !player->GetSession()->IsGMAccount()) + if (!playersStore[good].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -499,7 +500,7 @@ void Channel::UnBan(Player const* player, std::string const& badname) } bool isConstantModerator = _channelRights.moderators.find(player->GetSession()->GetAccountId()) != _channelRights.moderators.end(); - if (!player->GetSession()->IsGMAccount() && !isConstantModerator) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && !isConstantModerator) { if (_channelRights.flags & CHANNEL_RIGHT_CANT_BAN) { @@ -544,7 +545,7 @@ void Channel::Password(Player const* player, std::string const& pass) return; } - if (!playersStore[guid].IsModerator() && !player->GetSession()->IsGMAccount()) + if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -581,7 +582,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo return; } - if (!playersStore[guid].IsModerator() && !player->GetSession()->IsGMAccount()) + if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -598,7 +599,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo if (!victim || !IsOn(victim) || // allow make moderator from another team only if both is GMs // at this moment this only way to show channel post for GM from another team - ((!player->GetSession()->IsGMAccount() || !newp->GetSession()->IsGMAccount()) && player->GetTeamId() != newp->GetTeamId() && + ((!player->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL) || !newp->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL)) && player->GetTeamId() != newp->GetTeamId() && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL))) { WorldPacket data; @@ -624,7 +625,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo else { bool isGoodConstantModerator = _channelRights.moderators.find(player->GetSession()->GetAccountId()) != _channelRights.moderators.end(); - if (!player->GetSession()->IsGMAccount() && !isGoodConstantModerator) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && !isGoodConstantModerator) { if (_channelRights.flags & CHANNEL_RIGHT_CANT_MUTE) { @@ -652,7 +653,7 @@ void Channel::SetOwner(Player const* player, std::string const& newname) } bool isGoodConstantModerator = _channelRights.moderators.find(player->GetSession()->GetAccountId()) != _channelRights.moderators.end(); - if (!player->GetSession()->IsGMAccount() && guid != _ownerGUID && !isGoodConstantModerator) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && guid != _ownerGUID && !isGoodConstantModerator) { WorldPacket data; MakeNotOwner(&data); @@ -706,15 +707,20 @@ void Channel::List(Player const* player) std::size_t pos = data.wpos(); data << uint32(0); // size of list, placeholder + uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); + uint32 count = 0; if (!(_channelRights.flags & CHANNEL_RIGHT_CANT_SPEAK)) for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i) - if (AccountMgr::IsPlayerAccount(i->second.plrPtr->GetSession()->GetSecurity())) - { - data << i->first; - data << uint8(i->second.flags); // flags seems to be changed... - ++count; - } + if (Player* member = i->second.plrPtr) + if ((player->GetSession()->HasPermission(rbac::RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS) || + member->GetSession()->GetSecurity() <= AccountTypes(gmLevelInWhoList)) && + member->IsVisibleGloballyFor(player)) + { + data << i->first; + data << uint8(i->second.flags); // flags seems to be changed... + ++count; + } data.put(pos, count); @@ -733,7 +739,7 @@ void Channel::Announce(Player const* player) return; } - if (!playersStore[guid].IsModerator() && !player->GetSession()->IsGMAccount()) + if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -932,7 +938,7 @@ void Channel::SendToAllWatching(WorldPacket* data) bool Channel::ShouldAnnouncePlayer(Player const* player) const { - return !(player->GetSession()->IsGMAccount() && sWorld->getBoolConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL)); + return !(player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL) && sWorld->getBoolConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL)); } void Channel::Voice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) @@ -1158,7 +1164,7 @@ void Channel::JoinNotify(Player* p) { if (_channelRights.flags & CHANNEL_RIGHT_CANT_SPEAK) return; - if (!AccountMgr::IsPlayerAccount(p->GetSession()->GetSecurity())) + if (p->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) return; WorldPacket data(SMSG_USERLIST_ADD, 8 + 1 + 1 + 4 + GetName().size()); @@ -1175,7 +1181,7 @@ void Channel::LeaveNotify(Player* p) { if (_channelRights.flags & CHANNEL_RIGHT_CANT_SPEAK) return; - if (!AccountMgr::IsPlayerAccount(p->GetSession()->GetSecurity())) + if (p->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) return; WorldPacket data(SMSG_USERLIST_REMOVE, 8 + 1 + 4 + GetName().size()); @@ -1191,7 +1197,7 @@ void Channel::FlagsNotify(Player* p) { if (_channelRights.flags & CHANNEL_RIGHT_CANT_SPEAK) return; - if (!AccountMgr::IsPlayerAccount(p->GetSession()->GetSecurity())) + if (p->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) return; WorldPacket data(SMSG_USERLIST_UPDATE, 8 + 1 + 1 + 4 + GetName().size()); @@ -1230,8 +1236,7 @@ void Channel::ToggleModeration(Player* player) return; } - const AccountTypes level = static_cast(sWorld->getIntConfig(CONFIG_GM_LEVEL_CHANNEL_MODERATION)); - const bool gm = (player->GetSession()->GetSecurity() >= level); + const bool gm = player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR); if (!playersStore[guid].IsModerator() && !gm) { diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index a0dc7f090..69b969eef 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -24,6 +24,7 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "RBAC.h" #include "Realm.h" #include "ScriptMgr.h" #include "Tokenize.h" @@ -54,6 +55,13 @@ bool ChatHandler::IsAvailable(uint32 securityLevel) const return IsConsole() ? true : m_session->GetSecurity() >= AccountTypes(securityLevel); } +bool ChatHandler::HasPermission(uint32 permissionId) const +{ + if (IsConsole()) + return true; + return m_session && m_session->HasPermission(permissionId); +} + bool ChatHandler::HasLowerSecurity(Player* target, ObjectGuid guid, bool strong) { WorldSession* target_session = nullptr; @@ -82,7 +90,7 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac return false; // ignore only for non-players for non strong checks (when allow apply command at least to same sec level) - if (!AccountMgr::IsPlayerAccount(m_session->GetSecurity()) && !strong && sWorld->getBoolConfig(CONFIG_GM_LOWER_SECURITY)) + if (m_session->HasPermission(rbac::RBAC_PERM_CAN_IGNORE_LOWER_SECURITY_CHECK) && !strong && sWorld->getBoolConfig(CONFIG_GM_LOWER_SECURITY)) return false; if (target) @@ -117,7 +125,7 @@ void ChatHandler::SendGMText(std::string_view str) { std::vector lines = Acore::Tokenize(str, '\n', true); // Session should have permissions to receive global gm messages - if (AccountMgr::IsPlayerAccount(m_session->GetSecurity())) + if (!m_session->HasPermission(rbac::RBAC_PERM_RECEIVE_GLOBAL_GM_TEXTMESSAGE)) return; for (std::string_view line : lines) @@ -231,7 +239,7 @@ bool ChatHandler::_ParseCommands(std::string_view text) return true; // Pretend commands don't exist for regular players - if (m_session && AccountMgr::IsPlayerAccount(m_session->GetSecurity()) && !sWorld->getBoolConfig(CONFIG_ALLOW_PLAYER_COMMANDS)) + if (m_session && !m_session->HasPermission(rbac::RBAC_PERM_COMMANDS_NOTIFY_COMMAND_NOT_FOUND_ERROR) && !sWorld->getBoolConfig(CONFIG_ALLOW_PLAYER_COMMANDS)) return false; // Send error message for GMs @@ -358,7 +366,7 @@ std::size_t ChatHandler::BuildChatPacket(WorldPacket& data, ChatMsg chatType, La if (Player const* playerSender = sender->ToPlayer()) { chatTag = playerSender->GetChatTag(); - gmMessage = playerSender->IsGameMaster(); + gmMessage = playerSender->GetSession()->HasPermission(rbac::RBAC_PERM_COMMAND_GM_CHAT); } } diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h index e2891a3b8..7bc8f0e80 100644 --- a/src/server/game/Chat/Chat.h +++ b/src/server/game/Chat/Chat.h @@ -241,6 +241,7 @@ public: Player* GetPlayer() const; WorldSession* GetSession() { return m_session; } bool IsAvailable(uint32 securityLevel) const; + bool HasPermission(uint32 permissionId) const; protected: explicit ChatHandler() : m_session(nullptr), sentErrorMessage(false) {} // for CLI subclass diff --git a/src/server/game/Chat/ChatCommands/ChatCommand.cpp b/src/server/game/Chat/ChatCommands/ChatCommand.cpp index 0e763f1b3..6e72f54aa 100644 --- a/src/server/game/Chat/ChatCommands/ChatCommand.cpp +++ b/src/server/game/Chat/ChatCommands/ChatCommand.cpp @@ -22,6 +22,7 @@ #include "DatabaseEnv.h" #include "Log.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "StringFormat.h" #include "Tokenize.h" @@ -515,6 +516,10 @@ bool Acore::Impl::ChatCommands::ChatCommandNode::IsInvokerVisible(ChatHandler co if (who.IsConsole() && (_permission.AllowConsole == Acore::ChatCommands::Console::Yes)) return true; + // RBAC permissions start at 200, SEC_* levels are 0-4 + if (_permission.RequiredLevel >= rbac::RBAC_PERM_COMMAND_RBAC) + return who.HasPermission(_permission.RequiredLevel); + return !who.IsConsole() && who.IsAvailable(_permission.RequiredLevel); } diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 9da1f9199..5422da2ec 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -34,6 +34,7 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "SharedDefines.h" #include "SocialMgr.h" @@ -493,7 +494,9 @@ namespace lfg uint32 lockData = 0; - if (dungeon->expansion > expansion || (onlySeasonalBosses && !dungeon->seasonal)) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_DUNGEON_FINDER)) + lockData = LFG_LOCKSTATUS_RAID_LOCKED; + else if (dungeon->expansion > expansion || (onlySeasonalBosses && !dungeon->seasonal)) lockData = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION; else if (IsDungeonDisabled(dungeon->map, dungeon->difficulty)) lockData = LFG_LOCKSTATUS_RAID_LOCKED; @@ -698,7 +701,11 @@ namespace lfg if (!isRaid && joinData.result == LFG_JOIN_OK) { // Check player or group member restrictions - if (player->InBattleground() || (player->InBattlegroundQueue() && !sWorld->getBoolConfig(CONFIG_ALLOW_JOIN_BG_AND_LFG))) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_DUNGEON_FINDER)) + { + joinData.result = LFG_JOIN_NOT_MEET_REQS; + } + else if (player->InBattleground() || (player->InBattlegroundQueue() && !sWorld->getBoolConfig(CONFIG_ALLOW_JOIN_BG_AND_LFG))) { joinData.result = LFG_JOIN_USING_BG_SYSTEM; } @@ -721,7 +728,11 @@ namespace lfg { if (Player* plrg = itr->GetSource()) { - if (plrg->HasAura(LFG_SPELL_DUNGEON_DESERTER)) + if (!plrg->GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_DUNGEON_FINDER)) + { + joinData.result = LFG_JOIN_PARTY_NOT_MEET_REQS; + } + else if (plrg->HasAura(LFG_SPELL_DUNGEON_DESERTER)) { joinData.result = LFG_JOIN_PARTY_DESERTER; } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 0739c1d3b..37d347b5e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -66,6 +66,7 @@ #include "Pet.h" #include "PetitionMgr.h" #include "QuestDef.h" +#include "RBAC.h" #include "Realm.h" #include "ReputationMgr.h" #include "ScriptMgr.h" @@ -175,7 +176,7 @@ Player::Player(WorldSession* session): Unit(), m_mover(this), _cinematicMgr(*thi //m_pad = 0; // players always accept - if (AccountMgr::IsPlayerAccount(GetSession()->GetSecurity())) + if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS)) SetAcceptWhispers(true); m_usedTalentCount = 0; @@ -573,7 +574,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo ? sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) : sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL); - if (!AccountMgr::IsPlayerAccount(GetSession()->GetSecurity())) + if (GetSession()->HasPermission(rbac::RBAC_PERM_USE_START_GM_LEVEL)) { uint32 gm_level = sWorld->getIntConfig(CONFIG_START_GM_LEVEL); if (gm_level > start_level) @@ -1312,7 +1313,7 @@ void Player::ToggleAFK() ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK); // afk player not allowed in battleground - if (!IsGameMaster() && isAFK() && InBattleground()) + if (!GetSession()->HasPermission(rbac::RBAC_PERM_CAN_AFK_ON_BATTLEGROUND) && isAFK() && InBattleground() && !InArena()) LeaveBattleground(); } @@ -1358,7 +1359,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati return false; } - if (AccountMgr::IsPlayerAccount(GetSession()->GetSecurity()) && sDisableMgr->IsDisabledFor(DISABLE_TYPE_MAP, mapid, this)) + if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_DISABLE_MAP) && sDisableMgr->IsDisabledFor(DISABLE_TYPE_MAP, mapid, this)) { LOG_ERROR("entities.player", "Player ({}, name: {}) tried to enter a forbidden map {}", GetGUID().ToString(), GetName(), mapid); SendTransferAborted(mapid, TRANSFER_ABORT_MAP_NOT_ALLOWED); @@ -2188,6 +2189,11 @@ bool Player::IsInAreaTriggerRadius(AreaTrigger const* trigger, float delta) cons return true; } +bool Player::CanBeGameMaster() const +{ + return GetSession()->HasPermission(rbac::RBAC_PERM_COMMAND_GM); +} + void Player::SetGameMaster(bool on) { if (on) @@ -2540,10 +2546,15 @@ void Player::InitTalentForLevel() { uint32 talentPointsForLevel = CalculateTalentsPoints(); - // xinef: more talent points that we have are used, reset + // if used more than have then reset if (m_usedTalentCount > talentPointsForLevel) - resetTalents(true); - // xinef: else, recalculate free talent points count + { + if (!GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_MORE_TALENTS_THAN_ALLOWED)) + resetTalents(true); + else + SetFreeTalentPoints(0); + } + // else update amount of free points else SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount); @@ -9427,7 +9438,7 @@ void Player::TextEmote(std::string_view text, WorldObject const* /*= nullptr*/, SendDirectMessage(&data); // Special handling for messages, do not use visibility map for stealthed units - Acore::MessageDistDeliverer notifier(this, &data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE), nullptr, true); + Acore::MessageDistDeliverer notifier(this, &data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), !GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHAT), nullptr, true); Cell::VisitObjects(this, notifier, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE)); } @@ -11293,13 +11304,17 @@ void Player::LeaveBattleground(Battleground* bg) TeleportToEntryPoint(); } -bool Player::CanJoinToBattleground() const +bool Player::CanJoinToBattleground(Battleground const* bg) const { // check Deserter debuff if (HasAura(26013)) return false; - return true; + if (bg->isArena()) + return GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_ARENAS); + if (bg->GetBgTypeID() == BATTLEGROUND_RB) + return GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_RANDOM_BG); + return GetSession()->HasPermission(rbac::RBAC_PERM_JOIN_NORMAL_BG); } bool Player::CanReportAfkDueToLimit() diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 2eb10a110..1b1ca057f 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1172,6 +1172,7 @@ public: [[nodiscard]] bool isAcceptWhispers() const { return m_ExtraFlags & PLAYER_EXTRA_ACCEPT_WHISPERS; } void SetAcceptWhispers(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_ACCEPT_WHISPERS; else m_ExtraFlags &= ~PLAYER_EXTRA_ACCEPT_WHISPERS; } [[nodiscard]] bool IsGameMaster() const { return m_ExtraFlags & PLAYER_EXTRA_GM_ON; } + [[nodiscard]] bool CanBeGameMaster() const; void SetGameMaster(bool on); [[nodiscard]] bool isGMChat() const { return m_ExtraFlags & PLAYER_EXTRA_GM_CHAT; } void SetGMChat(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_GM_CHAT; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_CHAT; } @@ -2313,7 +2314,7 @@ public: [[nodiscard]] TeamId GetBgTeamId() const { return m_bgData.bgTeamId != TEAM_NEUTRAL ? m_bgData.bgTeamId : GetTeamId(); } void LeaveBattleground(Battleground* bg = nullptr); - [[nodiscard]] bool CanJoinToBattleground() const; + [[nodiscard]] bool CanJoinToBattleground(Battleground const* bg) const; bool CanReportAfkDueToLimit(); void ReportedAfkBy(Player* reporter); void ClearAfkReports() { m_bgData.bgAfkReporter.clear(); } diff --git a/src/server/game/Entities/Player/PlayerMisc.cpp b/src/server/game/Entities/Player/PlayerMisc.cpp index 91459d848..91a3fa93c 100644 --- a/src/server/game/Entities/Player/PlayerMisc.cpp +++ b/src/server/game/Entities/Player/PlayerMisc.cpp @@ -20,6 +20,7 @@ #include "InstancePackets.h" #include "MapMgr.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "WorldSession.h" @@ -30,7 +31,7 @@ void Player::UpdateSpeakTime(ChatFloodThrottle::Index index) { // ignore chat spam protection for GMs in any mode - if (!AccountMgr::IsPlayerAccount(GetSession()->GetSecurity())) + if (GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_SPAM)) return; uint32 limit, delay; diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index 843d1118e..51ff08982 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -49,6 +49,7 @@ #include "Player.h" #include "QueryHolder.h" #include "QuestDef.h" +#include "RBAC.h" #include "ReputationMgr.h" #include "ScriptMgr.h" #include "ScriptObjectFwd.h" @@ -5024,7 +5025,9 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons m_name = fields[2].Get(); // check name limitations - if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS) + uint8 nameResult = ObjectMgr::CheckPlayerName(m_name); + if (nameResult != CHAR_NAME_SUCCESS && + !(nameResult == CHAR_NAME_RESERVED && GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME))) { CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_ADD_AT_LOGIN_FLAG); stmt->SetData(0, uint16(AT_LOGIN_RENAME)); @@ -5568,7 +5571,7 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons outDebugValues(); // GM state - if (!AccountMgr::IsPlayerAccount(GetSession()->GetSecurity())) + if (GetSession()->HasPermission(rbac::RBAC_PERM_RESTORE_SAVED_GM_STATE)) { switch (sWorld->getIntConfig(CONFIG_GM_LOGIN_STATE)) { diff --git a/src/server/game/Entities/Player/SocialMgr.cpp b/src/server/game/Entities/Player/SocialMgr.cpp index ed59470dd..46fc3a416 100644 --- a/src/server/game/Entities/Player/SocialMgr.cpp +++ b/src/server/game/Entities/Player/SocialMgr.cpp @@ -20,6 +20,7 @@ #include "DatabaseEnv.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "Util.h" #include "WorldPacket.h" #include "WorldSession.h" @@ -224,8 +225,6 @@ void SocialMgr::GetFriendInfo(Player* player, ObjectGuid const& friendGUID, Frie return; TeamId teamId = player->GetTeamId(); - AccountTypes security = player->GetSession()->GetSecurity(); - bool allowTwoSideWhoList = sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); AccountTypes gmLevelInWhoList = AccountTypes(sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST)); auto const& itr = player->GetSocial()->m_playerSocialMap.find(friendGUID); @@ -234,7 +233,14 @@ void SocialMgr::GetFriendInfo(Player* player, ObjectGuid const& friendGUID, Frie // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if ((!AccountMgr::IsPlayerAccount(security) || ((pFriend->GetTeamId() == teamId || allowTwoSideWhoList) && pFriend->GetSession()->GetSecurity() <= gmLevelInWhoList)) && pFriend->IsVisibleGloballyFor(player)) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS) && + pFriend->GetSession()->GetSecurity() > gmLevelInWhoList) + return; + + if (pFriend->GetTeamId() != teamId && !player->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_WHO_LIST)) + return; + + if (pFriend->IsVisibleGloballyFor(player)) { friendInfo.Status = FRIEND_STATUS_ONLINE; if (pFriend->isAFK()) @@ -297,8 +303,6 @@ void SocialMgr::BroadcastToFriendListers(Player* player, WorldPacket* packet) return; TeamId teamId = player->GetTeamId(); - AccountTypes security = player->GetSession()->GetSecurity(); - bool allowTwoSideWhoList = sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); AccountTypes gmLevelInWhoList = AccountTypes(sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST)); for (auto const& itr : m_socialMap) @@ -307,10 +311,19 @@ void SocialMgr::BroadcastToFriendListers(Player* player, WorldPacket* packet) if (itr2 != itr.second.m_playerSocialMap.end() && (itr2->second.Flags & SOCIAL_FLAG_FRIEND)) { Player* pFriend = ObjectAccessor::FindPlayer(itr.first); + if (!pFriend) + continue; + WorldSession* session = pFriend->GetSession(); // PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters // MODERATOR, GAME MASTER, ADMINISTRATOR can see all - if (pFriend && (!AccountMgr::IsPlayerAccount(pFriend->GetSession()->GetSecurity()) || ((pFriend->GetTeamId() == teamId || allowTwoSideWhoList) && security <= gmLevelInWhoList)) && player->IsVisibleGloballyFor(pFriend)) + if (!session->HasPermission(rbac::RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS) && player->GetSession()->GetSecurity() > gmLevelInWhoList) + continue; + + if (pFriend->GetTeamId() != teamId && !session->HasPermission(rbac::RBAC_PERM_TWO_SIDE_WHO_LIST)) + continue; + + if (player->IsVisibleGloballyFor(pFriend)) pFriend->SendDirectMessage(packet); } } diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index efb0a28e0..25ecb1cd2 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -1985,7 +1985,7 @@ GroupJoinBattlegroundResult Group::CanJoinBattlegroundQueue(Battleground const* return ERR_BATTLEGROUND_JOIN_RANGE_INDEX; // check for deserter debuff in case not arena queue - if (bgTemplate->GetBgTypeID() != BATTLEGROUND_AA && !member->CanJoinToBattleground()) + if (bgTemplate->GetBgTypeID() != BATTLEGROUND_AA && !member->CanJoinToBattleground(bgTemplate)) return ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS; // check if someone in party is using dungeon system diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index 7ca122b9d..abd12702d 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -31,6 +31,7 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "SocialMgr.h" #include "World.h" @@ -903,6 +904,13 @@ void Guild::BankMoveItemData::LogBankEvent(CharacterDatabaseTransaction trans, M void Guild::BankMoveItemData::LogAction(MoveItemData* pFrom) const { MoveItemData::LogAction(pFrom); + if (!pFrom->IsBank() && m_pPlayer->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(m_pPlayer->GetSession()->GetAccountId(), "GM {} (Account: {}) deposit item: {} (Entry: {} Count: {}) to guild bank named: {} (Guild ID: {})", + m_pPlayer->GetName(), m_pPlayer->GetSession()->GetAccountId(), + pFrom->GetItem()->GetTemplate()->Name1, pFrom->GetItem()->GetEntry(), pFrom->GetItem()->GetCount(), + m_pGuild->GetName(), m_pGuild->GetId()); + } } Item* Guild::BankMoveItemData::_StoreItem(CharacterDatabaseTransaction trans, BankTab* pTab, Item* pItem, ItemPosCount& pos, bool clone) const @@ -1683,6 +1691,12 @@ void Guild::HandleMemberDepositMoney(WorldSession* session, uint32 amount) CharacterDatabase.CommitTransaction(trans); + if (session->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(session->GetAccountId(), "GM {} (Account: {}) deposited {} to guild bank (Guild: {} GuildId: {})", + player->GetName(), session->GetAccountId(), amount, GetName(), GetId()); + } + std::string aux = Acore::Impl::ByteArrayToHexStr(reinterpret_cast(&m_bankMoney), 8, true); _BroadcastEvent(GE_BANK_MONEY_SET, ObjectGuid::Empty, aux.c_str()); @@ -1737,6 +1751,12 @@ bool Guild::HandleMemberWithdrawMoney(WorldSession* session, uint32 amount, bool _LogBankEvent(trans, repair ? GUILD_BANK_LOG_REPAIR_MONEY : GUILD_BANK_LOG_WITHDRAW_MONEY, uint8(0), player->GetGUID(), amount); CharacterDatabase.CommitTransaction(trans); + if (session->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(session->GetAccountId(), "GM {} (Account: {}) withdrew {} from guild bank (Guild: {} GuildId: {})", + player->GetName(), session->GetAccountId(), amount, GetName(), GetId()); + } + if (amount > 10 * GOLD) // sender_acc = 0 (guild has no account), sender_guid = Guild id, sender_name = Guild name CharacterDatabase.Execute("INSERT INTO log_money VALUES({}, {}, \"{}\", \"{}\", {}, \"{}\", {}, \"(guild, members: {}, new amount: {}, leader guid low: {}, withdrawer level: {})\", NOW(), {})", 0, GetId(), GetName(), session->GetRemoteAddress(), session->GetAccountId(), player->GetName(), amount, GetMemberCount(), GetTotalBankMoney(), GetLeaderGUID().GetCounter(), player->GetLevel(), 4); diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 5c0329186..78cf20cbd 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -24,6 +24,7 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "World.h" #include "WorldPacket.h" @@ -321,6 +322,13 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, ERR_AUCTION_OK); + if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(GetAccountId(), "GM {} (Account: {}) created auction: {} (Item: {} Count: {}) Bid: {} Buyout: {}", + _player->GetName(), GetAccountId(), AH->Id, + item->GetTemplate()->Name1, item->GetCount(), bid, buyout); + } + GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1); LOG_INFO("entities.player.auctionhouse", "AuctionHouse: Account: {} (IP: {}), Player [{}] (GUID: {}) created auction #{}: Item '{}' (Entry: {}) x{}, StartBid: {} copper, Buyout: {} copper, Deposit: {} copper", @@ -388,6 +396,13 @@ void WorldSession::HandleAuctionSellItem(WorldPacket& recvData) SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, ERR_AUCTION_OK); + if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(GetAccountId(), "GM {} (Account: {}) created auction: {} (Item: {} Count: {}) Bid: {} Buyout: {}", + _player->GetName(), GetAccountId(), AH->Id, + newItem->GetTemplate()->Name1, newItem->GetCount(), bid, buyout); + } + GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1); LOG_INFO("entities.player.auctionhouse", "AuctionHouse: Account: {} (IP: {}), Player [{}] (GUID: {}) created auction #{}: Item '{}' (Entry: {}) x{}, StartBid: {} copper, Buyout: {} copper, Deposit: {} copper", @@ -501,6 +516,13 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, ERR_AUCTION_OK, 0); + if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(GetAccountId(), "GM {} (Account: {}) bid on auction: {} (Item: {} Count: {}) Bid: {}", + player->GetName(), GetAccountId(), auction->Id, + auction->item_template, auction->itemCount, price); + } + LOG_INFO("entities.player.auctionhouse", "AuctionHouse: Account: {} (IP: {}), Player [{}] (GUID: {}) placed bid on auction #{}: Item (Entry: {}) x{}, Bid: {} copper, Owner: {} (GUID: {})", GetAccountId(), GetRemoteAddress(), player->GetName(), player->GetGUID().GetCounter(), auction->Id, auction->item_template, auction->itemCount, price, auction->owner.GetCounter(), auction->owner.GetCounter()); @@ -528,6 +550,13 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket& recvData) SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, ERR_AUCTION_OK); + if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(GetAccountId(), "GM {} (Account: {}) bought out auction: {} (Item: {} Count: {}) Buyout: {}", + player->GetName(), GetAccountId(), auction->Id, + auction->item_template, auction->itemCount, auction->buyout); + } + LOG_INFO("entities.player.auctionhouse", "AuctionHouse: Account: {} (IP: {}), Player [{}] (GUID: {}) bought out auction #{}: Item (Entry: {}) x{}, Buyout: {} copper, Owner: {} (GUID: {})", GetAccountId(), GetRemoteAddress(), player->GetName(), player->GetGUID().GetCounter(), auction->Id, auction->item_template, auction->itemCount, auction->buyout, auction->owner.GetCounter(), auction->owner.GetCounter()); diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index 499bae1e6..f9cbfac32 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -161,7 +161,7 @@ void WorldSession::HandleBattlemasterJoinOpcode(WorldPacket& recvData) { err = ERR_LFG_CANT_USE_BATTLEGROUND; } - else if (!_player->CanJoinToBattleground()) // has deserter debuff + else if (!_player->CanJoinToBattleground(bg)) // has deserter debuff { err = ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS; } @@ -473,7 +473,7 @@ void WorldSession::HandleBattleFieldPortOpcode(WorldPacket& recvData) if (action == 1 && ginfo.ArenaType == 0) { // can't join with deserter, check it here right before joining to be sure - if (!_player->CanJoinToBattleground()) + if (!_player->CanJoinToBattleground(bg)) { WorldPacket data; sBattlegroundMgr->BuildGroupJoinedBattlegroundPacket(&data, ERR_GROUP_JOIN_BATTLEGROUND_DESERTERS); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 40942b333..b700205fa 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -17,6 +17,7 @@ #include "AccountMgr.h" #include "AreaDefines.h" +#include "RBAC.h" #include "ArenaTeamMgr.h" #include "AuctionHouseMgr.h" #include "Battleground.h" @@ -285,7 +286,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) >> createInfo->FacialHair >> createInfo->OutfitId; - if (AccountMgr::IsPlayerAccount(GetSecurity())) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_TEAMMASK)) { if (uint32 mask = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED)) { @@ -329,7 +330,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) return; } - if (AccountMgr::IsPlayerAccount(GetSecurity())) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RACEMASK)) { uint32 raceMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK); if ((1 << (createInfo->Race - 1)) & raceMaskDisabled) @@ -337,7 +338,10 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) SendCharCreate(CHAR_CREATE_DISABLED); return; } + } + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_CLASSMASK)) + { uint32 classMaskDisabled = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK); if ((1 << (createInfo->Class - 1)) & classMaskDisabled) { @@ -358,13 +362,16 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) uint8 res = ObjectMgr::CheckPlayerName(createInfo->Name, true); if (res != CHAR_NAME_SUCCESS) { - SendCharCreate(ResponseCodes(res)); - return; + if (res != CHAR_NAME_RESERVED || !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME)) + { + SendCharCreate(ResponseCodes(res)); + return; + } } // speedup check for heroic class disabled case uint32 heroic_free_slots = sWorld->getIntConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM); - if (heroic_free_slots == 0 && AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT) + if (heroic_free_slots == 0 && !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_DEATH_KNIGHT) && createInfo->Class == CLASS_DEATH_KNIGHT) { SendCharCreate(CHAR_CREATE_UNIQUE_CLASS_LIMIT); return; @@ -372,7 +379,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) // speedup check for heroic class disabled case uint32 req_level_for_heroic = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); - if (AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_DEATH_KNIGHT) && createInfo->Class == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) { SendCharCreate(CHAR_CREATE_LEVEL_REQUIREMENT); return; @@ -427,7 +434,7 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) } } - bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity()); + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || HasPermission(rbac::RBAC_PERM_TWO_SIDE_CHARACTER_CREATION); uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); std::function finalizeCharacterCreation = [this, createInfo](PreparedQueryResult result) @@ -440,9 +447,9 @@ void WorldSession::HandleCharCreateOpcode(WorldPacket& recvData) bool haveSameRace = false; uint32 heroicReqLevel = sWorld->getIntConfig(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER); bool hasHeroicReqLevel = (heroicReqLevel == 0); - bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || !AccountMgr::IsPlayerAccount(GetSecurity()); + bool allowTwoSideAccounts = !sWorld->IsPvPRealm() || sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS) || HasPermission(rbac::RBAC_PERM_TWO_SIDE_CHARACTER_CREATION); uint32 skipCinematics = sWorld->getIntConfig(CONFIG_SKIP_CINEMATICS); - bool checkDeathKnightReqs = AccountMgr::IsPlayerAccount(GetSecurity()) && createInfo->Class == CLASS_DEATH_KNIGHT; + bool checkDeathKnightReqs = !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_DEATH_KNIGHT) && createInfo->Class == CLASS_DEATH_KNIGHT; if (result) { @@ -1365,8 +1372,11 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recvData) uint8 res = ObjectMgr::CheckPlayerName(renameInfo->Name, true); if (res != CHAR_NAME_SUCCESS) { - SendCharRename(ResponseCodes(res), renameInfo.get()); - return; + if (res != CHAR_NAME_RESERVED || !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME)) + { + SendCharRename(ResponseCodes(res), renameInfo.get()); + return; + } } // Ensure that the character belongs to the current account, that rename at login is enabled @@ -1705,8 +1715,11 @@ void WorldSession::HandleCharCustomizeCallback(std::shared_ptr(ObjectMgr::CheckPlayerName(customizeInfo->Name, true)); if (res != CHAR_NAME_SUCCESS) { - SendCharCustomize(res, customizeInfo.get()); - return; + if (res != CHAR_NAME_RESERVED || !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME)) + { + SendCharCustomize(res, customizeInfo.get()); + return; + } } // character with this name already exist @@ -2084,7 +2097,7 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptrgetIntConfig(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK); if ((1 << (factionChangeInfo->Race - 1)) & raceMaskDisabled) @@ -2104,8 +2117,11 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptr(ObjectMgr::CheckPlayerName(factionChangeInfo->Name, true)); if (res != CHAR_NAME_SUCCESS) { - SendCharFactionChange(res, factionChangeInfo.get()); - return; + if (res != CHAR_NAME_RESERVED || !HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_RESERVEDNAME)) + { + SendCharFactionChange(res, factionChangeInfo.get()); + return; + } } // character with this name already exist @@ -2321,7 +2337,7 @@ void WorldSession::HandleCharFactionOrRaceChangeCallback(std::shared_ptrDeleteMember(factionChangeInfo->Guid, false, false, true); } - if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND)) + if (!HasPermission(rbac::RBAC_PERM_TWO_SIDE_ADD_FRIEND)) { // Delete Friend List stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SOCIAL_BY_GUID); diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index f191dc05a..6b120f925 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -17,6 +17,7 @@ #include "AccountMgr.h" #include "CellImpl.h" +#include "RBAC.h" #include "ChannelMgr.h" #include "Chat.h" #include "ChatPackets.h" @@ -112,7 +113,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) } // pussywizard: chatting on most chat types requires 2 hours played to prevent spam/abuse - if (AccountMgr::IsPlayerAccount(GetSecurity())) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_CHANNEL_REQ)) { switch (type) { @@ -382,8 +383,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) } Player* receiver = ObjectAccessor::FindPlayerByName(to, false); - bool senderIsPlayer = AccountMgr::IsPlayerAccount(GetSecurity()); - bool receiverIsPlayer = AccountMgr::IsPlayerAccount(receiver ? receiver->GetSession()->GetSecurity() : SEC_PLAYER); + bool senderIsPlayer = !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHAT); + bool receiverIsPlayer = receiver ? !receiver->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHAT) : true; if (sender->GetLevel() < sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ) && receiver != sender && receiver && !receiver->IsGameMaster()) { @@ -391,7 +392,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) return; } - if (!receiver || (senderIsPlayer && !receiverIsPlayer && !receiver->isAcceptWhispers() && !receiver->IsInWhisperWhiteList(sender->GetGUID()))) + if (!receiver || (lang != LANG_ADDON && !receiver->isAcceptWhispers() && receiver->GetSession()->HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS) && !receiver->IsInWhisperWhiteList(sender->GetGUID()))) { SendPlayerNotFoundNotice(to); return; @@ -404,7 +405,6 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) return; } - // pussywizard: optimization if (GetPlayer()->HasAura(1852) && !receiver->IsGameMaster()) { ChatHandler(this).SendNotification(LANG_GM_SILENCE, GetPlayer()->GetName()); @@ -412,7 +412,9 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) } // If player is a Gamemaster and doesn't accept whisper, we auto-whitelist every player that the Gamemaster is talking to - if (!senderIsPlayer && !sender->isAcceptWhispers() && !sender->IsInWhisperWhiteList(receiver->GetGUID())) + // We also do that if a player is under the required level for whispers. + if (receiver->GetLevel() < sWorld->getIntConfig(CONFIG_CHAT_WHISPER_LEVEL_REQ) || + (HasPermission(rbac::RBAC_PERM_CAN_FILTER_WHISPERS) && !sender->isAcceptWhispers() && !sender->IsInWhisperWhiteList(receiver->GetGUID()))) sender->AddWhisperWhiteList(receiver->GetGUID()); GetPlayer()->Whisper(msg, Language(lang), receiver); @@ -554,7 +556,7 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) break; case CHAT_MSG_CHANNEL: { - if (AccountMgr::IsPlayerAccount(GetSecurity())) + if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_CHANNEL_REQ)) { if (sender->GetLevel() < sWorld->getIntConfig(CONFIG_CHAT_CHANNEL_LEVEL_REQ)) { diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 387a69238..b7083c0fa 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -17,6 +17,7 @@ #include "AccountMgr.h" #include "Chat.h" +#include "RBAC.h" #include "CharacterCache.h" #include "DBCStores.h" #include "DatabaseEnv.h" @@ -38,9 +39,9 @@ bool WorldSession::CanOpenMailBox(ObjectGuid guid) { if (guid == _player->GetGUID()) { - if (_player->GetSession()->GetSecurity() < SEC_MODERATOR) + if (!HasPermission(rbac::RBAC_PERM_COMMAND_MAILBOX)) { - LOG_ERROR("network.opcode", "{} attempt open mailbox in cheating way.", _player->GetName()); + LOG_WARN("cheat", "{} attempted to open mailbox by using a cheat.", _player->GetName()); return false; } } @@ -213,7 +214,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) uint32 rc_account = receive ? receive->GetSession()->GetAccountId() : sCharacterCache->GetCharacterAccountIdByGuid(receiverGuid); - if (/*!accountBound*/ GetAccountId() != rc_account && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeamId() != rc_teamId && AccountMgr::IsPlayerAccount(GetSecurity())) + if (/*!accountBound*/ GetAccountId() != rc_account && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL) && player->GetTeamId() != rc_teamId && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_MAIL)) { player->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM); return; @@ -286,6 +287,25 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) player->SendMailResult(0, MAIL_SEND, MAIL_OK); + if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + if (items_count > 0) + { + for (uint8 i = 0; i < items_count; ++i) + { + Item* item = items[i]; + LOG_GM(GetAccountId(), "GM {} (Account: {}) sent mail to {} (Account: {}) with item: {} (Entry: {} Count: {})", + player->GetName(), GetAccountId(), receiver, rc_account, + item->GetTemplate()->Name1, item->GetEntry(), item->GetCount()); + } + } + if (money > 0) + { + LOG_GM(GetAccountId(), "GM {} (Account: {}) sent mail to {} (Account: {}) with money: {}", + player->GetName(), GetAccountId(), receiver, rc_account, money); + } + } + player->ModifyMoney(-int32(reqmoney)); player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost); @@ -585,6 +605,12 @@ void WorldSession::HandleMailTakeItem(WorldPacket& recvData) it->SetState(ITEM_UNCHANGED); // need to set this state, otherwise item cannot be removed later, if neccessary player->MoveItemToInventory(dest, it, true); + if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(GetAccountId(), "GM {} (Account: {}) took mail item: {} (Entry: {} Count: {}) from mailbox", + player->GetName(), GetAccountId(), it->GetTemplate()->Name1, it->GetEntry(), count); + } + player->SaveInventoryAndGoldToDB(trans); player->_SaveMail(trans); CharacterDatabase.CommitTransaction(trans); @@ -620,6 +646,12 @@ void WorldSession::HandleMailTakeMoney(WorldPacket& recvData) return; } + if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(GetAccountId(), "GM {} (Account: {}) took mail money: {} from mailbox", + player->GetName(), GetAccountId(), m->money); + } + m->money = 0; m->state = MAIL_STATE_CHANGED; player->m_mailsUpdated = true; diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 56047089f..30886a778 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -17,6 +17,7 @@ #include "AccountMgr.h" #include "BattlefieldMgr.h" +#include "RBAC.h" #include "Battleground.h" #include "BattlegroundMgr.h" #include "CharacterPackets.h" @@ -283,8 +284,6 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) levelMax = STRONG_MAX_LEVEL; uint32 team = _player->GetTeamId(); - uint32 security = GetSecurity(); - bool allowTwoSideWhoList = sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST); uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); uint32 displaycount = 0; @@ -294,20 +293,13 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) for (auto const& target : sWhoListCacheMgr->GetWhoList()) { - if (AccountMgr::IsPlayerAccount(security)) - { - // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST - if (target.GetTeamId() != team && !allowTwoSideWhoList) - { - continue; - } + // player can see member of other team only if CONFIG_ALLOW_TWO_SIDE_WHO_LIST + if (target.GetTeamId() != team && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_WHO_LIST)) + continue; - // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST - if (target.GetSecurity() > AccountTypes(gmLevelInWhoList)) - { - continue; - } - } + // player can see MODERATOR, GAME MASTER, ADMINISTRATOR only if CONFIG_GM_IN_WHO_LIST + if (!HasPermission(rbac::RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS) && target.GetSecurity() > AccountTypes(gmLevelInWhoList)) + continue; // check if target is globally visible for player if ((_player->GetGUID() != target.GetGuid() && !target.IsVisible()) && @@ -429,7 +421,7 @@ void WorldSession::HandleLogoutRequestOpcode(WorldPackets::Character::LogoutRequ if (ObjectGuid lguid = GetPlayer()->GetLootGUID()) DoLootRelease(lguid); - bool instantLogout = ((GetSecurity() >= 0 && uint32(GetSecurity()) >= sWorld->getIntConfig(CONFIG_INSTANT_LOGOUT)) + bool instantLogout = (HasPermission(rbac::RBAC_PERM_INSTANT_LOGOUT) || (GetPlayer()->HasPlayerFlag(PLAYER_FLAGS_RESTING) && !GetPlayer()->IsInCombat())) || GetPlayer()->IsInFlight(); bool preventAfkSanctuaryLogout = sWorld->getIntConfig(CONFIG_AFK_PREVENT_LOGOUT) == 1 @@ -1010,7 +1002,7 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recv_data) WorldPacket data(SMSG_INSPECT_TALENT, guid_size + 4 + talent_points); data << player->GetPackGUID(); - if (sWorld->getBoolConfig(CONFIG_TALENTS_INSPECTING) || _player->IsGameMaster()) + if (sWorld->getBoolConfig(CONFIG_TALENTS_INSPECTING) || _player->CanBeGameMaster()) { player->BuildPlayerTalentsInfoData(&data); } @@ -1081,7 +1073,7 @@ void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recv_data) LOG_DEBUG("network", "CMSG_WORLD_TELEPORT: Player = {}, Time = {}, map = {}, x = {}, y = {}, z = {}, o = {}", GetPlayer()->GetName(), time, mapid, PositionX, PositionY, PositionZ, Orientation); - if (AccountMgr::IsAdminAccount(GetSecurity())) + if (HasPermission(rbac::RBAC_PERM_OPCODE_WORLD_TELEPORT)) GetPlayer()->TeleportTo(mapid, PositionX, PositionY, PositionZ, Orientation); else ChatHandler(this).SendNotification(LANG_PERMISSION_DENIED); @@ -1093,7 +1085,7 @@ void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data) std::string charname; recv_data >> charname; - if (!AccountMgr::IsAdminAccount(GetSecurity())) + if (!HasPermission(rbac::RBAC_PERM_OPCODE_WHOIS)) { ChatHandler(this).SendNotification(LANG_PERMISSION_DENIED); return; diff --git a/src/server/game/Handlers/Socialhandler.cpp b/src/server/game/Handlers/Socialhandler.cpp index 6fccfa1d3..23038746e 100644 --- a/src/server/game/Handlers/Socialhandler.cpp +++ b/src/server/game/Handlers/Socialhandler.cpp @@ -17,6 +17,7 @@ #include "AccountMgr.h" #include "Language.h" +#include "RBAC.h" #include "Log.h" #include "ObjectAccessor.h" #include "ObjectMgr.h" @@ -57,13 +58,13 @@ void WorldSession::HandleAddFriendOpcode(WorldPacket& recv_data) TeamId teamId = Player::TeamIdForRace(playerData->Race); FriendsResult friendResult = FRIEND_NOT_FOUND; - if (!AccountMgr::IsPlayerAccount(GetSecurity()) || sWorld->getBoolConfig(CONFIG_ALLOW_GM_FRIEND)|| AccountMgr::IsPlayerAccount(AccountMgr::GetSecurity(friendAccountId, realm.Id.Realm))) + if (HasPermission(rbac::RBAC_PERM_ALLOW_GM_FRIEND) || sWorld->getBoolConfig(CONFIG_ALLOW_GM_FRIEND) || AccountMgr::IsPlayerAccount(AccountMgr::GetSecurity(friendAccountId, realm.Id.Realm))) { if (friendGuid) { if (friendGuid == GetPlayer()->GetGUID()) friendResult = FRIEND_SELF; - else if (GetPlayer()->GetTeamId() != teamId && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && AccountMgr::IsPlayerAccount(GetSecurity())) + else if (GetPlayer()->GetTeamId() != teamId && !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND) && !HasPermission(rbac::RBAC_PERM_TWO_SIDE_ADD_FRIEND)) friendResult = FRIEND_ENEMY; else if (GetPlayer()->GetSocial()->HasFriend(friendGuid)) friendResult = FRIEND_ALREADY; diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp index 8fbe3018e..8266d13d1 100644 --- a/src/server/game/Handlers/TradeHandler.cpp +++ b/src/server/game/Handlers/TradeHandler.cpp @@ -22,6 +22,7 @@ #include "ObjectAccessor.h" #include "Opcodes.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "SocialMgr.h" #include "Spell.h" @@ -504,6 +505,35 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) trader->ModifyMoney(-int32(his_trade->GetMoney())); trader->ModifyMoney(my_trade->GetMoney()); + if (HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) + { + if (myItems[i]) + LOG_GM(GetAccountId(), "GM {} (Account: {}) traded item: {} (Entry: {} Count: {}) to {}", + _player->GetName(), GetAccountId(), + myItems[i]->GetTemplate()->Name1, myItems[i]->GetEntry(), myItems[i]->GetCount(), + trader->GetName()); + } + if (my_trade->GetMoney() > 0) + LOG_GM(GetAccountId(), "GM {} (Account: {}) traded money: {} to {}", + _player->GetName(), GetAccountId(), my_trade->GetMoney(), trader->GetName()); + } + if (trader->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i) + { + if (hisItems[i]) + LOG_GM(trader->GetSession()->GetAccountId(), "GM {} (Account: {}) traded item: {} (Entry: {} Count: {}) to {}", + trader->GetName(), trader->GetSession()->GetAccountId(), + hisItems[i]->GetTemplate()->Name1, hisItems[i]->GetEntry(), hisItems[i]->GetCount(), + _player->GetName()); + } + if (his_trade->GetMoney() > 0) + LOG_GM(trader->GetSession()->GetAccountId(), "GM {} (Account: {}) traded money: {} to {}", + trader->GetName(), trader->GetSession()->GetAccountId(), his_trade->GetMoney(), _player->GetName()); + } + // log completed trade LOG_INFO("entities.player.trade", "Trade: Account: {} (IP: {}), Player [{}] ({}) traded with Player [{}] ({}): gave {} copper, received {} copper, gave item(s) [{}], received item(s) [{}]", GetAccountId(), GetRemoteAddress(), _player->GetName(), _player->GetGUID().GetCounter(), @@ -671,7 +701,9 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) return; } - if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeamId() != _player->GetTeamId()) + if (pOther->GetTeamId() != _player->GetTeamId() && + !sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && + !GetPlayer()->GetSession()->HasPermission(rbac::RBAC_PERM_ALLOW_TWO_SIDE_TRADE)) { info.Status = TRADE_STATUS_WRONG_FACTION; SendTradeStatus(info); diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp index 94b367e61..f4dec4454 100644 --- a/src/server/game/Instances/InstanceScript.cpp +++ b/src/server/game/Instances/InstanceScript.cpp @@ -28,6 +28,7 @@ #include "Opcodes.h" #include "Pet.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "Spell.h" #include "WorldSession.h" @@ -387,6 +388,11 @@ void InstanceScript::SetSummoner(Creature* creature) summoner->AI()->JustSummoned(creature); } +bool InstanceScript::_SkipCheckRequiredBosses(Player const* player /*= nullptr*/) const +{ + return player && player->GetSession()->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES); +} + bool InstanceScript::SetBossState(uint32 id, EncounterState state) { if (id < bosses.size()) diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h index ba05588ee..8b9c1e677 100644 --- a/src/server/game/Instances/InstanceScript.h +++ b/src/server/game/Instances/InstanceScript.h @@ -264,6 +264,8 @@ public: // Checks boss requirements (one boss required to kill other) virtual bool CheckRequiredBosses(uint32 /*bossId*/, Player const* /*player*/ = nullptr) const { return true; } + bool _SkipCheckRequiredBosses(Player const* player = nullptr) const; + void SetCompletedEncountersMask(uint32 newMask, bool save); // Returns completed encounters mask for packets diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 8ecc58b47..de55d37cd 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -112,7 +112,10 @@ enum AcoreStrings LANG_PVPSTATS = 80, LANG_PVPSTATS_DISABLED = 81, LANG_GENERIC_TWO_CURLIES_WITH_COLON = 82, - // Free 83 - 86 + LANG_RBAC_HELP_ACC_LIST = 83, + LANG_RBAC_HELP_ACC_GRANT = 84, + LANG_RBAC_HELP_ACC_DENY = 85, + LANG_RBAC_HELP_ACC_REVOKE = 86, LANG_UNKNOWN_ERROR = 87, LANG_2FA_COMMANDS_NOT_SETUP = 88, diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 131e39a30..408c89736 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -41,6 +41,7 @@ #include "PacketUtilities.h" #include "Pet.h" #include "Player.h" +#include "Realm.h" #include "QueryHolder.h" #include "ScriptMgr.h" #include "SocialMgr.h" @@ -115,6 +116,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, s _security(sec), _skipQueue(skipQueue), _accountId(id), + _RBACData(nullptr), _accountName(std::move(name)), _accountFlags(accountFlags), m_expansion(expansion), @@ -171,6 +173,8 @@ WorldSession::~WorldSession() m_Socket = nullptr; } + delete _RBACData; + ///- empty incoming packet queue WorldPacket* packet = nullptr; while (_recvQueue.next(packet)) @@ -360,7 +364,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) ///- Before we process anything: /// If necessary, kick the player because the client didn't send anything for too long /// (or they've been idling in character select) - if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket) + if (IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION)) m_Socket->CloseSocket(); if (updater.ProcessUnsafe()) @@ -1519,3 +1523,54 @@ void WorldSession::SetPacketLogging(bool state) if (m_Socket) m_Socket->SetPacketLogging(state); } + +void WorldSession::LoadPermissions() +{ + uint32 id = GetAccountId(); + uint8 secLevel = GetSecurity(); + + LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: {}, Name: {}, realmId: {}, secLevel: {}]", + id, _accountName, realm.Id.Realm, secLevel); + + _RBACData = new rbac::RBACData(id, _accountName, realm.Id.Realm, secLevel); + _RBACData->LoadFromDB(); +} + +QueryCallback WorldSession::LoadPermissionsAsync() +{ + uint32 id = GetAccountId(); + uint8 secLevel = GetSecurity(); + + LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: {}, Name: {}, realmId: {}, secLevel: {}]", + id, _accountName, realm.Id.Realm, secLevel); + + _RBACData = new rbac::RBACData(id, _accountName, realm.Id.Realm, secLevel); + return _RBACData->LoadFromDBAsync(); +} + +bool WorldSession::HasPermission(uint32 permission) +{ + if (!_RBACData) + LoadPermissions(); + + bool hasPermission = _RBACData->HasPermission(permission); + LOG_DEBUG("rbac", "WorldSession::HasPermission [AccountId: {}, Name: {}, realmId: {}]", + _RBACData->GetId(), _RBACData->GetName(), realm.Id.Realm); + + return hasPermission; +} + +void WorldSession::InvalidateRBACData() +{ + LOG_DEBUG("rbac", "WorldSession::InvalidateRBACData [AccountId: {}, Name: {}, realmId: {}]", + _RBACData->GetId(), _RBACData->GetName(), realm.Id.Realm); + delete _RBACData; + _RBACData = nullptr; +} + +void WorldSession::InitRBACDataForTest() +{ + delete _RBACData; + _RBACData = new rbac::RBACData(_accountId, _accountName, realm.Id.Realm, _security); + _RBACData->LoadFromDBCallback(nullptr); +} diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index b7f33931c..0904312c9 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -440,6 +440,16 @@ public: AccountTypes GetSecurity() const { return _security; } bool CanSkipQueue() const { return _skipQueue; } + + // RBAC + rbac::RBACData* GetRBACData() const { return _RBACData; } + bool HasPermission(uint32 permissionId); + void LoadPermissions(); + QueryCallback LoadPermissionsAsync(); + void InvalidateRBACData(); + + /// For unit testing - initializes RBAC data without database access + void InitRBACDataForTest(); uint32 GetAccountId() const { return _accountId; } Player* GetPlayer() const { return _player; } std::string const& GetPlayerName() const; @@ -1224,6 +1234,7 @@ private: AccountTypes _security; bool _skipQueue; uint32 _accountId; + rbac::RBACData* _RBACData; std::string _accountName; uint32 _accountFlags; uint8 m_expansion; diff --git a/src/server/game/Server/WorldSessionMgr.cpp b/src/server/game/Server/WorldSessionMgr.cpp index b43099669..8f5d40325 100644 --- a/src/server/game/Server/WorldSessionMgr.cpp +++ b/src/server/game/Server/WorldSessionMgr.cpp @@ -17,6 +17,7 @@ #include "Chat.h" #include "ChatPackets.h" +#include "RBAC.h" #include "GameTime.h" #include "Metric.h" #include "Player.h" @@ -323,7 +324,7 @@ void WorldSessionMgr::AddSession_(WorldSession* session) // don't count this session when checking player limit --Sessions; - if (pLimit > 0 && Sessions >= pLimit && AccountMgr::IsPlayerAccount(session->GetSecurity()) && !session->CanSkipQueue() && !HasRecentlyDisconnected(session)) + if (pLimit > 0 && Sessions >= pLimit && !session->HasPermission(rbac::RBAC_PERM_SKIP_QUEUE) && !session->CanSkipQueue() && !HasRecentlyDisconnected(session)) { AddQueuedPlayer(session); UpdateMaxSessionCounters(); @@ -390,7 +391,7 @@ void WorldSessionMgr::SendGlobalGMMessage(WorldPacket const* packet, WorldSessio itr->second->GetPlayer() && itr->second->GetPlayer()->IsInWorld() && itr->second != self && - !AccountMgr::IsPlayerAccount(itr->second->GetSecurity()) && + itr->second->HasPermission(rbac::RBAC_PERM_RECEIVE_GLOBAL_GM_TEXTMESSAGE) && (teamId == TEAM_NEUTRAL || itr->second->GetPlayer()->GetTeamId() == teamId)) { itr->second->SendPacket(packet); diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index cf809ba0a..e4ca47d10 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -31,6 +31,7 @@ #include "World.h" #include "WorldSession.h" #include "WorldSessionMgr.h" +#include "RBAC.h" #include "zlib.h" #include @@ -763,7 +764,7 @@ bool WorldSocket::HandlePing(WorldPacket& recvPacket) { std::unique_lock sessionGuard(_worldSessionLock); - if (_worldSession && AccountMgr::IsPlayerAccount(_worldSession->GetSecurity())) + if (_worldSession && !_worldSession->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_OVERSPEED_PING)) { LOG_ERROR("network", "WorldSocket::HandlePing: {} kicked for over-speed pings (address: {})", _worldSession->GetPlayerInfo(), GetRemoteIpAddress().to_string()); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 1113d6c90..b36c34d45 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -42,6 +42,7 @@ #include "OutdoorPvPMgr.h" #include "Pet.h" #include "Player.h" +#include "RBAC.h" #include "ReputationMgr.h" #include "ScriptMgr.h" #include "SharedDefines.h" @@ -2900,6 +2901,14 @@ void Spell::EffectEnchantItemPerm(SpellEffIndex effIndex) // add new enchanting if equipped item_owner->ApplyEnchantment(itemTarget, PERM_ENCHANTMENT_SLOT, true); + if (item_owner != p_caster && p_caster->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(p_caster->GetSession()->GetAccountId(), "GM {} (Account: {}) enchanting(perm): {} (SpellID: {} EncID: {}) on {}'s item: {} (Entry: {})", + p_caster->GetName(), p_caster->GetSession()->GetAccountId(), + m_spellInfo->SpellName[0], m_spellInfo->Id, enchant_id, + item_owner->GetName(), itemTarget->GetTemplate()->Name1, itemTarget->GetEntry()); + } + item_owner->RemoveTradeableItem(itemTarget); itemTarget->ClearSoulboundTradeable(item_owner); } @@ -2947,6 +2956,15 @@ void Spell::EffectEnchantItemPrismatic(SpellEffIndex effIndex) if (!item_owner) return; + Player* p_caster = m_caster->ToPlayer(); + if (item_owner != p_caster && p_caster->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(p_caster->GetSession()->GetAccountId(), "GM {} (Account: {}) enchanting(prismatic): {} (SpellID: {} EncID: {}) on {}'s item: {} (Entry: {})", + p_caster->GetName(), p_caster->GetSession()->GetAccountId(), + m_spellInfo->SpellName[0], m_spellInfo->Id, enchant_id, + item_owner->GetName(), itemTarget->GetTemplate()->Name1, itemTarget->GetEntry()); + } + // remove old enchanting before applying new if equipped item_owner->ApplyEnchantment(itemTarget, PRISMATIC_ENCHANTMENT_SLOT, false); @@ -3096,6 +3114,14 @@ void Spell::EffectEnchantItemTmp(SpellEffIndex effIndex) // add new enchanting if equipped item_owner->ApplyEnchantment(itemTarget, TEMP_ENCHANTMENT_SLOT, true); + if (item_owner != p_caster && p_caster->GetSession()->HasPermission(rbac::RBAC_PERM_LOG_GM_TRADE)) + { + LOG_GM(p_caster->GetSession()->GetAccountId(), "GM {} (Account: {}) enchanting(temp): {} (SpellID: {} EncID: {}) on {}'s item: {} (Entry: {})", + p_caster->GetName(), p_caster->GetSession()->GetAccountId(), + m_spellInfo->SpellName[0], m_spellInfo->Id, enchant_id, + item_owner->GetName(), itemTarget->GetTemplate()->Name1, itemTarget->GetEntry()); + } + item_owner->RemoveTradeableItem(itemTarget); itemTarget->ClearSoulboundTradeable(item_owner); } diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 03e70e55c..b0c808785 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -110,6 +110,7 @@ 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 ReloadRBAC() = 0; }; #endif //AZEROTHCORE_IWORLD_H diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 72b8004e0..85b3871a3 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -469,6 +469,9 @@ void World::SetInitialWorldSettings() LOG_INFO("server.loading", ">> Localization Strings loaded in {} ms", GetMSTimeDiffToNow(oldMSTime)); LOG_INFO("server.loading", " "); + LOG_INFO("server.loading", "Loading Account Roles and Permissions..."); + sAccountMgr->LoadRBAC(); + LOG_INFO("server.loading", "Loading Page Texts..."); sObjectMgr->LoadPageTexts(); @@ -1741,6 +1744,16 @@ void World::ResetEventSeasonalQuests(uint16 event_id) itr->second->GetPlayer()->ResetSeasonalQuestStatus(event_id); } +void World::ReloadRBAC() +{ + // Passive reload, we mark the data as invalidated and next time a permission is checked it will be reloaded + LOG_INFO("rbac", "World::ReloadRBAC()"); + WorldSessionMgr::SessionMap const& sessionMap = sWorldSessionMgr->GetAllSessions(); + for (WorldSessionMgr::SessionMap::const_iterator itr = sessionMap.begin(); itr != sessionMap.end(); ++itr) + if (WorldSession* session = itr->second) + session->InvalidateRBACData(); +} + void World::ResetRandomBG() { LOG_DEBUG("server.worldserver", "Random BG status reset for all characters."); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index d792b4d8c..b0499a741 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -235,6 +235,7 @@ public: [[nodiscard]] uint32 GetCleaningFlags() const override { return _cleaningFlags; } void SetCleaningFlags(uint32 flags) override { _cleaningFlags = flags; } void ResetEventSeasonalQuests(uint16 event_id) override; + void ReloadRBAC() override; [[nodiscard]] std::string const& GetRealmName() const override { return _realmName; } // pussywizard void SetRealmName(std::string name) override { _realmName = name; } // pussywizard diff --git a/src/server/scripts/Commands/cs_account.cpp b/src/server/scripts/Commands/cs_account.cpp index ad9f1f66e..9c37eff54 100644 --- a/src/server/scripts/Commands/cs_account.cpp +++ b/src/server/scripts/Commands/cs_account.cpp @@ -23,6 +23,7 @@ #include "CryptoGenerics.h" #include "IPLocation.h" #include "Player.h" +#include "RBAC.h" #include "Realm.h" #include "ScriptMgr.h" #include "SecretMgr.h" @@ -45,42 +46,42 @@ public: { static ChatCommandTable accountSetCommandTable = { - { "addon", HandleAccountSetAddonCommand, SEC_GAMEMASTER, Console::Yes }, - { "gmlevel", HandleAccountSetGmLevelCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "password", HandleAccountSetPasswordCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "2fa", HandleAccountSet2FACommand, SEC_PLAYER, Console::Yes }, - { "email", HandleAccountSetEmailCommand, SEC_ADMINISTRATOR, Console::Yes } + { "addon", HandleAccountSetAddonCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_SET_ADDON, Console::Yes }, + { "gmlevel", HandleAccountSetGmLevelCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_SET_SECLEVEL, Console::Yes }, + { "password", HandleAccountSetPasswordCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_SET_PASSWORD, Console::Yes }, + { "2fa", HandleAccountSet2FACommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_SET, Console::Yes }, + { "email", HandleAccountSetEmailCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_SET_SEC_EMAIL, Console::Yes } }; static ChatCommandTable accountLockCommandTable { - { "country", HandleAccountLockCountryCommand, SEC_PLAYER, Console::Yes }, - { "ip", HandleAccountLockIpCommand, SEC_PLAYER, Console::Yes } + { "country", HandleAccountLockCountryCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_LOCK_COUNTRY, Console::Yes }, + { "ip", HandleAccountLockIpCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_LOCK_IP, Console::Yes } }; static ChatCommandTable account2faCommandTable { - { "setup", HandleAccount2FASetupCommand, SEC_PLAYER, Console::No }, - { "remove", HandleAccount2FARemoveCommand, SEC_PLAYER, Console::No } + { "setup", HandleAccount2FASetupCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT, Console::No }, + { "remove", HandleAccount2FARemoveCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT, Console::No } }; static ChatCommandTable accountRemoveCommandTable { - { "country", HandleAccountRemoveLockCountryCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "country", HandleAccountRemoveLockCountryCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_LOCK_COUNTRY, Console::Yes }, }; static ChatCommandTable accountCommandTable = { { "2fa", account2faCommandTable }, - { "addon", HandleAccountAddonCommand, SEC_MODERATOR, Console::No }, - { "create", HandleAccountCreateCommand, SEC_CONSOLE, Console::Yes }, - { "delete", HandleAccountDeleteCommand, SEC_CONSOLE, Console::Yes }, - { "onlinelist", HandleAccountOnlineListCommand, SEC_CONSOLE, Console::Yes }, + { "addon", HandleAccountAddonCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_ADDON, Console::No }, + { "create", HandleAccountCreateCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_CREATE, Console::Yes }, + { "delete", HandleAccountDeleteCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_DELETE, Console::Yes }, + { "onlinelist", HandleAccountOnlineListCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_ONLINE_LIST, Console::Yes }, { "lock", accountLockCommandTable }, { "set", accountSetCommandTable }, - { "password", HandleAccountPasswordCommand, SEC_PLAYER, Console::No }, + { "password", HandleAccountPasswordCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT_PASSWORD, Console::No }, { "remove", accountRemoveCommandTable }, - { "", HandleAccountCommand, SEC_PLAYER, Console::No } + { "", HandleAccountCommand, rbac::RBAC_PERM_COMMAND_ACCOUNT, Console::No } }; static ChatCommandTable commandTable = @@ -279,7 +280,7 @@ public: // if email is not specified, use empty string std::string emailStr = email ? email : ""; - AccountOpResult result = AccountMgr::CreateAccount(std::string(accountName), std::string(password), emailStr); + AccountOpResult result = sAccountMgr->CreateAccount(std::string(accountName), std::string(password), emailStr); switch (result) { case AOR_OK: @@ -658,6 +659,16 @@ public: { AccountTypes gmLevel = handler->GetSession()->GetSecurity(); handler->PSendSysMessage(LANG_ACCOUNT_LEVEL, uint32(gmLevel)); + + if (handler->GetSession()->HasPermission(rbac::RBAC_PERM_MAY_CHECK_OWN_EMAIL)) + { + LoginDatabasePreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_GET_EMAIL_BY_ID); + stmt->SetData(0, handler->GetSession()->GetAccountId()); + PreparedQueryResult result = LoginDatabase.Query(stmt); + if (result) + handler->PSendSysMessage(LANG_COMMAND_EMAIL_OUTPUT, (*result)[0].Get()); + } + return true; } diff --git a/src/server/scripts/Commands/cs_achievement.cpp b/src/server/scripts/Commands/cs_achievement.cpp index 8557f46ae..37963f8aa 100644 --- a/src/server/scripts/Commands/cs_achievement.cpp +++ b/src/server/scripts/Commands/cs_achievement.cpp @@ -20,6 +20,7 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -32,8 +33,8 @@ public: { static ChatCommandTable achievementCommandTable = { - { "add", HandleAchievementAddCommand, SEC_GAMEMASTER, Console::Yes }, - { "checkall", HandleAchievementCheckAllCommand, SEC_ADMINISTRATOR, Console::Yes } + { "add", HandleAchievementAddCommand, rbac::RBAC_PERM_COMMAND_ACHIEVEMENT_ADD, Console::Yes }, + { "checkall", HandleAchievementCheckAllCommand, rbac::RBAC_PERM_COMMAND_ACHIEVEMENT_CHECKALL, Console::Yes } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_arena.cpp b/src/server/scripts/Commands/cs_arena.cpp index 4adb0f736..a2a8f92cf 100644 --- a/src/server/scripts/Commands/cs_arena.cpp +++ b/src/server/scripts/Commands/cs_arena.cpp @@ -15,12 +15,13 @@ * with this program. If not, see . */ -#include "ArenaTeamMgr.h" #include "ArenaSeasonMgr.h" #include "ArenaTeamFilter.h" +#include "ArenaTeamMgr.h" #include "Chat.h" #include "CommandScript.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -33,25 +34,25 @@ public: { static ChatCommandTable arenaSeasonSetCommandTable = { - { "state", HandleArenaSeasonSetStateCommand, SEC_ADMINISTRATOR, Console::Yes } + { "state", HandleArenaSeasonSetStateCommand, rbac::RBAC_PERM_COMMAND_ARENA_SEASON, Console::Yes } }; static ChatCommandTable arenaSeasonCommandTable = { - { "reward", HandleArenaSeasonRewardCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "deleteteams", HandleArenaSeasonDeleteTeamsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "start", HandleArenaSeasonStartCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "reward", HandleArenaSeasonRewardCommand, rbac::RBAC_PERM_COMMAND_ARENA_SEASON_REWARD, Console::Yes }, + { "deleteteams", HandleArenaSeasonDeleteTeamsCommand, rbac::RBAC_PERM_COMMAND_ARENA_SEASON_DELETETEAMS, Console::Yes }, + { "start", HandleArenaSeasonStartCommand, rbac::RBAC_PERM_COMMAND_ARENA_SEASON_START, Console::Yes }, { "set", arenaSeasonSetCommandTable } }; static ChatCommandTable arenaCommandTable = { - { "create", HandleArenaCreateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "disband", HandleArenaDisbandCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "rename", HandleArenaRenameCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "captain", HandleArenaCaptainCommand, SEC_ADMINISTRATOR, Console::No }, - { "info", HandleArenaInfoCommand, SEC_GAMEMASTER, Console::Yes }, - { "lookup", HandleArenaLookupCommand, SEC_GAMEMASTER, Console::Yes }, + { "create", HandleArenaCreateCommand, rbac::RBAC_PERM_COMMAND_ARENA_CREATE, Console::Yes }, + { "disband", HandleArenaDisbandCommand, rbac::RBAC_PERM_COMMAND_ARENA_DISBAND, Console::Yes }, + { "rename", HandleArenaRenameCommand, rbac::RBAC_PERM_COMMAND_ARENA_RENAME, Console::Yes }, + { "captain", HandleArenaCaptainCommand, rbac::RBAC_PERM_COMMAND_ARENA_CAPTAIN, Console::No }, + { "info", HandleArenaInfoCommand, rbac::RBAC_PERM_COMMAND_ARENA_INFO, Console::Yes }, + { "lookup", HandleArenaLookupCommand, rbac::RBAC_PERM_COMMAND_ARENA_LOOKUP, Console::Yes }, { "season", arenaSeasonCommandTable } }; diff --git a/src/server/scripts/Commands/cs_bag.cpp b/src/server/scripts/Commands/cs_bag.cpp index fd365b875..a0567e49c 100644 --- a/src/server/scripts/Commands/cs_bag.cpp +++ b/src/server/scripts/Commands/cs_bag.cpp @@ -19,6 +19,7 @@ #include "CommandScript.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" constexpr std::array itemQualityToString = { @@ -43,7 +44,7 @@ public: { static ChatCommandTable commandTable = { - { "bags clear", HandleBagsClearCommand, SEC_GAMEMASTER, Console::No }, + { "bags clear", HandleBagsClearCommand, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No }, }; return commandTable; diff --git a/src/server/scripts/Commands/cs_ban.cpp b/src/server/scripts/Commands/cs_ban.cpp index 466364be8..539b60e06 100644 --- a/src/server/scripts/Commands/cs_ban.cpp +++ b/src/server/scripts/Commands/cs_ban.cpp @@ -25,6 +25,7 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" /// Ban function modes enum BanMode @@ -49,32 +50,32 @@ public: { static ChatCommandTable unbanCommandTable = { - { "account", HandleUnBanAccountCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "character", HandleUnBanCharacterCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "playeraccount", HandleUnBanAccountByCharCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "ip", HandleUnBanIPCommand, SEC_ADMINISTRATOR, Console::Yes } + { "account", HandleUnBanAccountCommand, rbac::RBAC_PERM_COMMAND_UNBAN_ACCOUNT, Console::Yes }, + { "character", HandleUnBanCharacterCommand, rbac::RBAC_PERM_COMMAND_UNBAN_CHARACTER, Console::Yes }, + { "playeraccount", HandleUnBanAccountByCharCommand, rbac::RBAC_PERM_COMMAND_UNBAN_PLAYERACCOUNT, Console::Yes }, + { "ip", HandleUnBanIPCommand, rbac::RBAC_PERM_COMMAND_UNBAN_IP, Console::Yes } }; static ChatCommandTable banlistCommandTable = { - { "account", HandleBanListAccountCommand, SEC_GAMEMASTER, Console::Yes }, - { "character", HandleBanListCharacterCommand, SEC_GAMEMASTER, Console::Yes }, - { "ip", HandleBanListIPCommand, SEC_GAMEMASTER, Console::Yes } + { "account", HandleBanListAccountCommand, rbac::RBAC_PERM_COMMAND_BANLIST_ACCOUNT, Console::Yes }, + { "character", HandleBanListCharacterCommand, rbac::RBAC_PERM_COMMAND_BANLIST_CHARACTER, Console::Yes }, + { "ip", HandleBanListIPCommand, rbac::RBAC_PERM_COMMAND_BANLIST_IP, Console::Yes } }; static ChatCommandTable baninfoCommandTable = { - { "account", HandleBanInfoAccountCommand, SEC_GAMEMASTER, Console::Yes }, - { "character", HandleBanInfoCharacterCommand, SEC_GAMEMASTER, Console::Yes }, - { "ip", HandleBanInfoIPCommand, SEC_GAMEMASTER, Console::Yes } + { "account", HandleBanInfoAccountCommand, rbac::RBAC_PERM_COMMAND_BANINFO_ACCOUNT, Console::Yes }, + { "character", HandleBanInfoCharacterCommand, rbac::RBAC_PERM_COMMAND_BANINFO_CHARACTER, Console::Yes }, + { "ip", HandleBanInfoIPCommand, rbac::RBAC_PERM_COMMAND_BANINFO_IP, Console::Yes } }; static ChatCommandTable banCommandTable = { - { "account", HandleBanAccountCommand, SEC_GAMEMASTER, Console::Yes }, - { "character", HandleBanCharacterCommand, SEC_GAMEMASTER, Console::Yes }, - { "playeraccount",HandleBanAccountByCharCommand, SEC_GAMEMASTER, Console::Yes }, - { "ip", HandleBanIPCommand, SEC_GAMEMASTER, Console::Yes } + { "account", HandleBanAccountCommand, rbac::RBAC_PERM_COMMAND_BAN_ACCOUNT, Console::Yes }, + { "character", HandleBanCharacterCommand, rbac::RBAC_PERM_COMMAND_BAN_CHARACTER, Console::Yes }, + { "playeraccount",HandleBanAccountByCharCommand, rbac::RBAC_PERM_COMMAND_BAN_PLAYERACCOUNT, Console::Yes }, + { "ip", HandleBanIPCommand, rbac::RBAC_PERM_COMMAND_BAN_IP, Console::Yes } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_bf.cpp b/src/server/scripts/Commands/cs_bf.cpp index 815f1fb20..3e8d0502b 100644 --- a/src/server/scripts/Commands/cs_bf.cpp +++ b/src/server/scripts/Commands/cs_bf.cpp @@ -18,10 +18,11 @@ #include "BattlefieldMgr.h" #include "Chat.h" #include "CommandScript.h" -#include "Language.h" #include "GameTime.h" +#include "Language.h" #include "ObjectAccessor.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -34,12 +35,12 @@ public: { static ChatCommandTable battlefieldcommandTable = { - { "start", HandleBattlefieldStart, SEC_ADMINISTRATOR, Console::Yes }, - { "stop", HandleBattlefieldEnd, SEC_ADMINISTRATOR, Console::Yes }, - { "switch", HandleBattlefieldSwitch, SEC_ADMINISTRATOR, Console::Yes }, - { "timer", HandleBattlefieldTimer, SEC_ADMINISTRATOR, Console::Yes }, - { "enable", HandleBattlefieldEnable, SEC_ADMINISTRATOR, Console::Yes }, - { "queue", HandleBattlefieldQueue, SEC_GAMEMASTER, Console::Yes } + { "start", HandleBattlefieldStart, rbac::RBAC_PERM_COMMAND_BF_START, Console::Yes }, + { "stop", HandleBattlefieldEnd, rbac::RBAC_PERM_COMMAND_BF_STOP, Console::Yes }, + { "switch", HandleBattlefieldSwitch, rbac::RBAC_PERM_COMMAND_BF_SWITCH, Console::Yes }, + { "timer", HandleBattlefieldTimer, rbac::RBAC_PERM_COMMAND_BF_TIMER, Console::Yes }, + { "enable", HandleBattlefieldEnable, rbac::RBAC_PERM_COMMAND_BF_ENABLE, Console::Yes }, + { "queue", HandleBattlefieldQueue, rbac::RBAC_PERM_COMMAND_BF_QUEUE, Console::Yes } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_cache.cpp b/src/server/scripts/Commands/cs_cache.cpp index 2e05f5472..6f7899824 100644 --- a/src/server/scripts/Commands/cs_cache.cpp +++ b/src/server/scripts/Commands/cs_cache.cpp @@ -20,6 +20,7 @@ #include "Group.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -32,9 +33,9 @@ public: { static ChatCommandTable cacheCommandTable = { - { "info", HandleCacheInfoCommand, SEC_GAMEMASTER, Console::Yes }, - { "delete", HandleCacheDeleteCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "refresh", HandleCacheRefreshCommand, SEC_GAMEMASTER, Console::Yes } + { "info", HandleCacheInfoCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "delete", HandleCacheDeleteCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "refresh", HandleCacheRefreshCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_cast.cpp b/src/server/scripts/Commands/cs_cast.cpp index 654e2cf00..56c604a72 100644 --- a/src/server/scripts/Commands/cs_cast.cpp +++ b/src/server/scripts/Commands/cs_cast.cpp @@ -20,6 +20,7 @@ #include "Creature.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" #include "SpellInfo.h" #include "SpellMgr.h" @@ -34,12 +35,12 @@ public: { static ChatCommandTable castCommandTable = { - { "back", HandleCastBackCommand, SEC_GAMEMASTER, Console::No }, - { "dist", HandleCastDistCommand, SEC_GAMEMASTER, Console::No }, - { "self", HandleCastSelfCommand, SEC_GAMEMASTER, Console::No }, - { "target", HandleCastTargetCommad, SEC_GAMEMASTER, Console::No }, - { "dest", HandleCastDestCommand, SEC_GAMEMASTER, Console::No }, - { "", HandleCastCommand, SEC_GAMEMASTER, Console::No } + { "back", HandleCastBackCommand, rbac::RBAC_PERM_COMMAND_CAST_BACK, Console::No }, + { "dist", HandleCastDistCommand, rbac::RBAC_PERM_COMMAND_CAST_DIST, Console::No }, + { "self", HandleCastSelfCommand, rbac::RBAC_PERM_COMMAND_CAST_SELF, Console::No }, + { "target", HandleCastTargetCommad, rbac::RBAC_PERM_COMMAND_CAST_TARGET, Console::No }, + { "dest", HandleCastDestCommand, rbac::RBAC_PERM_COMMAND_CAST_DEST, Console::No }, + { "", HandleCastCommand, rbac::RBAC_PERM_COMMAND_CAST, Console::No } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index 11fc6d312..464b70512 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -25,6 +25,7 @@ #include "ObjectMgr.h" #include "Player.h" #include "PlayerDump.h" +#include "RBAC.h" #include "ReputationMgr.h" #include "Timer.h" #include "World.h" @@ -41,44 +42,45 @@ public: { static ChatCommandTable pdumpCommandTable = { - { "load", HandlePDumpLoadCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "write", HandlePDumpWriteCommand, SEC_ADMINISTRATOR, Console::Yes } + { "copy", HandlePDumpCopyCommand, rbac::RBAC_PERM_COMMAND_PDUMP_COPY, Console::Yes }, + { "load", HandlePDumpLoadCommand, rbac::RBAC_PERM_COMMAND_PDUMP_LOAD, Console::Yes }, + { "write", HandlePDumpWriteCommand, rbac::RBAC_PERM_COMMAND_PDUMP_WRITE, Console::Yes } }; static ChatCommandTable characterDeletedCommandTable = { - { "delete", HandleCharacterDeletedDeleteCommand, SEC_CONSOLE, Console::Yes }, - { "list", HandleCharacterDeletedListCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "restore", HandleCharacterDeletedRestoreCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "purge", HandleCharacterDeletedPurgeCommand, SEC_CONSOLE, Console::Yes } + { "delete", HandleCharacterDeletedDeleteCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_DELETED_DELETE, Console::Yes }, + { "list", HandleCharacterDeletedListCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_DELETED_LIST, Console::Yes }, + { "restore", HandleCharacterDeletedRestoreCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_DELETED_RESTORE, Console::Yes }, + { "purge", HandleCharacterDeletedPurgeCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_DELETED_OLD, Console::Yes } }; static ChatCommandTable characterCheckCommandTable = { - { "bank", HandleCharacterCheckBankCommand, SEC_GAMEMASTER, Console::Yes }, - { "bag", HandleCharacterCheckBagCommand, SEC_GAMEMASTER, Console::Yes }, - { "profession", HandleCharacterCheckProfessionCommand, SEC_GAMEMASTER, Console::Yes } + { "bank", HandleCharacterCheckBankCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_CHECK_BANK, Console::Yes }, + { "bag", HandleCharacterCheckBagCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_CHECK_BAG, Console::Yes }, + { "profession", HandleCharacterCheckProfessionCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_CHECK_PROFESSION, Console::Yes } }; static ChatCommandTable characterCommandTable = { - { "customize", HandleCharacterCustomizeCommand, SEC_GAMEMASTER, Console::Yes }, - { "changefaction", HandleCharacterChangeFactionCommand, SEC_GAMEMASTER, Console::Yes }, - { "changerace", HandleCharacterChangeRaceCommand, SEC_GAMEMASTER, Console::Yes }, - { "changeaccount", HandleCharacterChangeAccountCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "customize", HandleCharacterCustomizeCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_CUSTOMIZE, Console::Yes }, + { "changefaction", HandleCharacterChangeFactionCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_CHANGEFACTION, Console::Yes }, + { "changerace", HandleCharacterChangeRaceCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_CHANGERACE, Console::Yes }, + { "changeaccount", HandleCharacterChangeAccountCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_CHANGEACCOUNT, Console::Yes }, { "check", characterCheckCommandTable }, - { "erase", HandleCharacterEraseCommand, SEC_CONSOLE, Console::Yes }, + { "erase", HandleCharacterEraseCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_ERASE, Console::Yes }, { "deleted", characterDeletedCommandTable }, - { "level", HandleCharacterLevelCommand, SEC_GAMEMASTER, Console::Yes }, - { "rename", HandleCharacterRenameCommand, SEC_GAMEMASTER, Console::Yes }, - { "reputation", HandleCharacterReputationCommand, SEC_GAMEMASTER, Console::Yes }, - { "titles", HandleCharacterTitlesCommand, SEC_GAMEMASTER, Console::Yes } + { "level", HandleCharacterLevelCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_LEVEL, Console::Yes }, + { "rename", HandleCharacterRenameCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_RENAME, Console::Yes }, + { "reputation", HandleCharacterReputationCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_REPUTATION, Console::Yes }, + { "titles", HandleCharacterTitlesCommand, rbac::RBAC_PERM_COMMAND_CHARACTER_TITLES, Console::Yes } }; static ChatCommandTable commandTable = { { "character", characterCommandTable }, - { "levelup", HandleLevelUpCommand, SEC_GAMEMASTER, Console::No }, + { "levelup", HandleLevelUpCommand, rbac::RBAC_PERM_COMMAND_LEVELUP, Console::No }, { "pdump", pdumpCommandTable } }; diff --git a/src/server/scripts/Commands/cs_cheat.cpp b/src/server/scripts/Commands/cs_cheat.cpp index 2b067ab78..880dc772c 100644 --- a/src/server/scripts/Commands/cs_cheat.cpp +++ b/src/server/scripts/Commands/cs_cheat.cpp @@ -19,6 +19,8 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" +#include "WorldSession.h" using namespace Acore::ChatCommands; @@ -31,14 +33,14 @@ public: { static ChatCommandTable cheatCommandTable = { - { "god", HandleGodModeCheatCommand, SEC_GAMEMASTER, Console::No }, - { "casttime", HandleCasttimeCheatCommand, SEC_GAMEMASTER, Console::No }, - { "cooldown", HandleCoolDownCheatCommand, SEC_GAMEMASTER, Console::No }, - { "power", HandlePowerCheatCommand, SEC_GAMEMASTER, Console::No }, - { "waterwalk", HandleWaterWalkCheatCommand, SEC_GAMEMASTER, Console::No }, - { "status", HandleCheatStatusCommand, SEC_GAMEMASTER, Console::No }, - { "taxi", HandleTaxiCheatCommand, SEC_GAMEMASTER, Console::No }, - { "explore", HandleExploreCheatCommand, SEC_GAMEMASTER, Console::No } + { "god", HandleGodModeCheatCommand, rbac::RBAC_PERM_COMMAND_CHEAT_GOD, Console::No }, + { "casttime", HandleCasttimeCheatCommand, rbac::RBAC_PERM_COMMAND_CHEAT_CASTTIME, Console::No }, + { "cooldown", HandleCoolDownCheatCommand, rbac::RBAC_PERM_COMMAND_CHEAT_COOLDOWN, Console::No }, + { "power", HandlePowerCheatCommand, rbac::RBAC_PERM_COMMAND_CHEAT_POWER, Console::No }, + { "waterwalk", HandleWaterWalkCheatCommand, rbac::RBAC_PERM_COMMAND_CHEAT_WATERWALK, Console::No }, + { "status", HandleCheatStatusCommand, rbac::RBAC_PERM_COMMAND_CHEAT_STATUS, Console::No }, + { "taxi", HandleTaxiCheatCommand, rbac::RBAC_PERM_COMMAND_CHEAT_TAXI, Console::No }, + { "explore", HandleExploreCheatCommand, rbac::RBAC_PERM_COMMAND_CHEAT_EXPLORE, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 8c80529d9..401446077 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -37,6 +37,7 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "PoolMgr.h" +#include "RBAC.h" #include "RaceMgr.h" #include "ScriptMgr.h" #include "Transport.h" @@ -57,71 +58,71 @@ public: { static ChatCommandTable debugPlayCommandTable = { - { "cinematic", HandleDebugPlayCinematicCommand, SEC_ADMINISTRATOR, Console::No }, - { "movie", HandleDebugPlayMovieCommand, SEC_ADMINISTRATOR, Console::No }, - { "sound", HandleDebugPlaySoundCommand, SEC_ADMINISTRATOR, Console::No }, - { "music", HandleDebugPlayMusicCommand, SEC_ADMINISTRATOR, Console::No }, - { "visual", HandleDebugVisualCommand, SEC_ADMINISTRATOR, Console::No } + { "cinematic", HandleDebugPlayCinematicCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "movie", HandleDebugPlayMovieCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "sound", HandleDebugPlaySoundCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "music", HandleDebugPlayMusicCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "visual", HandleDebugVisualCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No } }; static ChatCommandTable debugSendCommandTable = { - { "buyerror", HandleDebugSendBuyErrorCommand, SEC_ADMINISTRATOR, Console::No }, - { "channelnotify", HandleDebugSendChannelNotifyCommand, SEC_ADMINISTRATOR, Console::No }, - { "chatmessage", HandleDebugSendChatMsgCommand, SEC_ADMINISTRATOR, Console::No }, - { "equiperror", HandleDebugSendEquipErrorCommand, SEC_ADMINISTRATOR, Console::No }, - { "largepacket", HandleDebugSendLargePacketCommand, SEC_ADMINISTRATOR, Console::No }, - { "opcode", HandleDebugSendOpcodeCommand, SEC_ADMINISTRATOR, Console::No }, - { "qpartymsg", HandleDebugSendQuestPartyMsgCommand, SEC_ADMINISTRATOR, Console::No }, - { "qinvalidmsg", HandleDebugSendQuestInvalidMsgCommand, SEC_ADMINISTRATOR, Console::No }, - { "sellerror", HandleDebugSendSellErrorCommand, SEC_ADMINISTRATOR, Console::No }, - { "setphaseshift", HandleDebugSendSetPhaseShiftCommand, SEC_ADMINISTRATOR, Console::No }, - { "spellfail", HandleDebugSendSpellFailCommand, SEC_ADMINISTRATOR, Console::No } + { "buyerror", HandleDebugSendBuyErrorCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "channelnotify", HandleDebugSendChannelNotifyCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "chatmessage", HandleDebugSendChatMsgCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "equiperror", HandleDebugSendEquipErrorCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "largepacket", HandleDebugSendLargePacketCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "opcode", HandleDebugSendOpcodeCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "qpartymsg", HandleDebugSendQuestPartyMsgCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "qinvalidmsg", HandleDebugSendQuestInvalidMsgCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "sellerror", HandleDebugSendSellErrorCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "setphaseshift", HandleDebugSendSetPhaseShiftCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "spellfail", HandleDebugSendSpellFailCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No } }; static ChatCommandTable debugCommandTable = { - { "setbit", HandleDebugSet32BitCommand, SEC_ADMINISTRATOR, Console::No }, - { "threat", HandleDebugThreatListCommand, SEC_ADMINISTRATOR, Console::No }, - { "threatinfo", HandleDebugThreatInfoCommand, SEC_ADMINISTRATOR, Console::No }, - { "combat", HandleDebugCombatListCommand, SEC_ADMINISTRATOR, Console::No }, - { "hostile", HandleDebugHostileRefListCommand, SEC_ADMINISTRATOR, Console::No }, - { "anim", HandleDebugAnimCommand, SEC_ADMINISTRATOR, Console::No }, - { "arena", HandleDebugArenaCommand, 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 }, - { "getvalue", HandleDebugGetValueCommand, SEC_ADMINISTRATOR, Console::No }, - { "getitemvalue", HandleDebugGetItemValueCommand, SEC_ADMINISTRATOR, Console::No }, - { "Mod32Value", HandleDebugMod32ValueCommand, SEC_ADMINISTRATOR, Console::No }, + { "setbit", HandleDebugSet32BitCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "threat", HandleDebugThreatListCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "threatinfo", HandleDebugThreatInfoCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "combat", HandleDebugCombatListCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "hostile", HandleDebugHostileRefListCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "anim", HandleDebugAnimCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "arena", HandleDebugArenaCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "bg", HandleDebugBattlegroundCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes}, + { "cooldown", HandleDebugCooldownCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "getitemstate", HandleDebugGetItemStateCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "lootrecipient", HandleDebugGetLootRecipientCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "getvalue", HandleDebugGetValueCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "getitemvalue", HandleDebugGetItemValueCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "Mod32Value", HandleDebugMod32ValueCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, { "play", debugPlayCommandTable }, { "send", debugSendCommandTable }, - { "setaurastate", HandleDebugSetAuraStateCommand, SEC_ADMINISTRATOR, Console::No }, - { "setitemvalue", HandleDebugSetItemValueCommand, SEC_ADMINISTRATOR, Console::No }, - { "setvalue", HandleDebugSetValueCommand, SEC_ADMINISTRATOR, Console::No }, - { "spawnvehicle", HandleDebugSpawnVehicleCommand, SEC_ADMINISTRATOR, Console::No }, - { "setvid", HandleDebugSetVehicleIdCommand, SEC_ADMINISTRATOR, Console::No }, - { "entervehicle", HandleDebugEnterVehicleCommand, SEC_ADMINISTRATOR, Console::No }, - { "uws", HandleDebugUpdateWorldStateCommand, SEC_ADMINISTRATOR, Console::No }, - { "update", HandleDebugUpdateCommand, SEC_ADMINISTRATOR, Console::No }, - { "itemexpire", HandleDebugItemExpireCommand, SEC_ADMINISTRATOR, Console::No }, - { "areatriggers", HandleDebugAreaTriggersCommand, SEC_ADMINISTRATOR, Console::No }, - { "lfg", HandleDebugDungeonFinderCommand, SEC_ADMINISTRATOR, Console::Yes}, - { "loot", HandleDebugLootCommand, SEC_GAMEMASTER, Console::Yes}, - { "los", HandleDebugLoSCommand, SEC_ADMINISTRATOR, Console::No }, - { "moveflags", HandleDebugMoveflagsCommand, SEC_ADMINISTRATOR, Console::No }, - { "unitstate", HandleDebugUnitStateCommand, SEC_ADMINISTRATOR, Console::No }, - { "objectcount", HandleDebugObjectCountCommand, SEC_ADMINISTRATOR, Console::Yes}, - { "dummy", HandleDebugDummyCommand, SEC_ADMINISTRATOR, Console::No }, - { "mapdata", HandleDebugMapDataCommand, SEC_ADMINISTRATOR, Console::No }, - { "boundary", HandleDebugBoundaryCommand, SEC_ADMINISTRATOR, Console::No }, - { "visibilitydata", HandleDebugVisibilityDataCommand, SEC_ADMINISTRATOR, Console::No }, - { "factionchange", HandleDebugFactionChangeCommand, SEC_ADMINISTRATOR, Console::Yes}, - { "zonestats", HandleDebugZoneStatsCommand, SEC_MODERATOR, Console::Yes} + { "setaurastate", HandleDebugSetAuraStateCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "setitemvalue", HandleDebugSetItemValueCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "setvalue", HandleDebugSetValueCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "spawnvehicle", HandleDebugSpawnVehicleCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "setvid", HandleDebugSetVehicleIdCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "entervehicle", HandleDebugEnterVehicleCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "uws", HandleDebugUpdateWorldStateCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "update", HandleDebugUpdateCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "itemexpire", HandleDebugItemExpireCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "areatriggers", HandleDebugAreaTriggersCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "lfg", HandleDebugDungeonFinderCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes}, + { "loot", HandleDebugLootCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes}, + { "los", HandleDebugLoSCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "moveflags", HandleDebugMoveflagsCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "unitstate", HandleDebugUnitStateCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "objectcount", HandleDebugObjectCountCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes}, + { "dummy", HandleDebugDummyCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "mapdata", HandleDebugMapDataCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "boundary", HandleDebugBoundaryCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "visibilitydata", HandleDebugVisibilityDataCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "factionchange", HandleDebugFactionChangeCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes}, + { "zonestats", HandleDebugZoneStatsCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes} }; static ChatCommandTable commandTable = { { "debug", debugCommandTable }, - { "wpgps", HandleWPGPSCommand, SEC_ADMINISTRATOR, Console::No } + { "wpgps", HandleWPGPSCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No } }; return commandTable; } diff --git a/src/server/scripts/Commands/cs_deserter.cpp b/src/server/scripts/Commands/cs_deserter.cpp index 860046d10..1e2e64066 100644 --- a/src/server/scripts/Commands/cs_deserter.cpp +++ b/src/server/scripts/Commands/cs_deserter.cpp @@ -27,6 +27,7 @@ #include "GroupMgr.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" #include "SpellAuras.h" using namespace Acore::ChatCommands; @@ -50,15 +51,15 @@ public: { static ChatCommandTable deserterInstanceCommandTable = { - { "add", HandleDeserterInstanceAdd, SEC_ADMINISTRATOR, Console::Yes }, - { "remove all", HandleDeserterInstanceRemoveAll, SEC_ADMINISTRATOR, Console::Yes }, - { "remove", HandleDeserterInstanceRemove, SEC_ADMINISTRATOR, Console::Yes } + { "add", HandleDeserterInstanceAdd, rbac::RBAC_PERM_COMMAND_DESERTER_INSTANCE_ADD, Console::Yes }, + { "remove all", HandleDeserterInstanceRemoveAll, rbac::RBAC_PERM_COMMAND_DESERTER_INSTANCE_REMOVE, Console::Yes }, + { "remove", HandleDeserterInstanceRemove, rbac::RBAC_PERM_COMMAND_DESERTER_INSTANCE_REMOVE, Console::Yes } }; static ChatCommandTable deserterBGCommandTable = { - { "add", HandleDeserterBGAdd, SEC_ADMINISTRATOR, Console::Yes }, - { "remove all", HandleDeserterBGRemoveAll, SEC_ADMINISTRATOR, Console::Yes }, - { "remove", HandleDeserterBGRemove, SEC_ADMINISTRATOR, Console::Yes } + { "add", HandleDeserterBGAdd, rbac::RBAC_PERM_COMMAND_DESERTER_BG_ADD, Console::Yes }, + { "remove all", HandleDeserterBGRemoveAll, rbac::RBAC_PERM_COMMAND_DESERTER_BG_REMOVE, Console::Yes }, + { "remove", HandleDeserterBGRemove, rbac::RBAC_PERM_COMMAND_DESERTER_BG_REMOVE, Console::Yes } }; static ChatCommandTable deserterCommandTable = diff --git a/src/server/scripts/Commands/cs_disable.cpp b/src/server/scripts/Commands/cs_disable.cpp index 2846f59a4..c0e35098d 100644 --- a/src/server/scripts/Commands/cs_disable.cpp +++ b/src/server/scripts/Commands/cs_disable.cpp @@ -21,6 +21,8 @@ #include "Language.h" #include "ObjectMgr.h" #include "OutdoorPvP.h" +#include "Player.h" +#include "RBAC.h" #include "SpellMgr.h" using namespace Acore::ChatCommands; @@ -34,21 +36,21 @@ public: { static ChatCommandTable removeDisableCommandTable = { - { "spell", HandleRemoveDisableSpellCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest", HandleRemoveDisableQuestCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "map", HandleRemoveDisableMapCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "battleground", HandleRemoveDisableBattlegroundCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "outdoorpvp", HandleRemoveDisableOutdoorPvPCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "vmap", HandleRemoveDisableVmapCommand, SEC_ADMINISTRATOR, Console::Yes } + { "spell", HandleRemoveDisableSpellCommand, rbac::RBAC_PERM_COMMAND_DISABLE_REMOVE_SPELL, Console::Yes }, + { "quest", HandleRemoveDisableQuestCommand, rbac::RBAC_PERM_COMMAND_DISABLE_REMOVE_QUEST, Console::Yes }, + { "map", HandleRemoveDisableMapCommand, rbac::RBAC_PERM_COMMAND_DISABLE_REMOVE_MAP, Console::Yes }, + { "battleground", HandleRemoveDisableBattlegroundCommand, rbac::RBAC_PERM_COMMAND_DISABLE_REMOVE_BATTLEGROUND, Console::Yes }, + { "outdoorpvp", HandleRemoveDisableOutdoorPvPCommand, rbac::RBAC_PERM_COMMAND_DISABLE_REMOVE_OUTDOORPVP, Console::Yes }, + { "vmap", HandleRemoveDisableVmapCommand, rbac::RBAC_PERM_COMMAND_DISABLE_REMOVE_VMAP, Console::Yes } }; static ChatCommandTable addDisableCommandTable = { - { "spell", HandleAddDisableSpellCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest", HandleAddDisableQuestCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "map", HandleAddDisableMapCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "battleground", HandleAddDisableBattlegroundCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "outdoorpvp", HandleAddDisableOutdoorPvPCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "vmap", HandleAddDisableVmapCommand, SEC_ADMINISTRATOR, Console::Yes } + { "spell", HandleAddDisableSpellCommand, rbac::RBAC_PERM_COMMAND_DISABLE_ADD_SPELL, Console::Yes }, + { "quest", HandleAddDisableQuestCommand, rbac::RBAC_PERM_COMMAND_DISABLE_ADD_QUEST, Console::Yes }, + { "map", HandleAddDisableMapCommand, rbac::RBAC_PERM_COMMAND_DISABLE_ADD_MAP, Console::Yes }, + { "battleground", HandleAddDisableBattlegroundCommand, rbac::RBAC_PERM_COMMAND_DISABLE_ADD_BATTLEGROUND, Console::Yes }, + { "outdoorpvp", HandleAddDisableOutdoorPvPCommand, rbac::RBAC_PERM_COMMAND_DISABLE_ADD_OUTDOORPVP, Console::Yes }, + { "vmap", HandleAddDisableVmapCommand, rbac::RBAC_PERM_COMMAND_DISABLE_ADD_VMAP, Console::Yes } }; static ChatCommandTable disableCommandTable = { diff --git a/src/server/scripts/Commands/cs_event.cpp b/src/server/scripts/Commands/cs_event.cpp index 1b101e086..97d44cb60 100644 --- a/src/server/scripts/Commands/cs_event.cpp +++ b/src/server/scripts/Commands/cs_event.cpp @@ -20,6 +20,7 @@ #include "GameEventMgr.h" #include "GameTime.h" #include "Language.h" +#include "RBAC.h" #include "Timer.h" using namespace Acore::ChatCommands; @@ -35,10 +36,10 @@ public: { static ChatCommandTable eventCommandTable = { - { "activelist", HandleEventActiveListCommand, SEC_GAMEMASTER, Console::Yes }, - { "start", HandleEventStartCommand, SEC_GAMEMASTER, Console::Yes }, - { "stop", HandleEventStopCommand, SEC_GAMEMASTER, Console::Yes }, - { "info", HandleEventInfoCommand, SEC_GAMEMASTER, Console::Yes } + { "activelist", HandleEventActiveListCommand, rbac::RBAC_PERM_COMMAND_EVENT_ACTIVELIST, Console::Yes }, + { "start", HandleEventStartCommand, rbac::RBAC_PERM_COMMAND_EVENT_START, Console::Yes }, + { "stop", HandleEventStopCommand, rbac::RBAC_PERM_COMMAND_EVENT_STOP, Console::Yes }, + { "info", HandleEventInfoCommand, rbac::RBAC_PERM_COMMAND_EVENT_INFO, Console::Yes } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_gear.cpp b/src/server/scripts/Commands/cs_gear.cpp index b67fc456d..c2ba986e2 100644 --- a/src/server/scripts/Commands/cs_gear.cpp +++ b/src/server/scripts/Commands/cs_gear.cpp @@ -19,6 +19,8 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" +#include "WorldSession.h" using namespace Acore::ChatCommands; @@ -31,8 +33,8 @@ public: { static ChatCommandTable gearCommandTable = { - { "repair", HandleGearRepairCommand, SEC_GAMEMASTER, Console::No }, - { "stats", HandleGearStatsCommand, SEC_PLAYER, Console::No } + { "repair", HandleGearRepairCommand, rbac::RBAC_PERM_COMMAND_GEAR_REPAIR, Console::No }, + { "stats", HandleGearStatsCommand, rbac::RBAC_PERM_COMMAND_GEAR_STATS, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_gm.cpp b/src/server/scripts/Commands/cs_gm.cpp index b1379ff24..0138f65c1 100644 --- a/src/server/scripts/Commands/cs_gm.cpp +++ b/src/server/scripts/Commands/cs_gm.cpp @@ -23,6 +23,7 @@ #include "ObjectAccessor.h" #include "Opcodes.h" #include "Player.h" +#include "RBAC.h" #include "Realm.h" #include "World.h" #include "WorldSession.h" @@ -38,14 +39,14 @@ public: { static ChatCommandTable gmCommandTable = { - { "chat", HandleGMChatCommand, SEC_GAMEMASTER, Console::No }, - { "fly", HandleGMFlyCommand, SEC_GAMEMASTER, Console::No }, - { "ingame", HandleGMListIngameCommand, SEC_PLAYER, Console::Yes }, - { "list", HandleGMListFullCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "visible", HandleGMVisibleCommand, SEC_GAMEMASTER, Console::No }, - { "on", HandleGMOnCommand, SEC_MODERATOR, Console::No }, - { "off", HandleGMOffCommand, SEC_MODERATOR, Console::No }, - { "spectator", HandleGMSpectatorCommand, SEC_GAMEMASTER, Console::No }, + { "chat", HandleGMChatCommand, rbac::RBAC_PERM_COMMAND_GM_CHAT, Console::No }, + { "fly", HandleGMFlyCommand, rbac::RBAC_PERM_COMMAND_GM_FLY, Console::No }, + { "ingame", HandleGMListIngameCommand, rbac::RBAC_PERM_COMMAND_GM_INGAME, Console::Yes }, + { "list", HandleGMListFullCommand, rbac::RBAC_PERM_COMMAND_GM_LIST, Console::Yes }, + { "visible", HandleGMVisibleCommand, rbac::RBAC_PERM_COMMAND_GM_VISIBLE, Console::No }, + { "on", HandleGMOnCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No }, + { "off", HandleGMOffCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No }, + { "spectator", HandleGMSpectatorCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No }, }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 88a7df172..7f575ac17 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -22,6 +22,7 @@ #include "MapMgr.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "TicketMgr.h" #include "Transport.h" @@ -39,19 +40,19 @@ public: { static ChatCommandTable goCommandTable = { - { "creature", HandleGoCreatureSpawnIdCommand, SEC_MODERATOR, Console::No }, - { "creature id", HandleGoCreatureCIdCommand, SEC_MODERATOR, Console::No }, - { "creature name", HandleGoCreatureNameCommand, SEC_MODERATOR, Console::No }, - { "gameobject", HandleGoGameObjectSpawnIdCommand, SEC_MODERATOR, Console::No }, - { "gameobject id", HandleGoGameObjectGOIdCommand, SEC_MODERATOR, Console::No }, - { "graveyard", HandleGoGraveyardCommand, SEC_MODERATOR, Console::No }, - { "grid", HandleGoGridCommand, SEC_MODERATOR, Console::No }, - { "taxinode", HandleGoTaxinodeCommand, SEC_MODERATOR, Console::No }, - { "trigger", HandleGoTriggerCommand, SEC_MODERATOR, Console::No }, - { "zonexy", HandleGoZoneXYCommand, SEC_MODERATOR, Console::No }, - { "xyz", HandleGoXYZCommand, SEC_MODERATOR, Console::No }, - { "ticket", HandleGoTicketCommand, SEC_GAMEMASTER, Console::No }, - { "quest", HandleGoQuestCommand, SEC_MODERATOR, Console::No }, + { "creature", HandleGoCreatureSpawnIdCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "creature id", HandleGoCreatureCIdCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "creature name", HandleGoCreatureNameCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "gameobject", HandleGoGameObjectSpawnIdCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "gameobject id", HandleGoGameObjectGOIdCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "graveyard", HandleGoGraveyardCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "grid", HandleGoGridCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "taxinode", HandleGoTaxinodeCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "trigger", HandleGoTriggerCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "zonexy", HandleGoZoneXYCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "xyz", HandleGoXYZCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "ticket", HandleGoTicketCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, + { "quest", HandleGoQuestCommand, rbac::RBAC_PERM_COMMAND_GO, Console::No }, }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index 79ce132bd..1bfed4f58 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -28,6 +28,7 @@ #include "ObjectMgr.h" #include "Player.h" #include "PoolMgr.h" +#include "RBAC.h" #include "Transport.h" #include @@ -45,21 +46,21 @@ public: { static ChatCommandTable gobjectCommandTable = { - { "activate", HandleGameObjectActivateCommand, SEC_GAMEMASTER, Console::No }, - { "delete", HandleGameObjectDeleteCommand, SEC_ADMINISTRATOR, Console::No }, - { "info", HandleGameObjectInfoCommand, SEC_MODERATOR, Console::No }, - { "move", HandleGameObjectMoveCommand, SEC_ADMINISTRATOR, Console::No }, - { "near", HandleGameObjectNearCommand, SEC_MODERATOR, Console::No }, - { "target", HandleGameObjectTargetCommand, SEC_MODERATOR, Console::No }, - { "turn", HandleGameObjectTurnCommand, SEC_ADMINISTRATOR, Console::No }, - { "add temp", HandleGameObjectAddTempCommand, SEC_GAMEMASTER, Console::No }, - { "add", HandleGameObjectAddCommand, SEC_ADMINISTRATOR, Console::No }, - { "load", HandleGameObjectLoadCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "set phase", HandleGameObjectSetPhaseCommand, SEC_ADMINISTRATOR, Console::No }, - { "set state", HandleGameObjectSetStateCommand, SEC_ADMINISTRATOR, Console::No }, - { "respawn", HandleGameObjectRespawn, SEC_GAMEMASTER, Console::No }, - { "spawngroup", HandleGameObjectSpawnGroupCommand, SEC_ADMINISTRATOR, Console::No }, - { "despawngroup", HandleGameObjectDespawnGroupCommand, SEC_ADMINISTRATOR, Console::No } + { "activate", HandleGameObjectActivateCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_ACTIVATE, Console::No }, + { "delete", HandleGameObjectDeleteCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_DELETE, Console::No }, + { "info", HandleGameObjectInfoCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_INFO, Console::No }, + { "move", HandleGameObjectMoveCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_MOVE, Console::No }, + { "near", HandleGameObjectNearCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_NEAR, Console::No }, + { "target", HandleGameObjectTargetCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_TARGET, Console::No }, + { "turn", HandleGameObjectTurnCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_TURN, Console::No }, + { "add temp", HandleGameObjectAddTempCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_ADD_TEMP, Console::No }, + { "add", HandleGameObjectAddCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_ADD, Console::No }, + { "load", HandleGameObjectLoadCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_LOAD, Console::Yes }, + { "set phase", HandleGameObjectSetPhaseCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_SET_PHASE, Console::No }, + { "set state", HandleGameObjectSetStateCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_SET_STATE, Console::No }, + { "respawn", HandleGameObjectRespawn, rbac::RBAC_PERM_COMMAND_GOBJECT_ACTIVATE, Console::No }, + { "spawngroup", HandleGameObjectSpawnGroupCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_ADD, Console::No }, + { "despawngroup", HandleGameObjectDespawnGroupCommand, rbac::RBAC_PERM_COMMAND_GOBJECT_DELETE, Console::No } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_group.cpp b/src/server/scripts/Commands/cs_group.cpp index 7be8d6674..83b4e001a 100644 --- a/src/server/scripts/Commands/cs_group.cpp +++ b/src/server/scripts/Commands/cs_group.cpp @@ -20,6 +20,7 @@ #include "GroupMgr.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -32,12 +33,12 @@ public: { static ChatCommandTable groupCommandTable = { - { "list", HandleGroupListCommand, SEC_GAMEMASTER, Console::Yes }, - { "join", HandleGroupJoinCommand, SEC_GAMEMASTER, Console::No }, - { "remove", HandleGroupRemoveCommand, SEC_GAMEMASTER, Console::No }, - { "disband", HandleGroupDisbandCommand, SEC_GAMEMASTER, Console::No }, - { "revive", HandleGroupReviveCommand, SEC_GAMEMASTER, Console::No }, - { "leader", HandleGroupLeaderCommand, SEC_GAMEMASTER, Console::No } + { "list", HandleGroupListCommand, rbac::RBAC_PERM_COMMAND_GROUP_LIST, Console::Yes }, + { "join", HandleGroupJoinCommand, rbac::RBAC_PERM_COMMAND_GROUP_JOIN, Console::No }, + { "remove", HandleGroupRemoveCommand, rbac::RBAC_PERM_COMMAND_GROUP_REMOVE, Console::No }, + { "disband", HandleGroupDisbandCommand, rbac::RBAC_PERM_COMMAND_GROUP_DISBAND, Console::No }, + { "revive", HandleGroupReviveCommand, rbac::RBAC_PERM_COMMAND_GROUP_REVIVE, Console::No }, + { "leader", HandleGroupLeaderCommand, rbac::RBAC_PERM_COMMAND_GROUP_LEADER, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_guild.cpp b/src/server/scripts/Commands/cs_guild.cpp index 45c55ddf0..1980344de 100644 --- a/src/server/scripts/Commands/cs_guild.cpp +++ b/src/server/scripts/Commands/cs_guild.cpp @@ -19,6 +19,7 @@ #include "CommandScript.h" #include "Guild.h" #include "GuildMgr.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -31,13 +32,13 @@ public: { static ChatCommandTable guildCommandTable = { - { "create", HandleGuildCreateCommand, SEC_GAMEMASTER, Console::Yes }, - { "delete", HandleGuildDeleteCommand, SEC_GAMEMASTER, Console::Yes }, - { "invite", HandleGuildInviteCommand, SEC_GAMEMASTER, Console::Yes }, - { "uninvite", HandleGuildUninviteCommand, SEC_GAMEMASTER, Console::Yes }, - { "rank", HandleGuildRankCommand, SEC_GAMEMASTER, Console::Yes }, - { "rename", HandleGuildRenameCommand, SEC_GAMEMASTER, Console::Yes }, - { "info", HandleGuildInfoCommand, SEC_GAMEMASTER, Console::Yes } + { "create", HandleGuildCreateCommand, rbac::RBAC_PERM_COMMAND_GUILD_CREATE, Console::Yes }, + { "delete", HandleGuildDeleteCommand, rbac::RBAC_PERM_COMMAND_GUILD_DELETE, Console::Yes }, + { "invite", HandleGuildInviteCommand, rbac::RBAC_PERM_COMMAND_GUILD_INVITE, Console::Yes }, + { "uninvite", HandleGuildUninviteCommand, rbac::RBAC_PERM_COMMAND_GUILD_UNINVITE, Console::Yes }, + { "rank", HandleGuildRankCommand, rbac::RBAC_PERM_COMMAND_GUILD_RANK, Console::Yes }, + { "rename", HandleGuildRenameCommand, rbac::RBAC_PERM_COMMAND_GUILD_RENAME, Console::Yes }, + { "info", HandleGuildInfoCommand, rbac::RBAC_PERM_COMMAND_GUILD_INFO, Console::Yes } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_honor.cpp b/src/server/scripts/Commands/cs_honor.cpp index af83d0b9f..f96f0c92d 100644 --- a/src/server/scripts/Commands/cs_honor.cpp +++ b/src/server/scripts/Commands/cs_honor.cpp @@ -19,6 +19,8 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" +#include "WorldSession.h" using namespace Acore::ChatCommands; @@ -31,14 +33,14 @@ public: { static ChatCommandTable honorAddCommandTable = { - { "kill", HandleHonorAddKillCommand, SEC_GAMEMASTER, Console::No }, - { "", HandleHonorAddCommand, SEC_GAMEMASTER, Console::No } + { "kill", HandleHonorAddKillCommand, rbac::RBAC_PERM_COMMAND_HONOR_ADD_KILL, Console::No }, + { "", HandleHonorAddCommand, rbac::RBAC_PERM_COMMAND_HONOR_ADD, Console::No } }; static ChatCommandTable honorCommandTable = { { "add", honorAddCommandTable }, - { "update", HandleHonorUpdateCommand, SEC_GAMEMASTER, Console::No } + { "update", HandleHonorUpdateCommand, rbac::RBAC_PERM_COMMAND_HONOR_UPDATE, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp index 078594a8a..009624f79 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -25,6 +25,7 @@ #include "MapMgr.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -37,12 +38,12 @@ public: { static ChatCommandTable instanceCommandTable = { - { "listbinds", HandleInstanceListBindsCommand, SEC_MODERATOR, Console::No }, - { "unbind", HandleInstanceUnbindCommand, SEC_GAMEMASTER, Console::No }, - { "stats", HandleInstanceStatsCommand, SEC_MODERATOR, Console::Yes }, - { "savedata", HandleInstanceSaveDataCommand, SEC_ADMINISTRATOR, Console::No }, - { "setbossstate", HandleInstanceSetBossStateCommand, SEC_GAMEMASTER, Console::Yes }, - { "getbossstate", HandleInstanceGetBossStateCommand, SEC_MODERATOR, Console::Yes }, + { "listbinds", HandleInstanceListBindsCommand, rbac::RBAC_PERM_COMMAND_INSTANCE_LISTBINDS, Console::No }, + { "unbind", HandleInstanceUnbindCommand, rbac::RBAC_PERM_COMMAND_INSTANCE_UNBIND, Console::No }, + { "stats", HandleInstanceStatsCommand, rbac::RBAC_PERM_COMMAND_INSTANCE_STATS, Console::Yes }, + { "savedata", HandleInstanceSaveDataCommand, rbac::RBAC_PERM_COMMAND_INSTANCE_SAVEDATA, Console::No }, + { "setbossstate", HandleInstanceSetBossStateCommand, rbac::RBAC_PERM_COMMAND_INSTANCE_SET_BOSS_STATE, Console::Yes }, + { "getbossstate", HandleInstanceGetBossStateCommand, rbac::RBAC_PERM_COMMAND_INSTANCE_GET_BOSS_STATE, Console::Yes }, }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_inventory.cpp b/src/server/scripts/Commands/cs_inventory.cpp index 43fae2e91..80ece0d0d 100644 --- a/src/server/scripts/Commands/cs_inventory.cpp +++ b/src/server/scripts/Commands/cs_inventory.cpp @@ -19,6 +19,8 @@ #include "CommandScript.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" +#include "WorldSession.h" constexpr std::array bagSpecsToString = { @@ -70,7 +72,7 @@ public: { static ChatCommandTable inventoryCommandTable = { - { "count", HandleInventoryCountCommand, SEC_MODERATOR, Console::No } + { "count", HandleInventoryCountCommand, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_item.cpp b/src/server/scripts/Commands/cs_item.cpp index 711f2532e..e866443fc 100644 --- a/src/server/scripts/Commands/cs_item.cpp +++ b/src/server/scripts/Commands/cs_item.cpp @@ -22,6 +22,7 @@ #include "Language.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -34,14 +35,14 @@ public: { static ChatCommandTable HandleItemRestoreCommandTable = { - { "list", HandleItemRestoreListCommand, SEC_GAMEMASTER, Console::Yes }, - { "", HandleItemRestoreCommand, SEC_GAMEMASTER, Console::Yes }, + { "list", HandleItemRestoreListCommand, rbac::RBAC_PERM_COMMAND_ITEM_RESTORE_LIST, Console::Yes }, + { "", HandleItemRestoreCommand, rbac::RBAC_PERM_COMMAND_ITEM_RESTORE, Console::Yes }, }; static ChatCommandTable itemCommandTable = { { "restore", HandleItemRestoreCommandTable }, - { "move", HandleItemMoveCommand, SEC_GAMEMASTER, Console::Yes }, - { "refund", HandleItemRefundCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "move", HandleItemMoveCommand, rbac::RBAC_PERM_COMMAND_ITEMMOVE, Console::Yes }, + { "refund", HandleItemRefundCommand, rbac::RBAC_PERM_COMMAND_ITEM_REFUND, Console::Yes }, }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_learn.cpp b/src/server/scripts/Commands/cs_learn.cpp index 1b5cc4b89..b86e56662 100644 --- a/src/server/scripts/Commands/cs_learn.cpp +++ b/src/server/scripts/Commands/cs_learn.cpp @@ -21,6 +21,7 @@ #include "Pet.h" #include "Player.h" #include "PlayerCommand.h" +#include "RBAC.h" #include "SpellInfo.h" #include "SpellMgr.h" @@ -35,33 +36,33 @@ public: { static ChatCommandTable learnAllMyCommandTable = { - { "class", HandleLearnAllMyClassCommand, SEC_GAMEMASTER, Console::No }, - { "pettalents", HandleLearnAllMyPetTalentsCommand, SEC_GAMEMASTER, Console::No }, - { "trainer", HandleLearnAllMyTrainerSpellsCommand, SEC_GAMEMASTER, Console::No }, - { "talents", HandleLearnAllMyTalentsCommand, SEC_GAMEMASTER, Console::No }, - { "quest", HandleLearnAllMyQuestSpells, SEC_GAMEMASTER, Console::No } + { "class", HandleLearnAllMyClassCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_MY_CLASS, Console::No }, + { "pettalents", HandleLearnAllMyPetTalentsCommand, rbac::RBAC_PERM_COMMAND_LEARN_MY_PETTALENTS, Console::No }, + { "trainer", HandleLearnAllMyTrainerSpellsCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_MY_SPELLS, Console::No }, + { "talents", HandleLearnAllMyTalentsCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_TALENTS, Console::No }, + { "quest", HandleLearnAllMyQuestSpells, rbac::RBAC_PERM_COMMAND_LEARN_ALL_MY_SPELLS, Console::No } }; static ChatCommandTable learnAllCommandTable = { { "my", learnAllMyCommandTable }, - { "gm", HandleLearnAllGMCommand, SEC_GAMEMASTER, Console::No }, - { "crafts", HandleLearnAllCraftsCommand, SEC_GAMEMASTER, Console::No }, - { "default", HandleLearnAllDefaultCommand, SEC_GAMEMASTER, Console::No }, - { "lang", HandleLearnAllLangCommand, SEC_GAMEMASTER, Console::No }, - { "recipes", HandleLearnAllRecipesCommand, SEC_GAMEMASTER, Console::No }, + { "gm", HandleLearnAllGMCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_GM, Console::No }, + { "crafts", HandleLearnAllCraftsCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_CRAFTS, Console::No }, + { "default", HandleLearnAllDefaultCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_DEFAULT, Console::No }, + { "lang", HandleLearnAllLangCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_LANG, Console::No }, + { "recipes", HandleLearnAllRecipesCommand, rbac::RBAC_PERM_COMMAND_LEARN_ALL_RECIPES, Console::No }, }; static ChatCommandTable learnCommandTable = { { "all", learnAllCommandTable }, - { "", HandleLearnCommand, SEC_GAMEMASTER, Console::No } + { "", HandleLearnCommand, rbac::RBAC_PERM_COMMAND_LEARN, Console::No } }; static ChatCommandTable commandTable = { { "learn", learnCommandTable }, - { "unlearn", HandleUnLearnCommand, SEC_GAMEMASTER, Console::No } + { "unlearn", HandleUnLearnCommand, rbac::RBAC_PERM_COMMAND_UNLEARN, Console::No } }; return commandTable; } diff --git a/src/server/scripts/Commands/cs_lfg.cpp b/src/server/scripts/Commands/cs_lfg.cpp index 693ab1585..c147d64f0 100644 --- a/src/server/scripts/Commands/cs_lfg.cpp +++ b/src/server/scripts/Commands/cs_lfg.cpp @@ -21,6 +21,7 @@ #include "LFGMgr.h" #include "Language.h" #include "Player.h" +#include "RBAC.h" void GetPlayerInfo(ChatHandler* handler, Player* player) { @@ -47,12 +48,12 @@ public: { static ChatCommandTable lfgCommandTable = { - { "player", HandleLfgPlayerInfoCommand, SEC_MODERATOR, Console::No }, - { "group", HandleLfgGroupInfoCommand, SEC_MODERATOR, Console::No }, - { "queue", HandleLfgQueueInfoCommand, SEC_MODERATOR, Console::Yes }, - { "clean", HandleLfgCleanCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "options", HandleLfgOptionsCommand, SEC_GAMEMASTER, Console::Yes }, - { "cooldown", HandleLfgCooldownClearCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "player", HandleLfgPlayerInfoCommand, rbac::RBAC_PERM_COMMAND_LFG_PLAYER, Console::No }, + { "group", HandleLfgGroupInfoCommand, rbac::RBAC_PERM_COMMAND_LFG_GROUP, Console::No }, + { "queue", HandleLfgQueueInfoCommand, rbac::RBAC_PERM_COMMAND_LFG_QUEUE, Console::Yes }, + { "clean", HandleLfgCleanCommand, rbac::RBAC_PERM_COMMAND_LFG_CLEAN, Console::Yes }, + { "options", HandleLfgOptionsCommand, rbac::RBAC_PERM_COMMAND_LFG_OPTIONS, Console::Yes }, + { "cooldown", HandleLfgCooldownClearCommand, rbac::RBAC_PERM_COMMAND_LFG_COOLDOWN, Console::Yes }, }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_list.cpp b/src/server/scripts/Commands/cs_list.cpp index 84b75bd1a..c098a7e30 100644 --- a/src/server/scripts/Commands/cs_list.cpp +++ b/src/server/scripts/Commands/cs_list.cpp @@ -26,6 +26,7 @@ #include "MapMgr.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "SpellAuraEffects.h" using namespace Acore::ChatCommands; @@ -39,18 +40,18 @@ public: { static ChatCommandTable listAurasCommandTable = { - { "", HandleListAllAurasCommand, SEC_MODERATOR, Console::No }, - { "id", HandleListAurasByIdCommand, SEC_MODERATOR, Console::No }, - { "name", HandleListAurasByNameCommand, SEC_MODERATOR, Console::No }, + { "", HandleListAllAurasCommand, rbac::RBAC_PERM_COMMAND_LIST_AURAS, Console::No }, + { "id", HandleListAurasByIdCommand, rbac::RBAC_PERM_COMMAND_LIST_AURAS, Console::No }, + { "name", HandleListAurasByNameCommand, rbac::RBAC_PERM_COMMAND_LIST_AURAS, Console::No }, }; static ChatCommandTable listCommandTable = { - { "creature", HandleListCreatureCommand, SEC_MODERATOR, Console::Yes }, - { "item", HandleListItemCommand, SEC_MODERATOR, Console::Yes }, - { "object", HandleListObjectCommand, SEC_MODERATOR, Console::Yes }, - { "auras", listAurasCommandTable }, - { "respawns", HandleListRespawnsCommand, SEC_GAMEMASTER, Console::No }, + { "creature", HandleListCreatureCommand, rbac::RBAC_PERM_COMMAND_LIST_CREATURE, Console::Yes }, + { "item", HandleListItemCommand, rbac::RBAC_PERM_COMMAND_LIST_ITEM, Console::Yes }, + { "object", HandleListObjectCommand, rbac::RBAC_PERM_COMMAND_LIST_OBJECT, Console::Yes }, + { "auras", listAurasCommandTable }, + { "respawns", HandleListRespawnsCommand, rbac::RBAC_PERM_COMMAND_LIST_CREATURE, Console::No }, }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp index 5dd54c3eb..4fb3c66ab 100644 --- a/src/server/scripts/Commands/cs_lookup.cpp +++ b/src/server/scripts/Commands/cs_lookup.cpp @@ -22,6 +22,7 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "ReputationMgr.h" #include "SharedDefines.h" #include "SpellInfo.h" @@ -38,29 +39,29 @@ public: { static ChatCommandTable lookupPlayerCommandTable = { - { "ip", HandleLookupPlayerIpCommand, SEC_GAMEMASTER, Console::Yes }, - { "account", HandleLookupPlayerAccountCommand, SEC_GAMEMASTER, Console::Yes }, - { "email", HandleLookupPlayerEmailCommand, SEC_GAMEMASTER, Console::Yes } + { "ip", HandleLookupPlayerIpCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_PLAYER_IP, Console::Yes }, + { "account", HandleLookupPlayerAccountCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_PLAYER_ACCOUNT, Console::Yes }, + { "email", HandleLookupPlayerEmailCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_PLAYER_EMAIL, Console::Yes } }; static ChatCommandTable lookupCommandTable = { - { "area", HandleLookupAreaCommand, SEC_MODERATOR, Console::Yes }, - { "creature", HandleLookupCreatureCommand, SEC_MODERATOR, Console::Yes }, - { "event", HandleLookupEventCommand, SEC_MODERATOR, Console::Yes }, - { "faction", HandleLookupFactionCommand, SEC_MODERATOR, Console::Yes }, - { "item", HandleLookupItemCommand, SEC_MODERATOR, Console::Yes }, - { "item set", HandleLookupItemSetCommand, SEC_MODERATOR, Console::Yes }, - { "map", HandleLookupMapCommand, SEC_MODERATOR, Console::Yes }, - { "object", HandleLookupObjectCommand, SEC_MODERATOR, Console::Yes }, - { "gobject", HandleLookupObjectCommand, SEC_MODERATOR, Console::Yes }, - { "quest", HandleLookupQuestCommand, SEC_MODERATOR, Console::Yes }, - { "skill", HandleLookupSkillCommand, SEC_MODERATOR, Console::Yes }, - { "taxinode", HandleLookupTaxiNodeCommand, SEC_MODERATOR, Console::Yes }, - { "teleport", HandleLookupTeleCommand, SEC_MODERATOR, Console::Yes }, - { "title", HandleLookupTitleCommand, SEC_MODERATOR, Console::Yes }, - { "spell", HandleLookupSpellCommand, SEC_MODERATOR, Console::Yes }, - { "spell id", HandleLookupSpellIdCommand, SEC_MODERATOR, Console::Yes }, + { "area", HandleLookupAreaCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_AREA, Console::Yes }, + { "creature", HandleLookupCreatureCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_CREATURE, Console::Yes }, + { "event", HandleLookupEventCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_EVENT, Console::Yes }, + { "faction", HandleLookupFactionCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_FACTION, Console::Yes }, + { "item", HandleLookupItemCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_ITEM, Console::Yes }, + { "item set", HandleLookupItemSetCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_ITEMSET, Console::Yes }, + { "map", HandleLookupMapCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_MAP, Console::Yes }, + { "object", HandleLookupObjectCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_OBJECT, Console::Yes }, + { "gobject", HandleLookupObjectCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_OBJECT, Console::Yes }, + { "quest", HandleLookupQuestCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_QUEST, Console::Yes }, + { "skill", HandleLookupSkillCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_SKILL, Console::Yes }, + { "taxinode", HandleLookupTaxiNodeCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_TAXINODE, Console::Yes }, + { "teleport", HandleLookupTeleCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_TELE, Console::Yes }, + { "title", HandleLookupTitleCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_TITLE, Console::Yes }, + { "spell", HandleLookupSpellCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_SPELL, Console::Yes }, + { "spell id", HandleLookupSpellIdCommand, rbac::RBAC_PERM_COMMAND_LOOKUP_SPELL_ID, Console::Yes }, { "player", lookupPlayerCommandTable }, }; diff --git a/src/server/scripts/Commands/cs_message.cpp b/src/server/scripts/Commands/cs_message.cpp index b6f051ee8..d03d72e20 100644 --- a/src/server/scripts/Commands/cs_message.cpp +++ b/src/server/scripts/Commands/cs_message.cpp @@ -23,6 +23,7 @@ #include "ObjectAccessor.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "World.h" #include "WorldSession.h" #include "WorldSessionMgr.h" @@ -38,13 +39,13 @@ public: { static ChatCommandTable commandTable = { - { "nameannounce", HandleNameAnnounceCommand, SEC_GAMEMASTER, Console::Yes }, - { "gmnameannounce", HandleGMNameAnnounceCommand, SEC_GAMEMASTER, Console::Yes }, - { "announce", HandleAnnounceCommand, SEC_GAMEMASTER, Console::Yes }, - { "gmannounce", HandleGMAnnounceCommand, SEC_GAMEMASTER, Console::Yes }, - { "notify", HandleNotifyCommand, SEC_GAMEMASTER, Console::Yes }, - { "gmnotify", HandleGMNotifyCommand, SEC_GAMEMASTER, Console::Yes }, - { "whispers", HandleWhispersCommand, SEC_MODERATOR, Console::No }, + { "nameannounce", HandleNameAnnounceCommand, rbac::RBAC_PERM_COMMAND_NAMEANNOUNCE, Console::Yes }, + { "gmnameannounce", HandleGMNameAnnounceCommand, rbac::RBAC_PERM_COMMAND_GMNAMEANNOUNCE, Console::Yes }, + { "announce", HandleAnnounceCommand, rbac::RBAC_PERM_COMMAND_ANNOUNCE, Console::Yes }, + { "gmannounce", HandleGMAnnounceCommand, rbac::RBAC_PERM_COMMAND_GMANNOUNCE, Console::Yes }, + { "notify", HandleNotifyCommand, rbac::RBAC_PERM_COMMAND_NOTIFY, Console::Yes }, + { "gmnotify", HandleGMNotifyCommand, rbac::RBAC_PERM_COMMAND_GMNOTIFY, Console::Yes }, + { "whispers", HandleWhispersCommand, rbac::RBAC_PERM_COMMAND_WHISPERS, Console::No }, }; return commandTable; } diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index c28a5de5c..961e9a5b9 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -41,6 +41,7 @@ #include "Pet.h" #include "Player.h" #include "PoolMgr.h" +#include "RBAC.h" #include "Realm.h" #include "ScriptMgr.h" #include "SpellAuras.h" @@ -137,66 +138,66 @@ public: { static ChatCommandTable auraCommandTable = { - { "stack", HandleAuraStacksCommand, SEC_GAMEMASTER, Console::No }, - { "", HandleAuraCommand, SEC_GAMEMASTER, Console::No } + { "stack", HandleAuraStacksCommand, rbac::RBAC_PERM_COMMAND_AURA_STACK, Console::No }, + { "", HandleAuraCommand, rbac::RBAC_PERM_COMMAND_AURA, Console::No } }; static ChatCommandTable commandTable = { - { "commentator", HandleCommentatorCommand, SEC_MODERATOR, Console::No }, - { "dev", HandleDevCommand, SEC_ADMINISTRATOR, Console::No }, - { "gps", HandleGPSCommand, SEC_MODERATOR, Console::No }, - { "aura", auraCommandTable }, - { "unaura", HandleUnAuraCommand, SEC_GAMEMASTER, Console::No }, - { "appear", HandleAppearCommand, SEC_MODERATOR, Console::No }, - { "summon", HandleSummonCommand, SEC_GAMEMASTER, Console::No }, - { "groupsummon", HandleGroupSummonCommand, SEC_GAMEMASTER, Console::No }, - { "commands", HandleCommandsCommand, SEC_PLAYER, Console::Yes }, - { "die", HandleDieCommand, SEC_GAMEMASTER, Console::No }, - { "revive", HandleReviveCommand, SEC_GAMEMASTER, Console::Yes }, - { "dismount", HandleDismountCommand, SEC_PLAYER, Console::No }, - { "guid", HandleGUIDCommand, SEC_GAMEMASTER, Console::No }, - { "help", HandleHelpCommand, SEC_PLAYER, Console::Yes }, - { "cooldown", HandleCooldownCommand, SEC_GAMEMASTER, Console::No }, - { "distance", HandleGetDistanceCommand, SEC_ADMINISTRATOR, Console::No }, - { "recall", HandleRecallCommand, SEC_GAMEMASTER, Console::No }, - { "save", HandleSaveCommand, SEC_PLAYER, Console::No }, - { "saveall", HandleSaveAllCommand, SEC_GAMEMASTER, Console::Yes }, - { "kick", HandleKickPlayerCommand, SEC_GAMEMASTER, Console::Yes }, - { "unstuck", HandleUnstuckCommand, SEC_GAMEMASTER, Console::Yes }, - { "linkgrave", HandleLinkGraveCommand, SEC_ADMINISTRATOR, Console::No }, - { "neargrave", HandleNearGraveCommand, SEC_GAMEMASTER, Console::No }, - { "showarea", HandleShowAreaCommand, SEC_GAMEMASTER, Console::No }, - { "hidearea", HandleHideAreaCommand, SEC_ADMINISTRATOR, Console::No }, - { "additem", HandleAddItemCommand, SEC_GAMEMASTER, Console::Yes }, - { "additem set", HandleAddItemSetCommand, SEC_GAMEMASTER, Console::No }, - { "wchange", HandleChangeWeather, SEC_ADMINISTRATOR, Console::No }, - { "maxskill", HandleMaxSkillCommand, SEC_GAMEMASTER, Console::No }, - { "setskill", HandleSetSkillCommand, SEC_GAMEMASTER, Console::No }, - { "pinfo", HandlePInfoCommand, SEC_GAMEMASTER, Console::Yes }, - { "respawn", HandleRespawnCommand, SEC_GAMEMASTER, Console::No }, - { "respawn all", HandleRespawnAllCommand, SEC_GAMEMASTER, Console::No }, - { "mute", HandleMuteCommand, SEC_GAMEMASTER, Console::Yes }, - { "mutehistory", HandleMuteInfoCommand, SEC_GAMEMASTER, Console::Yes }, - { "unmute", HandleUnmuteCommand, SEC_GAMEMASTER, Console::Yes }, - { "movegens", HandleMovegensCommand, SEC_ADMINISTRATOR, Console::No }, - { "cometome", HandleComeToMeCommand, SEC_ADMINISTRATOR, Console::No }, - { "damage", HandleDamageCommand, SEC_GAMEMASTER, Console::No }, - { "combatstop", HandleCombatStopCommand, SEC_GAMEMASTER, Console::Yes }, - { "flusharenapoints", HandleFlushArenaPointsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "freeze", HandleFreezeCommand, SEC_GAMEMASTER, Console::No }, - { "unfreeze", HandleUnFreezeCommand, SEC_GAMEMASTER, Console::No }, - { "possess", HandlePossessCommand, SEC_GAMEMASTER, Console::No }, - { "unpossess", HandleUnPossessCommand, SEC_GAMEMASTER, Console::No }, - { "bindsight", HandleBindSightCommand, SEC_ADMINISTRATOR, Console::No }, - { "unbindsight", HandleUnbindSightCommand, SEC_ADMINISTRATOR, Console::No }, - { "playall", HandlePlayAllCommand, SEC_GAMEMASTER, Console::No }, - { "skirmish", HandleSkirmishCommand, SEC_ADMINISTRATOR, Console::No }, - { "mailbox", HandleMailBoxCommand, SEC_MODERATOR, Console::No }, - { "string", HandleStringCommand, SEC_GAMEMASTER, Console::No }, - { "opendoor", HandleOpenDoorCommand, SEC_GAMEMASTER, Console::No }, - { "bm", HandleBMCommand, SEC_GAMEMASTER, Console::No }, - { "packetlog", HandlePacketLog, SEC_GAMEMASTER, Console::Yes } + { "commentator", HandleCommentatorCommand, rbac::RBAC_PERM_COMMAND_COMMENTATOR, Console::No }, + { "dev", HandleDevCommand, rbac::RBAC_PERM_COMMAND_DEV, Console::No }, + { "gps", HandleGPSCommand, rbac::RBAC_PERM_COMMAND_GPS, Console::No }, + { "aura", auraCommandTable }, + { "unaura", HandleUnAuraCommand, rbac::RBAC_PERM_COMMAND_UNAURA, Console::No }, + { "appear", HandleAppearCommand, rbac::RBAC_PERM_COMMAND_APPEAR, Console::No }, + { "summon", HandleSummonCommand, rbac::RBAC_PERM_COMMAND_SUMMON, Console::No }, + { "groupsummon", HandleGroupSummonCommand, rbac::RBAC_PERM_COMMAND_GROUP_SUMMON, Console::No }, + { "commands", HandleCommandsCommand, rbac::RBAC_PERM_COMMAND_COMMANDS, Console::Yes }, + { "die", HandleDieCommand, rbac::RBAC_PERM_COMMAND_DIE, Console::No }, + { "revive", HandleReviveCommand, rbac::RBAC_PERM_COMMAND_REVIVE, Console::Yes }, + { "dismount", HandleDismountCommand, rbac::RBAC_PERM_COMMAND_DISMOUNT, Console::No }, + { "guid", HandleGUIDCommand, rbac::RBAC_PERM_COMMAND_GUID, Console::No }, + { "help", HandleHelpCommand, rbac::RBAC_PERM_COMMAND_HELP, Console::Yes }, + { "cooldown", HandleCooldownCommand, rbac::RBAC_PERM_COMMAND_COOLDOWN, Console::No }, + { "distance", HandleGetDistanceCommand, rbac::RBAC_PERM_COMMAND_DISTANCE, Console::No }, + { "recall", HandleRecallCommand, rbac::RBAC_PERM_COMMAND_RECALL, Console::No }, + { "save", HandleSaveCommand, rbac::RBAC_PERM_COMMAND_SAVE, Console::No }, + { "saveall", HandleSaveAllCommand, rbac::RBAC_PERM_COMMAND_SAVEALL, Console::Yes }, + { "kick", HandleKickPlayerCommand, rbac::RBAC_PERM_COMMAND_KICK, Console::Yes }, + { "unstuck", HandleUnstuckCommand, rbac::RBAC_PERM_COMMAND_UNSTUCK, Console::Yes }, + { "linkgrave", HandleLinkGraveCommand, rbac::RBAC_PERM_COMMAND_LINKGRAVE, Console::No }, + { "neargrave", HandleNearGraveCommand, rbac::RBAC_PERM_COMMAND_NEARGRAVE, Console::No }, + { "showarea", HandleShowAreaCommand, rbac::RBAC_PERM_COMMAND_SHOWAREA, Console::No }, + { "hidearea", HandleHideAreaCommand, rbac::RBAC_PERM_COMMAND_HIDEAREA, Console::No }, + { "additem", HandleAddItemCommand, rbac::RBAC_PERM_COMMAND_ADDITEM, Console::Yes }, + { "additem set", HandleAddItemSetCommand, rbac::RBAC_PERM_COMMAND_ADDITEMSET, Console::No }, + { "wchange", HandleChangeWeather, rbac::RBAC_PERM_COMMAND_WCHANGE, Console::No }, + { "maxskill", HandleMaxSkillCommand, rbac::RBAC_PERM_COMMAND_MAXSKILL, Console::No }, + { "setskill", HandleSetSkillCommand, rbac::RBAC_PERM_COMMAND_SETSKILL, Console::No }, + { "pinfo", HandlePInfoCommand, rbac::RBAC_PERM_COMMAND_PINFO, Console::Yes }, + { "respawn", HandleRespawnCommand, rbac::RBAC_PERM_COMMAND_RESPAWN, Console::No }, + { "respawn all", HandleRespawnAllCommand, rbac::RBAC_PERM_COMMAND_RESPAWN_ALL, Console::No }, + { "mute", HandleMuteCommand, rbac::RBAC_PERM_COMMAND_MUTE, Console::Yes }, + { "mutehistory", HandleMuteInfoCommand, rbac::RBAC_PERM_COMMAND_MUTEHISTORY, Console::Yes }, + { "unmute", HandleUnmuteCommand, rbac::RBAC_PERM_COMMAND_UNMUTE, Console::Yes }, + { "movegens", HandleMovegensCommand, rbac::RBAC_PERM_COMMAND_MOVEGENS, Console::No }, + { "cometome", HandleComeToMeCommand, rbac::RBAC_PERM_COMMAND_COMETOME, Console::No }, + { "damage", HandleDamageCommand, rbac::RBAC_PERM_COMMAND_DAMAGE, Console::No }, + { "combatstop", HandleCombatStopCommand, rbac::RBAC_PERM_COMMAND_COMBATSTOP, Console::Yes }, + { "flusharenapoints", HandleFlushArenaPointsCommand, rbac::RBAC_PERM_COMMAND_FLUSHARENAPOINTS, Console::Yes }, + { "freeze", HandleFreezeCommand, rbac::RBAC_PERM_COMMAND_FREEZE, Console::No }, + { "unfreeze", HandleUnFreezeCommand, rbac::RBAC_PERM_COMMAND_UNFREEZE, Console::No }, + { "possess", HandlePossessCommand, rbac::RBAC_PERM_COMMAND_POSSESS, Console::No }, + { "unpossess", HandleUnPossessCommand, rbac::RBAC_PERM_COMMAND_UNPOSSESS, Console::No }, + { "bindsight", HandleBindSightCommand, rbac::RBAC_PERM_COMMAND_BINDSIGHT, Console::No }, + { "unbindsight", HandleUnbindSightCommand, rbac::RBAC_PERM_COMMAND_UNBINDSIGHT, Console::No }, + { "playall", HandlePlayAllCommand, rbac::RBAC_PERM_COMMAND_PLAYALL, Console::No }, + { "skirmish", HandleSkirmishCommand, rbac::RBAC_PERM_COMMAND_SKIRMISH, Console::No }, + { "mailbox", HandleMailBoxCommand, rbac::RBAC_PERM_COMMAND_MAILBOX, Console::No }, + { "string", HandleStringCommand, rbac::RBAC_PERM_COMMAND_STRING, Console::No }, + { "opendoor", HandleOpenDoorCommand, rbac::RBAC_PERM_COMMAND_OPENDOOR, Console::No }, + { "bm", HandleBMCommand, rbac::RBAC_PERM_COMMAND_BEASTMASTER, Console::No }, + { "packetlog", HandlePacketLog, rbac::RBAC_PERM_COMMAND_PACKETLOG, Console::Yes } }; return commandTable; @@ -1397,7 +1398,7 @@ public: Player* player = handler->GetSession()->GetPlayer(); // save GM account without delay and output message - if (handler->GetSession()->GetSecurity() >= SEC_GAMEMASTER) + if (handler->GetSession()->HasPermission(rbac::RBAC_PERM_COMMANDS_SAVE_WITHOUT_DELAY)) { if (Player* target = handler->getSelectedPlayer()) { diff --git a/src/server/scripts/Commands/cs_mmaps.cpp b/src/server/scripts/Commands/cs_mmaps.cpp index 91a164a11..88cd0cd51 100644 --- a/src/server/scripts/Commands/cs_mmaps.cpp +++ b/src/server/scripts/Commands/cs_mmaps.cpp @@ -32,6 +32,7 @@ #include "Map.h" #include "PathGenerator.h" #include "Player.h" +#include "RBAC.h" #include "TargetedMovementGenerator.h" using namespace Acore::ChatCommands; @@ -45,11 +46,11 @@ public: { static ChatCommandTable mmapCommandTable = { - { "loadedtiles", HandleMmapLoadedTilesCommand, SEC_ADMINISTRATOR, Console::No }, - { "loc", HandleMmapLocCommand, SEC_ADMINISTRATOR, Console::No }, - { "path", HandleMmapPathCommand, SEC_ADMINISTRATOR, Console::No }, - { "stats", HandleMmapStatsCommand, SEC_ADMINISTRATOR, Console::No }, - { "testarea", HandleMmapTestArea, SEC_ADMINISTRATOR, Console::No } + { "loadedtiles", HandleMmapLoadedTilesCommand, rbac::RBAC_PERM_COMMAND_MMAP_LOADEDTILES, Console::No }, + { "loc", HandleMmapLocCommand, rbac::RBAC_PERM_COMMAND_MMAP_LOC, Console::No }, + { "path", HandleMmapPathCommand, rbac::RBAC_PERM_COMMAND_MMAP_PATH, Console::No }, + { "stats", HandleMmapStatsCommand, rbac::RBAC_PERM_COMMAND_MMAP_STATS, Console::No }, + { "testarea", HandleMmapTestArea, rbac::RBAC_PERM_COMMAND_MMAP_TESTAREA, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp index c2b2f6d80..058ca793e 100644 --- a/src/server/scripts/Commands/cs_modify.cpp +++ b/src/server/scripts/Commands/cs_modify.cpp @@ -21,6 +21,7 @@ #include "Opcodes.h" #include "Pet.h" #include "Player.h" +#include "RBAC.h" #include "ReputationMgr.h" #include "StringConvert.h" @@ -35,43 +36,43 @@ public: { static ChatCommandTable modifyspeedCommandTable = { - { "fly", HandleModifyFlyCommand, SEC_GAMEMASTER, Console::No }, - { "all", HandleModifyASpeedCommand, SEC_GAMEMASTER, Console::No }, - { "walk", HandleModifySpeedCommand, SEC_GAMEMASTER, Console::No }, - { "backwalk", HandleModifyBWalkCommand, SEC_GAMEMASTER, Console::No }, - { "swim", HandleModifySwimCommand, SEC_GAMEMASTER, Console::No }, - { "", HandleModifyASpeedCommand, SEC_GAMEMASTER, Console::No } + { "fly", HandleModifyFlyCommand, rbac::RBAC_PERM_COMMAND_MODIFY_SPEED_FLY, Console::No }, + { "all", HandleModifyASpeedCommand, rbac::RBAC_PERM_COMMAND_MODIFY_SPEED_ALL, Console::No }, + { "walk", HandleModifySpeedCommand, rbac::RBAC_PERM_COMMAND_MODIFY_SPEED_WALK, Console::No }, + { "backwalk", HandleModifyBWalkCommand, rbac::RBAC_PERM_COMMAND_MODIFY_SPEED_BACKWALK, Console::No }, + { "swim", HandleModifySwimCommand, rbac::RBAC_PERM_COMMAND_MODIFY_SPEED_SWIM, Console::No }, + { "", HandleModifyASpeedCommand, rbac::RBAC_PERM_COMMAND_MODIFY_SPEED, Console::No } }; static ChatCommandTable modifyCommandTable = { - { "hp", HandleModifyHPCommand, SEC_GAMEMASTER, Console::No }, - { "mana", HandleModifyManaCommand, SEC_GAMEMASTER, Console::No }, - { "rage", HandleModifyRageCommand, SEC_GAMEMASTER, Console::No }, - { "runicpower", HandleModifyRunicPowerCommand, SEC_GAMEMASTER, Console::No }, - { "energy", HandleModifyEnergyCommand, SEC_GAMEMASTER, Console::No }, - { "money", HandleModifyMoneyCommand, SEC_GAMEMASTER, Console::No }, - { "scale", HandleModifyScaleCommand, SEC_GAMEMASTER, Console::No }, - { "bit", HandleModifyBitCommand, SEC_GAMEMASTER, Console::No }, - { "faction", HandleModifyFactionCommand, SEC_ADMINISTRATOR, Console::No }, - { "spell", HandleModifySpellCommand, SEC_CONSOLE, Console::No }, - { "talentpoints", HandleModifyTalentCommand, SEC_GAMEMASTER, Console::No }, - { "mount", HandleModifyMountCommand, SEC_GAMEMASTER, Console::No }, - { "honor", HandleModifyHonorCommand, SEC_GAMEMASTER, Console::No }, - { "reputation", HandleModifyRepCommand, SEC_GAMEMASTER, Console::No }, - { "arenapoints", HandleModifyArenaCommand, SEC_GAMEMASTER, Console::No }, - { "drunk", HandleModifyDrunkCommand, SEC_GAMEMASTER, Console::No }, - { "standstate", HandleModifyStandStateCommand, SEC_GAMEMASTER, Console::No }, - { "phase", HandleModifyPhaseCommand, SEC_GAMEMASTER, Console::No }, - { "gender", HandleModifyGenderCommand, SEC_GAMEMASTER, Console::No }, + { "hp", HandleModifyHPCommand, rbac::RBAC_PERM_COMMAND_MODIFY_HP, Console::No }, + { "mana", HandleModifyManaCommand, rbac::RBAC_PERM_COMMAND_MODIFY_MANA, Console::No }, + { "rage", HandleModifyRageCommand, rbac::RBAC_PERM_COMMAND_MODIFY_RAGE, Console::No }, + { "runicpower", HandleModifyRunicPowerCommand, rbac::RBAC_PERM_COMMAND_MODIFY_RUNICPOWER, Console::No }, + { "energy", HandleModifyEnergyCommand, rbac::RBAC_PERM_COMMAND_MODIFY_ENERGY, Console::No }, + { "money", HandleModifyMoneyCommand, rbac::RBAC_PERM_COMMAND_MODIFY_MONEY, Console::No }, + { "scale", HandleModifyScaleCommand, rbac::RBAC_PERM_COMMAND_MODIFY_SCALE, Console::No }, + { "bit", HandleModifyBitCommand, rbac::RBAC_PERM_COMMAND_MODIFY_BIT, Console::No }, + { "faction", HandleModifyFactionCommand, rbac::RBAC_PERM_COMMAND_MODIFY_FACTION, Console::No }, + { "spell", HandleModifySpellCommand, rbac::RBAC_PERM_COMMAND_MODIFY_SPELL, Console::No }, + { "talentpoints", HandleModifyTalentCommand, rbac::RBAC_PERM_COMMAND_MODIFY_TALENTPOINTS, Console::No }, + { "mount", HandleModifyMountCommand, rbac::RBAC_PERM_COMMAND_MODIFY_MOUNT, Console::No }, + { "honor", HandleModifyHonorCommand, rbac::RBAC_PERM_COMMAND_MODIFY_HONOR, Console::No }, + { "reputation", HandleModifyRepCommand, rbac::RBAC_PERM_COMMAND_MODIFY_REPUTATION, Console::No }, + { "arenapoints", HandleModifyArenaCommand, rbac::RBAC_PERM_COMMAND_MODIFY_ARENAPOINTS, Console::No }, + { "drunk", HandleModifyDrunkCommand, rbac::RBAC_PERM_COMMAND_MODIFY_DRUNK, Console::No }, + { "standstate", HandleModifyStandStateCommand, rbac::RBAC_PERM_COMMAND_MODIFY_STANDSTATE, Console::No }, + { "phase", HandleModifyPhaseCommand, rbac::RBAC_PERM_COMMAND_MODIFY_PHASE, Console::No }, + { "gender", HandleModifyGenderCommand, rbac::RBAC_PERM_COMMAND_MODIFY_GENDER, Console::No }, { "speed", modifyspeedCommandTable } }; static ChatCommandTable morphCommandTable = { - { "reset", HandleMorphResetCommand, SEC_MODERATOR, Console::No }, - { "target", HandleMorphTargetCommand, SEC_MODERATOR, Console::No }, - { "mount", HandleMorphMountCommand, SEC_MODERATOR, Console::No } + { "reset", HandleMorphResetCommand, rbac::RBAC_PERM_COMMAND_DEMORPH, Console::No }, + { "target", HandleMorphTargetCommand, rbac::RBAC_PERM_COMMAND_MORPH, Console::No }, + { "mount", HandleMorphMountCommand, rbac::RBAC_PERM_COMMAND_MORPH, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 84b5f430b..594c447c0 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -29,6 +29,7 @@ #include "Pet.h" #include "Player.h" #include "PoolMgr.h" +#include "RBAC.h" #include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand #include "Transport.h" #include @@ -143,58 +144,58 @@ public: { static ChatCommandTable npcAddCommandTable = { - { "formation", HandleNpcAddFormationCommand, SEC_ADMINISTRATOR, Console::No }, - { "item", HandleNpcAddVendorItemCommand, SEC_ADMINISTRATOR, Console::No }, - { "move", HandleNpcAddMoveCommand, SEC_ADMINISTRATOR, Console::No }, - { "temp", HandleNpcAddTempSpawnCommand, SEC_ADMINISTRATOR, Console::No }, - { "", HandleNpcAddCommand, SEC_ADMINISTRATOR, Console::No } + { "formation", HandleNpcAddFormationCommand, rbac::RBAC_PERM_COMMAND_NPC_ADD_FORMATION, Console::No }, + { "item", HandleNpcAddVendorItemCommand, rbac::RBAC_PERM_COMMAND_NPC_ADD_ITEM, Console::No }, + { "move", HandleNpcAddMoveCommand, rbac::RBAC_PERM_COMMAND_NPC_ADD_MOVE, Console::No }, + { "temp", HandleNpcAddTempSpawnCommand, rbac::RBAC_PERM_COMMAND_NPC_ADD_TEMP, Console::No }, + { "", HandleNpcAddCommand, rbac::RBAC_PERM_COMMAND_NPC_ADD, Console::No } }; static ChatCommandTable npcDeleteCommandTable = { - { "item", HandleNpcDeleteVendorItemCommand, SEC_ADMINISTRATOR, Console::No }, - { "", HandleNpcDeleteCommand, SEC_ADMINISTRATOR, Console::No } + { "item", HandleNpcDeleteVendorItemCommand, rbac::RBAC_PERM_COMMAND_NPC_DELETE_ITEM, Console::No }, + { "", HandleNpcDeleteCommand, rbac::RBAC_PERM_COMMAND_NPC_DELETE, Console::No } }; static ChatCommandTable npcFollowCommandTable = { - { "stop", HandleNpcUnFollowCommand, SEC_GAMEMASTER, Console::No }, - { "", HandleNpcFollowCommand, SEC_GAMEMASTER, Console::No } + { "stop", HandleNpcUnFollowCommand, rbac::RBAC_PERM_COMMAND_NPC_FOLLOW_STOP, Console::No }, + { "", HandleNpcFollowCommand, rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, Console::No } }; static ChatCommandTable npcFactionCommandTable = { - { "permanent", HandleNpcSetFactionIdCommand, SEC_ADMINISTRATOR, Console::No }, - { "temp", HandleNpcSetFactionTempIdCommand, SEC_ADMINISTRATOR, Console::No }, - { "original", HandleNpcSetOriginalFaction, SEC_ADMINISTRATOR, Console::No } + { "permanent", HandleNpcSetFactionIdCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_FACTIONID, Console::No }, + { "temp", HandleNpcSetFactionTempIdCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_FACTIONID, Console::No }, + { "original", HandleNpcSetOriginalFaction, rbac::RBAC_PERM_COMMAND_NPC_SET_FACTIONID, Console::No } }; static ChatCommandTable npcSetCommandTable = { - { "allowmove", HandleNpcSetAllowMovementCommand, SEC_ADMINISTRATOR, Console::No }, - { "entry", HandleNpcSetEntryCommand, SEC_ADMINISTRATOR, Console::No }, + { "allowmove", HandleNpcSetAllowMovementCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_ALLOWMOVE, Console::No }, + { "entry", HandleNpcSetEntryCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_ENTRY, Console::No }, { "faction", npcFactionCommandTable}, - { "flag", HandleNpcSetFlagCommand, SEC_ADMINISTRATOR, Console::No }, - { "level", HandleNpcSetLevelCommand, SEC_ADMINISTRATOR, Console::No }, - { "link", HandleNpcSetLinkCommand, SEC_ADMINISTRATOR, Console::No }, - { "model", HandleNpcSetModelCommand, SEC_ADMINISTRATOR, Console::No }, - { "movetype", HandleNpcSetMoveTypeCommand, SEC_ADMINISTRATOR, Console::No }, - { "phase", HandleNpcSetPhaseCommand, SEC_ADMINISTRATOR, Console::No }, - { "wanderdistance", HandleNpcSetWanderDistanceCommand, SEC_ADMINISTRATOR, Console::No }, - { "spawntime", HandleNpcSetSpawnTimeCommand, SEC_ADMINISTRATOR, Console::No }, - { "data", HandleNpcSetDataCommand, SEC_ADMINISTRATOR, Console::No } + { "flag", HandleNpcSetFlagCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_FLAG, Console::No }, + { "level", HandleNpcSetLevelCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_LEVEL, Console::No }, + { "link", HandleNpcSetLinkCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_LINK, Console::No }, + { "model", HandleNpcSetModelCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_MODEL, Console::No }, + { "movetype", HandleNpcSetMoveTypeCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_MOVETYPE, Console::No }, + { "phase", HandleNpcSetPhaseCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_PHASE, Console::No }, + { "wanderdistance", HandleNpcSetWanderDistanceCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_SPAWNDIST, Console::No }, + { "spawntime", HandleNpcSetSpawnTimeCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_SPAWNTIME, Console::No }, + { "data", HandleNpcSetDataCommand, rbac::RBAC_PERM_COMMAND_NPC_SET_DATA, Console::No } }; static ChatCommandTable npcCommandTable = { - { "info", HandleNpcInfoCommand, SEC_GAMEMASTER, Console::No }, - { "guid", HandleNpcGuidCommand, SEC_GAMEMASTER, Console::No }, - { "near", HandleNpcNearCommand, SEC_GAMEMASTER, Console::No }, - { "move", HandleNpcMoveCommand, SEC_GAMEMASTER, Console::No }, - { "playemote", HandleNpcPlayEmoteCommand, SEC_GAMEMASTER, Console::No }, - { "say", HandleNpcSayCommand, SEC_GAMEMASTER, Console::No }, - { "textemote", HandleNpcTextEmoteCommand, SEC_GAMEMASTER, Console::No }, - { "whisper", HandleNpcWhisperCommand, SEC_GAMEMASTER, Console::No }, - { "yell", HandleNpcYellCommand, SEC_GAMEMASTER, Console::No }, - { "tame", HandleNpcTameCommand, SEC_GAMEMASTER, Console::No }, - { "do", HandleNpcDoActionCommand, SEC_GAMEMASTER, Console::No }, + { "info", HandleNpcInfoCommand, rbac::RBAC_PERM_COMMAND_NPC_INFO, Console::No }, + { "guid", HandleNpcGuidCommand, rbac::RBAC_PERM_COMMAND_NPC_INFO, Console::No }, + { "near", HandleNpcNearCommand, rbac::RBAC_PERM_COMMAND_NPC_NEAR, Console::No }, + { "move", HandleNpcMoveCommand, rbac::RBAC_PERM_COMMAND_NPC_MOVE, Console::No }, + { "playemote", HandleNpcPlayEmoteCommand, rbac::RBAC_PERM_COMMAND_NPC_PLAYEMOTE, Console::No }, + { "say", HandleNpcSayCommand, rbac::RBAC_PERM_COMMAND_NPC_SAY, Console::No }, + { "textemote", HandleNpcTextEmoteCommand, rbac::RBAC_PERM_COMMAND_NPC_TEXTEMOTE, Console::No }, + { "whisper", HandleNpcWhisperCommand, rbac::RBAC_PERM_COMMAND_NPC_WHISPER, Console::No }, + { "yell", HandleNpcYellCommand, rbac::RBAC_PERM_COMMAND_NPC_YELL, Console::No }, + { "tame", HandleNpcTameCommand, rbac::RBAC_PERM_COMMAND_NPC_TAME, Console::No }, + { "do", HandleNpcDoActionCommand, rbac::RBAC_PERM_COMMAND_NPC_EVADE, Console::No }, { "add", npcAddCommandTable }, { "delete", npcDeleteCommandTable }, { "follow", npcFollowCommandTable }, diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp index d2c8d5b1d..42137e367 100644 --- a/src/server/scripts/Commands/cs_pet.cpp +++ b/src/server/scripts/Commands/cs_pet.cpp @@ -22,6 +22,7 @@ #include "ObjectMgr.h" #include "Pet.h" #include "Player.h" +#include "RBAC.h" #include "SpellInfo.h" #include "SpellMgr.h" @@ -36,9 +37,9 @@ public: { static ChatCommandTable petCommandTable = { - { "create", HandlePetCreateCommand, SEC_GAMEMASTER, Console::No }, - { "learn", HandlePetLearnCommand, SEC_GAMEMASTER, Console::No }, - { "unlearn", HandlePetUnlearnCommand, SEC_GAMEMASTER, Console::No } + { "create", HandlePetCreateCommand, rbac::RBAC_PERM_COMMAND_PET_CREATE, Console::No }, + { "learn", HandlePetLearnCommand, rbac::RBAC_PERM_COMMAND_PET_LEARN, Console::No }, + { "unlearn", HandlePetUnlearnCommand, rbac::RBAC_PERM_COMMAND_PET_UNLEARN, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_player.cpp b/src/server/scripts/Commands/cs_player.cpp index 240db01a0..a0eb7e9ec 100644 --- a/src/server/scripts/Commands/cs_player.cpp +++ b/src/server/scripts/Commands/cs_player.cpp @@ -17,6 +17,7 @@ #include "CommandScript.h" #include "PlayerCommand.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -29,8 +30,8 @@ public: { static ChatCommandTable playerCommandTable = { - { "learn", HandlePlayerLearnCommand, SEC_GAMEMASTER, Console::Yes }, - { "unlearn", HandlePlayerUnLearnCommand, SEC_GAMEMASTER, Console::Yes } + { "learn", HandlePlayerLearnCommand, rbac::RBAC_PERM_COMMAND_LEARN, Console::Yes }, + { "unlearn", HandlePlayerUnLearnCommand, rbac::RBAC_PERM_COMMAND_UNLEARN, Console::Yes } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_player_settings.cpp b/src/server/scripts/Commands/cs_player_settings.cpp index 06f038b91..30aadef1b 100644 --- a/src/server/scripts/Commands/cs_player_settings.cpp +++ b/src/server/scripts/Commands/cs_player_settings.cpp @@ -19,6 +19,7 @@ #include "CommandScript.h" #include "Player.h" #include "PlayerSettings.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -31,7 +32,7 @@ public: { static ChatCommandTable playerSettingsCommandTable = { - { "announcer", HandleSettingsAnnouncerFlags, SEC_MODERATOR, Console::No }, + { "announcer", HandleSettingsAnnouncerFlags, rbac::RBAC_PERM_COMMAND_SETTINGS_ANNOUNCER, Console::No }, }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_pooltools.cpp b/src/server/scripts/Commands/cs_pooltools.cpp index a915d8156..db641a76a 100644 --- a/src/server/scripts/Commands/cs_pooltools.cpp +++ b/src/server/scripts/Commands/cs_pooltools.cpp @@ -25,6 +25,7 @@ #include "GridNotifiersImpl.h" #include "MapMgr.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "Tokenize.h" @@ -63,12 +64,12 @@ public: { static ChatCommandTable poolToolsCommandTable = { - { "start", HandlePoolStart, SEC_ADMINISTRATOR, Console::No }, - { "def", HandlePoolDef, SEC_ADMINISTRATOR, Console::No }, - { "add", HandlePoolAdd, SEC_ADMINISTRATOR, Console::No }, - { "remove", HandlePoolRemove, SEC_ADMINISTRATOR, Console::No }, - { "end", HandlePoolEnd, SEC_ADMINISTRATOR, Console::No }, - { "clear", HandlePoolClear, SEC_ADMINISTRATOR, Console::No } + { "start", HandlePoolStart, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "def", HandlePoolDef, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "add", HandlePoolAdd, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "remove", HandlePoolRemove, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "end", HandlePoolEnd, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No }, + { "clear", HandlePoolClear, rbac::RBAC_PERM_COMMAND_DEBUG, Console::No } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_quest.cpp b/src/server/scripts/Commands/cs_quest.cpp index daee0c91c..bc1faf49b 100644 --- a/src/server/scripts/Commands/cs_quest.cpp +++ b/src/server/scripts/Commands/cs_quest.cpp @@ -22,6 +22,7 @@ #include "GameTime.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "ReputationMgr.h" using namespace Acore::ChatCommands; @@ -35,11 +36,11 @@ public: { static ChatCommandTable questCommandTable = { - { "add", HandleQuestAdd, SEC_GAMEMASTER, Console::Yes }, - { "complete", HandleQuestComplete, SEC_GAMEMASTER, Console::Yes }, - { "remove", HandleQuestRemove, SEC_GAMEMASTER, Console::Yes }, - { "reward", HandleQuestReward, SEC_GAMEMASTER, Console::Yes }, - { "status", HandleQuestStatus, SEC_GAMEMASTER, Console::Yes }, + { "add", HandleQuestAdd, rbac::RBAC_PERM_COMMAND_QUEST_ADD, Console::Yes }, + { "complete", HandleQuestComplete, rbac::RBAC_PERM_COMMAND_QUEST_COMPLETE, Console::Yes }, + { "remove", HandleQuestRemove, rbac::RBAC_PERM_COMMAND_QUEST_REMOVE, Console::Yes }, + { "reward", HandleQuestReward, rbac::RBAC_PERM_COMMAND_QUEST_REWARD, Console::Yes }, + { "status", HandleQuestStatus, rbac::RBAC_PERM_COMMAND_QUEST, Console::Yes }, }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_rbac.cpp b/src/server/scripts/Commands/cs_rbac.cpp new file mode 100644 index 000000000..7d80a33a7 --- /dev/null +++ b/src/server/scripts/Commands/cs_rbac.cpp @@ -0,0 +1,289 @@ +/* + * 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 General Public License as published by + * the Free Software Foundation; either version 2 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 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 "AccountMgr.h" +#include "Chat.h" +#include "CommandScript.h" +#include "Language.h" +#include "Player.h" +#include "RBAC.h" +#include "Realm.h" +#include "ScriptMgr.h" +#include "World.h" +#include "WorldSession.h" +#include "WorldSessionMgr.h" + +struct RBACCommandData +{ + RBACCommandData(rbac::RBACData* rbac_, bool needDelete_) : rbac(rbac_), needDelete(needDelete_) { } + RBACCommandData(RBACCommandData const&) = delete; + ~RBACCommandData() + { + if (needDelete) + delete rbac; + } + + rbac::RBACData* rbac = nullptr; + bool needDelete = false; +}; + +using namespace Acore::ChatCommands; + +class rbac_commandscript : public CommandScript +{ +public: + rbac_commandscript() : CommandScript("rbac_commandscript") { } + + ChatCommandTable GetCommands() const override + { + static ChatCommandTable rbacAccountCommandTable = + { + { "list", HandleRBACPermListCommand, LANG_RBAC_HELP_ACC_LIST, rbac::RBAC_PERM_COMMAND_RBAC_ACC_PERM_LIST, Console::Yes }, + { "grant", HandleRBACPermGrantCommand, LANG_RBAC_HELP_ACC_GRANT, rbac::RBAC_PERM_COMMAND_RBAC_ACC_PERM_GRANT, Console::Yes }, + { "deny", HandleRBACPermDenyCommand, LANG_RBAC_HELP_ACC_DENY, rbac::RBAC_PERM_COMMAND_RBAC_ACC_PERM_DENY, Console::Yes }, + { "revoke", HandleRBACPermRevokeCommand, LANG_RBAC_HELP_ACC_REVOKE, rbac::RBAC_PERM_COMMAND_RBAC_ACC_PERM_REVOKE, Console::Yes }, + }; + + static ChatCommandTable rbacCommandTable = + { + { "account", rbacAccountCommandTable }, + { "list", HandleRBACListPermissionsCommand, rbac::RBAC_PERM_COMMAND_RBAC_LIST, Console::Yes }, + }; + + static ChatCommandTable commandTable = + { + { "rbac", rbacCommandTable }, + }; + + return commandTable; + } + + static RBACCommandData GetRBACData(uint32 accountId, std::string const& accountName) + { + if (WorldSession* session = sWorldSessionMgr->FindSession(accountId)) + return { session->GetRBACData(), false }; + + rbac::RBACData* rbac = new rbac::RBACData(accountId, accountName, realm.Id.Realm, AccountMgr::GetSecurity(accountId, realm.Id.Realm)); + rbac->LoadFromDB(); + + return { rbac, true }; + } + + static bool HandleRBACPermGrantCommand(ChatHandler* handler, AccountIdentifier account, uint32 permId, Optional realmId) + { + if (handler->HasLowerSecurityAccount(nullptr, account.GetID(), true)) + return false; + + if (!realmId) + realmId = -1; + + RBACCommandData data = GetRBACData(account.GetID(), account.GetName()); + + rbac::RBACCommandResult result = data.rbac->GrantPermission(permId, *realmId); + rbac::RBACPermission const* permission = sAccountMgr->GetRBACPermission(permId); + + switch (result) + { + case rbac::RBAC_CANT_ADD_ALREADY_ADDED: + handler->PSendSysMessage(LANG_RBAC_PERM_GRANTED_IN_LIST, permId, permission->GetName().c_str(), + *realmId, account.GetID(), account.GetName().c_str()); + break; + case rbac::RBAC_IN_DENIED_LIST: + handler->PSendSysMessage(LANG_RBAC_PERM_GRANTED_IN_DENIED_LIST, permId, permission->GetName().c_str(), + *realmId, account.GetID(), account.GetName().c_str()); + break; + case rbac::RBAC_OK: + handler->PSendSysMessage(LANG_RBAC_PERM_GRANTED, permId, permission->GetName().c_str(), + *realmId, account.GetID(), account.GetName().c_str()); + break; + case rbac::RBAC_ID_DOES_NOT_EXISTS: + handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, permId); + break; + default: + break; + } + + return true; + } + + static bool HandleRBACPermDenyCommand(ChatHandler* handler, AccountIdentifier account, uint32 permId, Optional realmId) + { + if (handler->HasLowerSecurityAccount(nullptr, account.GetID(), true)) + return false; + + if (!realmId) + realmId = -1; + + RBACCommandData data = GetRBACData(account.GetID(), account.GetName()); + + rbac::RBACCommandResult result = data.rbac->DenyPermission(permId, *realmId); + rbac::RBACPermission const* permission = sAccountMgr->GetRBACPermission(permId); + + switch (result) + { + case rbac::RBAC_CANT_ADD_ALREADY_ADDED: + handler->PSendSysMessage(LANG_RBAC_PERM_DENIED_IN_LIST, permId, permission->GetName().c_str(), + *realmId, account.GetID(), account.GetName().c_str()); + break; + case rbac::RBAC_IN_GRANTED_LIST: + handler->PSendSysMessage(LANG_RBAC_PERM_DENIED_IN_GRANTED_LIST, permId, permission->GetName().c_str(), + *realmId, account.GetID(), account.GetName().c_str()); + break; + case rbac::RBAC_OK: + handler->PSendSysMessage(LANG_RBAC_PERM_DENIED, permId, permission->GetName().c_str(), + *realmId, account.GetID(), account.GetName().c_str()); + break; + case rbac::RBAC_ID_DOES_NOT_EXISTS: + handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, permId); + break; + default: + break; + } + + return true; + } + + static bool HandleRBACPermRevokeCommand(ChatHandler* handler, AccountIdentifier account, uint32 permId, Optional realmId) + { + if (handler->HasLowerSecurityAccount(nullptr, account.GetID(), true)) + return false; + + if (!realmId) + realmId = -1; + + RBACCommandData data = GetRBACData(account.GetID(), account.GetName()); + + rbac::RBACCommandResult result = data.rbac->RevokePermission(permId, *realmId); + rbac::RBACPermission const* permission = sAccountMgr->GetRBACPermission(permId); + + switch (result) + { + case rbac::RBAC_CANT_REVOKE_NOT_IN_LIST: + handler->PSendSysMessage(LANG_RBAC_PERM_REVOKED_NOT_IN_LIST, permId, permission->GetName().c_str(), + *realmId, account.GetID(), account.GetName().c_str()); + break; + case rbac::RBAC_OK: + handler->PSendSysMessage(LANG_RBAC_PERM_REVOKED, permId, permission->GetName().c_str(), + *realmId, account.GetID(), account.GetName().c_str()); + break; + case rbac::RBAC_ID_DOES_NOT_EXISTS: + handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, permId); + break; + default: + break; + } + + return true; + } + + static char const* GetSecurityLevelName(uint8 secLevel) + { + switch (secLevel) + { + case SEC_PLAYER: return "SEC_PLAYER"; + case SEC_MODERATOR: return "SEC_MODERATOR"; + case SEC_GAMEMASTER: return "SEC_GAMEMASTER"; + case SEC_ADMINISTRATOR: return "SEC_ADMINISTRATOR"; + case SEC_CONSOLE: return "SEC_CONSOLE"; + default: return "Unknown"; + } + } + + static bool HandleRBACPermListCommand(ChatHandler* handler, AccountIdentifier account) + { + RBACCommandData data = GetRBACData(account.GetID(), account.GetName()); + + handler->PSendSysMessage(LANG_RBAC_LIST_HEADER_GRANTED, data.rbac->GetId(), data.rbac->GetName().c_str()); + rbac::RBACPermissionContainer const& granted = data.rbac->GetGrantedPermissions(); + if (granted.empty()) + handler->SendSysMessage(handler->GetAcoreString(LANG_RBAC_LIST_EMPTY)); + else + { + for (uint32 grantedId : granted) + { + rbac::RBACPermission const* permission = sAccountMgr->GetRBACPermission(grantedId); + handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str()); + } + } + + handler->PSendSysMessage(LANG_RBAC_LIST_HEADER_DENIED, data.rbac->GetId(), data.rbac->GetName().c_str()); + rbac::RBACPermissionContainer const& denied = data.rbac->GetDeniedPermissions(); + if (denied.empty()) + handler->SendSysMessage(handler->GetAcoreString(LANG_RBAC_LIST_EMPTY)); + else + { + for (uint32 deniedId : denied) + { + rbac::RBACPermission const* permission = sAccountMgr->GetRBACPermission(deniedId); + handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str()); + } + } + + handler->PSendSysMessage(LANG_RBAC_LIST_HEADER_BY_SEC_LEVEL, data.rbac->GetId(), data.rbac->GetName().c_str(), + data.rbac->GetSecurityLevel(), GetSecurityLevelName(data.rbac->GetSecurityLevel())); + rbac::RBACPermissionContainer const& defaultPermissions = sAccountMgr->GetRBACDefaultPermissions(data.rbac->GetSecurityLevel()); + if (defaultPermissions.empty()) + handler->SendSysMessage(handler->GetAcoreString(LANG_RBAC_LIST_EMPTY)); + else + { + for (uint32 defaultPermission : defaultPermissions) + { + rbac::RBACPermission const* permission = sAccountMgr->GetRBACPermission(defaultPermission); + handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str()); + } + } + + return true; + } + + static bool HandleRBACListPermissionsCommand(ChatHandler* handler, Optional permId) + { + if (!permId) + { + rbac::RBACPermissionsContainer const& permissions = sAccountMgr->GetRBACPermissionList(); + handler->SendSysMessage(handler->GetAcoreString(LANG_RBAC_LIST_PERMISSIONS_HEADER)); + for (auto const& [_, permission] : permissions) + { + handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str()); + } + } + else + { + rbac::RBACPermission const* permission = sAccountMgr->GetRBACPermission(*permId); + if (!permission) + { + handler->PSendSysMessage(LANG_RBAC_WRONG_PARAMETER_ID, *permId); + handler->SetSentErrorMessage(true); + return false; + } + + handler->SendSysMessage(handler->GetAcoreString(LANG_RBAC_LIST_PERMISSIONS_HEADER)); + handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, permission->GetId(), permission->GetName().c_str()); + handler->SendSysMessage(handler->GetAcoreString(LANG_RBAC_LIST_PERMS_LINKED_HEADER)); + for (uint32 linkedPerm : permission->GetLinkedPermissions()) + if (rbac::RBACPermission const* rbacPermission = sAccountMgr->GetRBACPermission(linkedPerm)) + handler->PSendSysMessage(LANG_RBAC_LIST_ELEMENT, rbacPermission->GetId(), rbacPermission->GetName().c_str()); + } + + return true; + } +}; + +void AddSC_rbac_commandscript() +{ + new rbac_commandscript(); +} diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index ef121f039..ef6cf787a 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -15,6 +15,7 @@ * with this program. If not, see . */ +#include "AccountMgr.h" #include "AchievementMgr.h" #include "AuctionHouseMgr.h" #include "AutobroadcastMgr.h" @@ -31,6 +32,7 @@ #include "MotdMgr.h" #include "ObjectMgr.h" #include "PoolMgr.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "ServerMailMgr.h" #include "SkillDiscovery.h" @@ -55,122 +57,123 @@ public: { static ChatCommandTable reloadAllCommandTable = { - { "achievement", HandleReloadAllAchievementCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "area", HandleReloadAllAreaCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gossips", HandleReloadAllGossipsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "item", HandleReloadAllItemCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "locales", HandleReloadAllLocalesCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "loot", HandleReloadAllLootCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "npc", HandleReloadAllNpcCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest", HandleReloadAllQuestCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "scripts", HandleReloadAllScriptsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell", HandleReloadAllSpellCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "", HandleReloadAllCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "achievement", HandleReloadAllAchievementCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_ACHIEVEMENT, Console::Yes }, + { "area", HandleReloadAllAreaCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_AREA, Console::Yes }, + { "gossips", HandleReloadAllGossipsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_GOSSIP, Console::Yes }, + { "item", HandleReloadAllItemCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_ITEM, Console::Yes }, + { "locales", HandleReloadAllLocalesCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_LOCALES, Console::Yes }, + { "loot", HandleReloadAllLootCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_LOOT, Console::Yes }, + { "npc", HandleReloadAllNpcCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_NPC, Console::Yes }, + { "quest", HandleReloadAllQuestCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_QUEST, Console::Yes }, + { "scripts", HandleReloadAllScriptsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_SCRIPTS, Console::Yes }, + { "spell", HandleReloadAllSpellCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL_SPELL, Console::Yes }, + { "", HandleReloadAllCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ALL, Console::Yes }, }; static ChatCommandTable reloadCommandTable = { - { "antidos_opcode_policies", HandleReloadAntiDosOpcodePoliciesCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "auctions", HandleReloadAuctionsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "dungeon_access_template", HandleReloadDungeonAccessCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "dungeon_access_requirements", HandleReloadDungeonAccessCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "achievement_criteria_data", HandleReloadAchievementCriteriaDataCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "achievement_reward", HandleReloadAchievementRewardCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "antidos_opcode_policies", HandleReloadAntiDosOpcodePoliciesCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "auctions", HandleReloadAuctionsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_AUCTIONS, Console::Yes }, + { "dungeon_access_template", HandleReloadDungeonAccessCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ACCESS_REQUIREMENT, Console::Yes }, + { "dungeon_access_requirements", HandleReloadDungeonAccessCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ACCESS_REQUIREMENT, Console::Yes }, + { "achievement_criteria_data", HandleReloadAchievementCriteriaDataCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ACHIEVEMENT_CRITERIA_DATA, Console::Yes }, + { "achievement_reward", HandleReloadAchievementRewardCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ACHIEVEMENT_REWARD, Console::Yes }, { "all", reloadAllCommandTable }, - { "areatrigger", HandleReloadQuestAreaTriggersCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "areatrigger_involvedrelation", HandleReloadQuestAreaTriggersCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "areatrigger_tavern", HandleReloadAreaTriggerTavernCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "areatrigger_teleport", HandleReloadAreaTriggerTeleportCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "autobroadcast", HandleReloadAutobroadcastCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "motd", HandleReloadMotdCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "broadcast_text", HandleReloadBroadcastTextCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "battleground_template", HandleReloadBattlegroundTemplate, SEC_ADMINISTRATOR, Console::Yes }, - { "command", HandleReloadCommandCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "conditions", HandleReloadConditions, SEC_ADMINISTRATOR, Console::Yes }, - { "config", HandleReloadConfigCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_text", HandleReloadCreatureText, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_questender", HandleReloadCreatureQuestEnderCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_linked_respawn", HandleReloadLinkedRespawnCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_loot_template", HandleReloadLootTemplatesCreatureCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_movement_override", HandleReloadCreatureMovementOverrideCommand, SEC_ADMINISTRATOR, Console::Yes}, - { "creature_onkill_reputation", HandleReloadOnKillReputationCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_queststarter", HandleReloadCreatureQuestStarterCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_template", HandleReloadCreatureTemplateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "disables", HandleReloadDisablesCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "disenchant_loot_template", HandleReloadLootTemplatesDisenchantCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "event_scripts", HandleReloadEventScriptsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "fishing_loot_template", HandleReloadLootTemplatesFishingCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "game_graveyard", HandleReloadGameGraveyardCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "graveyard_zone", HandleReloadGameGraveyardZoneCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "game_tele", HandleReloadGameTeleCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gameobject_questender", HandleReloadGOQuestEnderCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gameobject_loot_template", HandleReloadLootTemplatesGameobjectCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gameobject_queststarter", HandleReloadGOQuestStarterCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gm_tickets", HandleReloadGMTicketsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gossip_menu", HandleReloadGossipMenuCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gossip_menu_option", HandleReloadGossipMenuOptionCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "item_enchantment_template", HandleReloadItemEnchantementsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "item_loot_template", HandleReloadLootTemplatesItemCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "item_set_names", HandleReloadItemSetNamesCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "lfg_dungeon_rewards", HandleReloadLfgRewardsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "achievement_reward_locale", HandleReloadLocalesAchievementRewardCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_template_locale", HandleReloadLocalesCreatureCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "creature_text_locale", HandleReloadLocalesCreatureTextCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gameobject_template_locale", HandleReloadLocalesGameobjectCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gossip_menu_option_locale", HandleReloadLocalesGossipMenuOptionCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "item_template_locale", HandleReloadLocalesItemCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "item_set_name_locale", HandleReloadLocalesItemSetNameCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "npc_text_locale", HandleReloadLocalesNpcTextCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "page_text_locale", HandleReloadLocalesPageTextCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "points_of_interest_locale", HandleReloadLocalesPointsOfInterestCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest_template_locale", HandleReloadLocalesQuestCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest_offer_reward_locale", HandleReloadLocalesQuestOfferRewardCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest_request_item_locale", HandleReloadLocalesQuestRequestItemsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "mail_level_reward", HandleReloadMailLevelRewardCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "mail_loot_template", HandleReloadLootTemplatesMailCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "mail_server_template", HandleReloadMailServerTemplateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "milling_loot_template", HandleReloadLootTemplatesMillingCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "npc_spellclick_spells", HandleReloadSpellClickSpellsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "trainer", HandleReloadTrainerCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "npc_vendor", HandleReloadNpcVendorCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "game_event_npc_vendor", HandleReloadGameEventNPCVendorCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "page_text", HandleReloadPageTextsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "pickpocketing_loot_template", HandleReloadLootTemplatesPickpocketingCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "points_of_interest", HandleReloadPointsOfInterestCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "prospecting_loot_template", HandleReloadLootTemplatesProspectingCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest_greeting", HandleReloadQuestGreetingCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest_poi", HandleReloadQuestPOICommand, SEC_ADMINISTRATOR, Console::Yes }, - { "quest_template", HandleReloadQuestTemplateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "reference_loot_template", HandleReloadLootTemplatesReferenceCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "reserved_name", HandleReloadReservedNameCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "profanity_name", HandleReloadProfanityNameCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "reputation_reward_rate", HandleReloadReputationRewardRateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "reputation_spillover_template", HandleReloadReputationRewardRateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "skill_discovery_template", HandleReloadSkillDiscoveryTemplateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "skill_extra_item_template", HandleReloadSkillExtraItemTemplateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "skill_fishing_base_level", HandleReloadSkillFishingBaseLevelCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "skinning_loot_template", HandleReloadLootTemplatesSkinningCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "smart_scripts", HandleReloadSmartScripts, SEC_ADMINISTRATOR, Console::Yes }, - { "spawn_group", HandleReloadSpawnGroupCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_required", HandleReloadSpellRequiredCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_area", HandleReloadSpellAreaCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_bonus_data", HandleReloadSpellBonusesCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_group", HandleReloadSpellGroupsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_loot_template", HandleReloadLootTemplatesSpellCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_linked_spell", HandleReloadSpellLinkedSpellCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_pet_auras", HandleReloadSpellPetAurasCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_proc", HandleReloadSpellProcsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_scripts", HandleReloadSpellScriptsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_target_position", HandleReloadSpellTargetPositionCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_threats", HandleReloadSpellThreatsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spell_group_stack_rules", HandleReloadSpellGroupStackRulesCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "player_loot_template", HandleReloadLootTemplatesPlayerCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "module_string", HandleReloadModuleStringCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "acore_string", HandleReloadAcoreStringCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "warden_action", HandleReloadWardenactionCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "waypoint_scripts", HandleReloadWpScriptsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "waypoint_data", HandleReloadWpCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "vehicle_accessory", HandleReloadVehicleAccessoryCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "vehicle_template_accessory", HandleReloadVehicleTemplateAccessoryCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "areatrigger", HandleReloadQuestAreaTriggersCommand, rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_INVOLVEDRELATION, Console::Yes }, + { "areatrigger_involvedrelation", HandleReloadQuestAreaTriggersCommand, rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_INVOLVEDRELATION, Console::Yes }, + { "areatrigger_tavern", HandleReloadAreaTriggerTavernCommand, rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TAVERN, Console::Yes }, + { "areatrigger_teleport", HandleReloadAreaTriggerTeleportCommand, rbac::RBAC_PERM_COMMAND_RELOAD_AREATRIGGER_TELEPORT, Console::Yes }, + { "autobroadcast", HandleReloadAutobroadcastCommand, rbac::RBAC_PERM_COMMAND_RELOAD_AUTOBROADCAST, Console::Yes }, + { "motd", HandleReloadMotdCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "broadcast_text", HandleReloadBroadcastTextCommand, rbac::RBAC_PERM_COMMAND_RELOAD_BROADCAST_TEXT, Console::Yes }, + { "battleground_template", HandleReloadBattlegroundTemplate, rbac::RBAC_PERM_COMMAND_RELOAD_BATTLEGROUND_TEMPLATE, Console::Yes }, + { "command", HandleReloadCommandCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "conditions", HandleReloadConditions, rbac::RBAC_PERM_COMMAND_RELOAD_CONDITIONS, Console::Yes }, + { "config", HandleReloadConfigCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CONFIG, Console::Yes }, + { "creature_text", HandleReloadCreatureText, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_TEXT, Console::Yes }, + { "creature_questender", HandleReloadCreatureQuestEnderCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_QUESTENDER, Console::Yes }, + { "creature_linked_respawn", HandleReloadLinkedRespawnCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_LINKED_RESPAWN, Console::Yes }, + { "creature_loot_template", HandleReloadLootTemplatesCreatureCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_LOOT_TEMPLATE, Console::Yes }, + { "creature_movement_override", HandleReloadCreatureMovementOverrideCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_MOVEMENT_OVERRIDE, Console::Yes}, + { "creature_onkill_reputation", HandleReloadOnKillReputationCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_ONKILL_REPUTATION, Console::Yes }, + { "creature_queststarter", HandleReloadCreatureQuestStarterCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_QUESTSTARTER, Console::Yes }, + { "creature_template", HandleReloadCreatureTemplateCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_TEMPLATE, Console::Yes }, + { "disables", HandleReloadDisablesCommand, rbac::RBAC_PERM_COMMAND_RELOAD_DISABLES, Console::Yes }, + { "disenchant_loot_template", HandleReloadLootTemplatesDisenchantCommand, rbac::RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE, Console::Yes }, + { "event_scripts", HandleReloadEventScriptsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_EVENT_SCRIPTS, Console::Yes }, + { "fishing_loot_template", HandleReloadLootTemplatesFishingCommand, rbac::RBAC_PERM_COMMAND_RELOAD_FISHING_LOOT_TEMPLATE, Console::Yes }, + { "game_graveyard", HandleReloadGameGraveyardCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "graveyard_zone", HandleReloadGameGraveyardZoneCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GRAVEYARD_ZONE, Console::Yes }, + { "game_tele", HandleReloadGameTeleCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GAME_TELE, Console::Yes }, + { "gameobject_questender", HandleReloadGOQuestEnderCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER, Console::Yes }, + { "gameobject_loot_template", HandleReloadLootTemplatesGameobjectCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE, Console::Yes }, + { "gameobject_queststarter", HandleReloadGOQuestStarterCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTSTARTER, Console::Yes }, + { "gm_tickets", HandleReloadGMTicketsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GM_TICKETS, Console::Yes }, + { "gossip_menu", HandleReloadGossipMenuCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU, Console::Yes }, + { "gossip_menu_option", HandleReloadGossipMenuOptionCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU_OPTION, Console::Yes }, + { "item_enchantment_template", HandleReloadItemEnchantementsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ITEM_ENCHANTMENT_TEMPLATE, Console::Yes }, + { "item_loot_template", HandleReloadLootTemplatesItemCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ITEM_LOOT_TEMPLATE, Console::Yes }, + { "item_set_names", HandleReloadItemSetNamesCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ITEM_SET_NAMES, Console::Yes }, + { "lfg_dungeon_rewards", HandleReloadLfgRewardsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_LFG_DUNGEON_REWARDS, Console::Yes }, + { "achievement_reward_locale", HandleReloadLocalesAchievementRewardCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ACHIEVEMENT_REWARD_LOCALE, Console::Yes }, + { "creature_template_locale", HandleReloadLocalesCreatureCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_TEMPLATE_LOCALE, Console::Yes }, + { "creature_text_locale", HandleReloadLocalesCreatureTextCommand, rbac::RBAC_PERM_COMMAND_RELOAD_CREATURE_TEXT_LOCALE, Console::Yes }, + { "gameobject_template_locale", HandleReloadLocalesGameobjectCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_TEMPLATE_LOCALE, Console::Yes }, + { "gossip_menu_option_locale", HandleReloadLocalesGossipMenuOptionCommand, rbac::RBAC_PERM_COMMAND_RELOAD_GOSSIP_MENU_OPTION_LOCALE, Console::Yes }, + { "item_template_locale", HandleReloadLocalesItemCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ITEM_TEMPLATE_LOCALE, Console::Yes }, + { "item_set_name_locale", HandleReloadLocalesItemSetNameCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ITEM_SET_NAME_LOCALE, Console::Yes }, + { "npc_text_locale", HandleReloadLocalesNpcTextCommand, rbac::RBAC_PERM_COMMAND_RELOAD_NPC_TEXT_LOCALE, Console::Yes }, + { "page_text_locale", HandleReloadLocalesPageTextCommand, rbac::RBAC_PERM_COMMAND_RELOAD_PAGE_TEXT_LOCALE, Console::Yes }, + { "points_of_interest_locale", HandleReloadLocalesPointsOfInterestCommand, rbac::RBAC_PERM_COMMAND_RELOAD_POINTS_OF_INTEREST_LOCALE, Console::Yes }, + { "quest_template_locale", HandleReloadLocalesQuestCommand, rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_TEMPLATE_LOCALE, Console::Yes }, + { "quest_offer_reward_locale", HandleReloadLocalesQuestOfferRewardCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "quest_request_item_locale", HandleReloadLocalesQuestRequestItemsCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "mail_level_reward", HandleReloadMailLevelRewardCommand, rbac::RBAC_PERM_COMMAND_RELOAD_MAIL_LEVEL_REWARD, Console::Yes }, + { "mail_loot_template", HandleReloadLootTemplatesMailCommand, rbac::RBAC_PERM_COMMAND_RELOAD_MAIL_LOOT_TEMPLATE, Console::Yes }, + { "mail_server_template", HandleReloadMailServerTemplateCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "milling_loot_template", HandleReloadLootTemplatesMillingCommand, rbac::RBAC_PERM_COMMAND_RELOAD_MILLING_LOOT_TEMPLATE, Console::Yes }, + { "npc_spellclick_spells", HandleReloadSpellClickSpellsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_NPC_SPELLCLICK_SPELLS, Console::Yes }, + { "trainer", HandleReloadTrainerCommand, rbac::RBAC_PERM_COMMAND_RELOAD_TRAINER, Console::Yes }, + { "npc_vendor", HandleReloadNpcVendorCommand, rbac::RBAC_PERM_COMMAND_RELOAD_NPC_VENDOR, Console::Yes }, + { "game_event_npc_vendor", HandleReloadGameEventNPCVendorCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "page_text", HandleReloadPageTextsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_PAGE_TEXT, Console::Yes }, + { "pickpocketing_loot_template", HandleReloadLootTemplatesPickpocketingCommand, rbac::RBAC_PERM_COMMAND_RELOAD_PICKPOCKETING_LOOT_TEMPLATE, Console::Yes }, + { "points_of_interest", HandleReloadPointsOfInterestCommand, rbac::RBAC_PERM_COMMAND_RELOAD_POINTS_OF_INTEREST, Console::Yes }, + { "prospecting_loot_template", HandleReloadLootTemplatesProspectingCommand, rbac::RBAC_PERM_COMMAND_RELOAD_PROSPECTING_LOOT_TEMPLATE, Console::Yes }, + { "quest_greeting", HandleReloadQuestGreetingCommand, rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_GREETING, Console::Yes }, + { "quest_poi", HandleReloadQuestPOICommand, rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_POI, Console::Yes }, + { "quest_template", HandleReloadQuestTemplateCommand, rbac::RBAC_PERM_COMMAND_RELOAD_QUEST_TEMPLATE, Console::Yes }, + { "rbac", HandleReloadRBACCommand, rbac::RBAC_PERM_COMMAND_RELOAD_RBAC, Console::Yes }, + { "reference_loot_template", HandleReloadLootTemplatesReferenceCommand, rbac::RBAC_PERM_COMMAND_RELOAD_REFERENCE_LOOT_TEMPLATE, Console::Yes }, + { "reserved_name", HandleReloadReservedNameCommand, rbac::RBAC_PERM_COMMAND_RELOAD_RESERVED_NAME, Console::Yes }, + { "profanity_name", HandleReloadProfanityNameCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "reputation_reward_rate", HandleReloadReputationRewardRateCommand, rbac::RBAC_PERM_COMMAND_RELOAD_REPUTATION_REWARD_RATE, Console::Yes }, + { "reputation_spillover_template", HandleReloadReputationRewardRateCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPILLOVER_TEMPLATE, Console::Yes }, + { "skill_discovery_template", HandleReloadSkillDiscoveryTemplateCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SKILL_DISCOVERY_TEMPLATE, Console::Yes }, + { "skill_extra_item_template", HandleReloadSkillExtraItemTemplateCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SKILL_EXTRA_ITEM_TEMPLATE, Console::Yes }, + { "skill_fishing_base_level", HandleReloadSkillFishingBaseLevelCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SKILL_FISHING_BASE_LEVEL, Console::Yes }, + { "skinning_loot_template", HandleReloadLootTemplatesSkinningCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SKINNING_LOOT_TEMPLATE, Console::Yes }, + { "smart_scripts", HandleReloadSmartScripts, rbac::RBAC_PERM_COMMAND_RELOAD_SMART_SCRIPTS, Console::Yes }, + { "spawn_group", HandleReloadSpawnGroupCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "spell_required", HandleReloadSpellRequiredCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_REQUIRED, Console::Yes }, + { "spell_area", HandleReloadSpellAreaCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_AREA, Console::Yes }, + { "spell_bonus_data", HandleReloadSpellBonusesCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_BONUS_DATA, Console::Yes }, + { "spell_group", HandleReloadSpellGroupsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP, Console::Yes }, + { "spell_loot_template", HandleReloadLootTemplatesSpellCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_LOOT_TEMPLATE, Console::Yes }, + { "spell_linked_spell", HandleReloadSpellLinkedSpellCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_LINKED_SPELL, Console::Yes }, + { "spell_pet_auras", HandleReloadSpellPetAurasCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS, Console::Yes }, + { "spell_proc", HandleReloadSpellProcsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC, Console::Yes }, + { "spell_scripts", HandleReloadSpellScriptsCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "spell_target_position", HandleReloadSpellTargetPositionCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION, Console::Yes }, + { "spell_threats", HandleReloadSpellThreatsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_THREATS, Console::Yes }, + { "spell_group_stack_rules", HandleReloadSpellGroupStackRulesCommand, rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP_STACK_RULES, Console::Yes }, + { "player_loot_template", HandleReloadLootTemplatesPlayerCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "module_string", HandleReloadModuleStringCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "acore_string", HandleReloadAcoreStringCommand, rbac::RBAC_PERM_COMMAND_RELOAD_ACORE_STRING, Console::Yes }, + { "warden_action", HandleReloadWardenactionCommand, rbac::RBAC_PERM_COMMAND_RELOAD, Console::Yes }, + { "waypoint_scripts", HandleReloadWpScriptsCommand, rbac::RBAC_PERM_COMMAND_RELOAD_WAYPOINT_SCRIPTS, Console::Yes }, + { "waypoint_data", HandleReloadWpCommand, rbac::RBAC_PERM_COMMAND_RELOAD_WAYPOINT_DATA, Console::Yes }, + { "vehicle_accessory", HandleReloadVehicleAccessoryCommand, rbac::RBAC_PERM_COMMAND_RELOAD_VEHICLE_ACCESSORY, Console::Yes }, + { "vehicle_template_accessory", HandleReloadVehicleTemplateAccessoryCommand, rbac::RBAC_PERM_COMMAND_RELOAD_VEHICLE_TEMPLATE_ACCESSORY, Console::Yes }, }; static ChatCommandTable commandTable = { @@ -579,6 +582,15 @@ public: return true; } + static bool HandleReloadRBACCommand(ChatHandler* handler) + { + LOG_INFO("rbac", "Reloading RBAC tables..."); + sAccountMgr->LoadRBAC(); + sWorld->ReloadRBAC(); + handler->SendGlobalGMSysMessage("RBAC data reloaded."); + return true; + } + static bool HandleReloadLootTemplatesCreatureCommand(ChatHandler* handler) { LOG_INFO("server.loading", "Reloading Loot Tables... (`creature_loot_template`)"); diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp index c3a6772cc..bc61aa5e0 100644 --- a/src/server/scripts/Commands/cs_reset.cpp +++ b/src/server/scripts/Commands/cs_reset.cpp @@ -21,6 +21,7 @@ #include "Language.h" #include "Pet.h" #include "Player.h" +#include "RBAC.h" #include "ScriptMgr.h" #include "WorldSessionMgr.h" @@ -36,25 +37,25 @@ public: { static ChatCommandTable resetItemsCommandTable = { - { "equipped", HandleResetItemsEquippedCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "bags", HandleResetItemsInBagsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "bank", HandleResetItemsInBankCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "keyring", HandleResetItemsKeyringCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "currency", HandleResetItemsInCurrenciesListCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "vendor_buyback", HandleResetItemsInVendorBuyBackTabCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "all", HandleResetItemsAllCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "allbags", HandleResetItemsAllAndDeleteBagsCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "equipped", HandleResetItemsEquippedCommand, rbac::RBAC_PERM_COMMAND_RESET, Console::Yes }, + { "bags", HandleResetItemsInBagsCommand, rbac::RBAC_PERM_COMMAND_RESET, Console::Yes }, + { "bank", HandleResetItemsInBankCommand, rbac::RBAC_PERM_COMMAND_RESET, Console::Yes }, + { "keyring", HandleResetItemsKeyringCommand, rbac::RBAC_PERM_COMMAND_RESET, Console::Yes }, + { "currency", HandleResetItemsInCurrenciesListCommand, rbac::RBAC_PERM_COMMAND_RESET, Console::Yes }, + { "vendor_buyback", HandleResetItemsInVendorBuyBackTabCommand, rbac::RBAC_PERM_COMMAND_RESET, Console::Yes }, + { "all", HandleResetItemsAllCommand, rbac::RBAC_PERM_COMMAND_RESET, Console::Yes }, + { "allbags", HandleResetItemsAllAndDeleteBagsCommand, rbac::RBAC_PERM_COMMAND_RESET, Console::Yes }, }; static ChatCommandTable resetCommandTable = { - { "achievements", HandleResetAchievementsCommand, SEC_CONSOLE, Console::Yes }, - { "honor", HandleResetHonorCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "level", HandleResetLevelCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "spells", HandleResetSpellsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "stats", HandleResetStatsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "talents", HandleResetTalentsCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "items", resetItemsCommandTable }, - { "all", HandleResetAllCommand, SEC_CONSOLE, Console::Yes } + { "achievements", HandleResetAchievementsCommand, rbac::RBAC_PERM_COMMAND_RESET_ACHIEVEMENTS, Console::Yes }, + { "honor", HandleResetHonorCommand, rbac::RBAC_PERM_COMMAND_RESET_HONOR, Console::Yes }, + { "level", HandleResetLevelCommand, rbac::RBAC_PERM_COMMAND_RESET_LEVEL, Console::Yes }, + { "spells", HandleResetSpellsCommand, rbac::RBAC_PERM_COMMAND_RESET_SPELLS, Console::Yes }, + { "stats", HandleResetStatsCommand, rbac::RBAC_PERM_COMMAND_RESET_STATS, Console::Yes }, + { "talents", HandleResetTalentsCommand, rbac::RBAC_PERM_COMMAND_RESET_TALENTS, Console::Yes }, + { "items", resetItemsCommandTable }, + { "all", HandleResetAllCommand, rbac::RBAC_PERM_COMMAND_RESET_ALL, Console::Yes } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_script_loader.cpp b/src/server/scripts/Commands/cs_script_loader.cpp index 9507a4bdd..9ad077a6a 100644 --- a/src/server/scripts/Commands/cs_script_loader.cpp +++ b/src/server/scripts/Commands/cs_script_loader.cpp @@ -54,6 +54,7 @@ void AddSC_player_commandscript(); void AddSC_pool_commandscript(); void AddSC_pooltools_commandscript(); void AddSC_quest_commandscript(); +void AddSC_rbac_commandscript(); void AddSC_reload_commandscript(); void AddSC_reset_commandscript(); void AddSC_send_commandscript(); @@ -111,6 +112,7 @@ void AddCommandsScripts() AddSC_pool_commandscript(); AddSC_pooltools_commandscript(); AddSC_quest_commandscript(); + AddSC_rbac_commandscript(); AddSC_reload_commandscript(); AddSC_reset_commandscript(); AddSC_send_commandscript(); diff --git a/src/server/scripts/Commands/cs_send.cpp b/src/server/scripts/Commands/cs_send.cpp index ee74a22a0..3e3d6f56f 100644 --- a/src/server/scripts/Commands/cs_send.cpp +++ b/src/server/scripts/Commands/cs_send.cpp @@ -23,6 +23,7 @@ #include "ObjectMgr.h" #include "Pet.h" #include "Player.h" +#include "RBAC.h" #include "Tokenize.h" using namespace Acore::ChatCommands; @@ -36,10 +37,10 @@ public: { static ChatCommandTable sendCommandTable = { - { "items", HandleSendItemsCommand, SEC_GAMEMASTER, Console::Yes }, - { "mail", HandleSendMailCommand, SEC_GAMEMASTER, Console::Yes }, - { "message", HandleSendMessageCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "money", HandleSendMoneyCommand, SEC_GAMEMASTER, Console::Yes } + { "items", HandleSendItemsCommand, rbac::RBAC_PERM_COMMAND_SEND_ITEMS, Console::Yes }, + { "mail", HandleSendMailCommand, rbac::RBAC_PERM_COMMAND_SEND_MAIL, Console::Yes }, + { "message", HandleSendMessageCommand, rbac::RBAC_PERM_COMMAND_SEND_MESSAGE, Console::Yes }, + { "money", HandleSendMoneyCommand, rbac::RBAC_PERM_COMMAND_SEND_MONEY, Console::Yes } }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index 98bfbbac3..fd4c51e72 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -25,6 +25,7 @@ #include "ModuleMgr.h" #include "MotdMgr.h" #include "MySQLThreading.h" +#include "RBAC.h" #include "Realm.h" #include "StringConvert.h" #include "UpdateTime.h" @@ -48,44 +49,44 @@ public: { static ChatCommandTable serverIdleRestartCommandTable = { - { "cancel", HandleServerShutDownCancelCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "", HandleServerIdleRestartCommand, SEC_CONSOLE, Console::Yes } + { "cancel", HandleServerShutDownCancelCommand, rbac::RBAC_PERM_COMMAND_SERVER_IDLERESTART_CANCEL, Console::Yes }, + { "", HandleServerIdleRestartCommand, rbac::RBAC_PERM_COMMAND_SERVER_IDLERESTART, Console::Yes } }; static ChatCommandTable serverIdleShutdownCommandTable = { - { "cancel", HandleServerShutDownCancelCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "", HandleServerIdleShutDownCommand, SEC_CONSOLE, Console::Yes } + { "cancel", HandleServerShutDownCancelCommand, rbac::RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN_CANCEL, Console::Yes }, + { "", HandleServerIdleShutDownCommand, rbac::RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN, Console::Yes } }; static ChatCommandTable serverRestartCommandTable = { - { "cancel", HandleServerShutDownCancelCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "", HandleServerRestartCommand, SEC_ADMINISTRATOR, Console::Yes } + { "cancel", HandleServerShutDownCancelCommand, rbac::RBAC_PERM_COMMAND_SERVER_RESTART_CANCEL, Console::Yes }, + { "", HandleServerRestartCommand, rbac::RBAC_PERM_COMMAND_SERVER_RESTART, Console::Yes } }; static ChatCommandTable serverShutdownCommandTable = { - { "cancel", HandleServerShutDownCancelCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "", HandleServerShutDownCommand, SEC_ADMINISTRATOR, Console::Yes } + { "cancel", HandleServerShutDownCancelCommand, rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN_CANCEL, Console::Yes }, + { "", HandleServerShutDownCommand, rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, Console::Yes } }; static ChatCommandTable serverSetCommandTable = { - { "loglevel", HandleServerSetLogLevelCommand, SEC_CONSOLE, Console::Yes }, - { "motd", HandleServerSetMotdCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "closed", HandleServerSetClosedCommand, SEC_CONSOLE, Console::Yes }, + { "loglevel", HandleServerSetLogLevelCommand, rbac::RBAC_PERM_COMMAND_SERVER_SET_LOGLEVEL, Console::Yes }, + { "motd", HandleServerSetMotdCommand, rbac::RBAC_PERM_COMMAND_SERVER_SET_MOTD, Console::Yes }, + { "closed", HandleServerSetClosedCommand, rbac::RBAC_PERM_COMMAND_SERVER_SET_CLOSED, Console::Yes }, }; static ChatCommandTable serverCommandTable = { - { "corpses", HandleServerCorpsesCommand, SEC_GAMEMASTER, Console::Yes }, - { "debug", HandleServerDebugCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "exit", HandleServerExitCommand, SEC_CONSOLE, Console::Yes }, + { "corpses", HandleServerCorpsesCommand, rbac::RBAC_PERM_COMMAND_SERVER_CORPSES, Console::Yes }, + { "debug", HandleServerDebugCommand, rbac::RBAC_PERM_COMMAND_SERVER_DEBUG, Console::Yes }, + { "exit", HandleServerExitCommand, rbac::RBAC_PERM_COMMAND_SERVER_EXIT, Console::Yes }, { "idlerestart", serverIdleRestartCommandTable }, { "idleshutdown", serverIdleShutdownCommandTable }, - { "info", HandleServerInfoCommand, SEC_PLAYER, Console::Yes }, - { "motd", HandleServerMotdCommand, SEC_PLAYER, Console::Yes }, + { "info", HandleServerInfoCommand, rbac::RBAC_PERM_COMMAND_SERVER_INFO, Console::Yes }, + { "motd", HandleServerMotdCommand, rbac::RBAC_PERM_COMMAND_SERVER_MOTD, Console::Yes }, { "restart", serverRestartCommandTable }, { "shutdown", serverShutdownCommandTable }, { "set", serverSetCommandTable } diff --git a/src/server/scripts/Commands/cs_spectator.cpp b/src/server/scripts/Commands/cs_spectator.cpp index 86fbf6413..29ff8321c 100644 --- a/src/server/scripts/Commands/cs_spectator.cpp +++ b/src/server/scripts/Commands/cs_spectator.cpp @@ -20,6 +20,7 @@ #include "Chat.h" #include "CommandScript.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -32,12 +33,12 @@ public: { static ChatCommandTable spectatorCommandTable = { - { "version", HandleSpectatorVersionCommand, SEC_PLAYER, Console::No }, - { "reset", HandleSpectatorResetCommand, SEC_PLAYER, Console::No }, - { "spectate", HandleSpectatorSpectateCommand, SEC_PLAYER, Console::No }, - { "watch", HandleSpectatorWatchCommand, SEC_PLAYER, Console::No }, - { "leave", HandleSpectatorLeaveCommand, SEC_PLAYER, Console::No }, - { "", HandleSpectatorCommand, SEC_PLAYER, Console::No } + { "version", HandleSpectatorVersionCommand, rbac::RBAC_PERM_COMMAND_SPECT_VERSION, Console::No }, + { "reset", HandleSpectatorResetCommand, rbac::RBAC_PERM_COMMAND_SPECT_RESET, Console::No }, + { "spectate", HandleSpectatorSpectateCommand, rbac::RBAC_PERM_COMMAND_SPECT_SPECTATE, Console::No }, + { "watch", HandleSpectatorWatchCommand, rbac::RBAC_PERM_COMMAND_SPECT_WATCH, Console::No }, + { "leave", HandleSpectatorLeaveCommand, rbac::RBAC_PERM_COMMAND_SPECT_LEAVE, Console::No }, + { "", HandleSpectatorCommand, rbac::RBAC_PERM_COMMAND_SPECT, Console::No } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_tele.cpp b/src/server/scripts/Commands/cs_tele.cpp index aed43e503..ee8c9d796 100644 --- a/src/server/scripts/Commands/cs_tele.cpp +++ b/src/server/scripts/Commands/cs_tele.cpp @@ -23,6 +23,7 @@ #include "MapMgr.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -35,22 +36,22 @@ public: { static ChatCommandTable teleNameNpcCommandTable = { - { "id", HandleTeleNameNpcIdCommand, SEC_GAMEMASTER, Console::Yes }, - { "guid", HandleTeleNameNpcSpawnIdCommand, SEC_GAMEMASTER, Console::Yes }, - { "name", HandleTeleNameNpcNameCommand, SEC_GAMEMASTER, Console::Yes }, + { "id", HandleTeleNameNpcIdCommand, rbac::RBAC_PERM_COMMAND_TELE_NAME, Console::Yes }, + { "guid", HandleTeleNameNpcSpawnIdCommand, rbac::RBAC_PERM_COMMAND_TELE_NAME, Console::Yes }, + { "name", HandleTeleNameNpcNameCommand, rbac::RBAC_PERM_COMMAND_TELE_NAME, Console::Yes }, }; static ChatCommandTable teleNameCommandTable = { { "npc", teleNameNpcCommandTable }, - { "", HandleTeleNameCommand, SEC_GAMEMASTER, Console::Yes }, + { "", HandleTeleNameCommand, rbac::RBAC_PERM_COMMAND_TELE_NAME, Console::Yes }, }; static ChatCommandTable teleCommandTable = { - { "add", HandleTeleAddCommand, SEC_ADMINISTRATOR, Console::No }, - { "del", HandleTeleDelCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "add", HandleTeleAddCommand, rbac::RBAC_PERM_COMMAND_TELE_ADD, Console::No }, + { "del", HandleTeleDelCommand, rbac::RBAC_PERM_COMMAND_TELE_DEL, Console::Yes }, { "name", teleNameCommandTable }, - { "group", HandleTeleGroupCommand, SEC_GAMEMASTER, Console::No }, - { "", HandleTeleCommand, SEC_GAMEMASTER, Console::No } + { "group", HandleTeleGroupCommand, rbac::RBAC_PERM_COMMAND_TELE_GROUP, Console::No }, + { "", HandleTeleCommand, rbac::RBAC_PERM_COMMAND_TELE, Console::No } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp index 0cd174b70..e54aad546 100644 --- a/src/server/scripts/Commands/cs_ticket.cpp +++ b/src/server/scripts/Commands/cs_ticket.cpp @@ -20,6 +20,7 @@ #include "CommandScript.h" #include "ObjectMgr.h" #include "Player.h" +#include "RBAC.h" #include "Realm.h" #include "TicketMgr.h" @@ -34,30 +35,30 @@ public: { static ChatCommandTable ticketResponseCommandTable = { - { "append", HandleGMTicketResponseAppendCommand, SEC_GAMEMASTER, Console::Yes }, - { "appendln", HandleGMTicketResponseAppendLnCommand, SEC_GAMEMASTER, Console::Yes }, - { "delete", HandleGMTicketResponseDeleteCommand, SEC_GAMEMASTER, Console::Yes }, - { "show", HandleGMTicketResponseShowCommand, SEC_GAMEMASTER, Console::Yes } + { "append", HandleGMTicketResponseAppendCommand, rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE_APPEND, Console::Yes }, + { "appendln", HandleGMTicketResponseAppendLnCommand, rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE_APPENDLN, Console::Yes }, + { "delete", HandleGMTicketResponseDeleteCommand, rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE, Console::Yes }, + { "show", HandleGMTicketResponseShowCommand, rbac::RBAC_PERM_COMMAND_TICKET_RESPONSE, Console::Yes } }; static ChatCommandTable ticketCommandTable = { - { "assign", HandleGMTicketAssignToCommand, SEC_GAMEMASTER, Console::Yes }, - { "close", HandleGMTicketCloseByIdCommand, SEC_GAMEMASTER, Console::Yes }, - { "closedlist", HandleGMTicketListClosedCommand, SEC_GAMEMASTER, Console::Yes }, - { "comment", HandleGMTicketCommentCommand, SEC_GAMEMASTER, Console::Yes }, - { "complete", HandleGMTicketCompleteCommand, SEC_GAMEMASTER, Console::Yes }, - { "delete", HandleGMTicketDeleteByIdCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "escalate", HandleGMTicketEscalateCommand, SEC_GAMEMASTER, Console::Yes }, - { "escalatedlist", HandleGMTicketListEscalatedCommand, SEC_GAMEMASTER, Console::Yes }, - { "list", HandleGMTicketListCommand, SEC_GAMEMASTER, Console::Yes }, - { "onlinelist", HandleGMTicketListOnlineCommand, SEC_GAMEMASTER, Console::Yes }, - { "reset", HandleGMTicketResetCommand, SEC_CONSOLE, Console::Yes }, + { "assign", HandleGMTicketAssignToCommand, rbac::RBAC_PERM_COMMAND_TICKET_ASSIGN, Console::Yes }, + { "close", HandleGMTicketCloseByIdCommand, rbac::RBAC_PERM_COMMAND_TICKET_CLOSE, Console::Yes }, + { "closedlist", HandleGMTicketListClosedCommand, rbac::RBAC_PERM_COMMAND_TICKET_CLOSEDLIST, Console::Yes }, + { "comment", HandleGMTicketCommentCommand, rbac::RBAC_PERM_COMMAND_TICKET_COMMENT, Console::Yes }, + { "complete", HandleGMTicketCompleteCommand, rbac::RBAC_PERM_COMMAND_TICKET_COMPLETE, Console::Yes }, + { "delete", HandleGMTicketDeleteByIdCommand, rbac::RBAC_PERM_COMMAND_TICKET_DELETE, Console::Yes }, + { "escalate", HandleGMTicketEscalateCommand, rbac::RBAC_PERM_COMMAND_TICKET_ESCALATE, Console::Yes }, + { "escalatedlist", HandleGMTicketListEscalatedCommand, rbac::RBAC_PERM_COMMAND_TICKET_ESCALATEDLIST, Console::Yes }, + { "list", HandleGMTicketListCommand, rbac::RBAC_PERM_COMMAND_TICKET_LIST, Console::Yes }, + { "onlinelist", HandleGMTicketListOnlineCommand, rbac::RBAC_PERM_COMMAND_TICKET_ONLINELIST, Console::Yes }, + { "reset", HandleGMTicketResetCommand, rbac::RBAC_PERM_COMMAND_TICKET_RESET, Console::Yes }, { "response", ticketResponseCommandTable }, - { "togglesystem", HandleToggleGMTicketSystem, SEC_ADMINISTRATOR, Console::Yes }, - { "unassign", HandleGMTicketUnAssignCommand, SEC_GAMEMASTER, Console::Yes }, - { "viewid", HandleGMTicketGetByIdCommand, SEC_GAMEMASTER, Console::Yes }, - { "viewname", HandleGMTicketGetByNameCommand, SEC_GAMEMASTER, Console::Yes } + { "togglesystem", HandleToggleGMTicketSystem, rbac::RBAC_PERM_COMMAND_TICKET_TOGGLESYSTEM, Console::Yes }, + { "unassign", HandleGMTicketUnAssignCommand, rbac::RBAC_PERM_COMMAND_TICKET_UNASSIGN, Console::Yes }, + { "viewid", HandleGMTicketGetByIdCommand, rbac::RBAC_PERM_COMMAND_TICKET_VIEWID, Console::Yes }, + { "viewname", HandleGMTicketGetByNameCommand, rbac::RBAC_PERM_COMMAND_TICKET_VIEWNAME, Console::Yes } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/Commands/cs_titles.cpp b/src/server/scripts/Commands/cs_titles.cpp index 51c254724..38a35f5f7 100644 --- a/src/server/scripts/Commands/cs_titles.cpp +++ b/src/server/scripts/Commands/cs_titles.cpp @@ -19,6 +19,7 @@ #include "CommandScript.h" #include "DBCStores.h" #include "Player.h" +#include "RBAC.h" using namespace Acore::ChatCommands; @@ -31,13 +32,13 @@ public: { static ChatCommandTable titlesSetCommandTable = { - { "mask", HandleTitlesSetMaskCommand, SEC_GAMEMASTER, Console::No }, + { "mask", HandleTitlesSetMaskCommand, rbac::RBAC_PERM_COMMAND_TITLES_SET_MASK, Console::No }, }; static ChatCommandTable titlesCommandTable = { - { "add", HandleTitlesAddCommand, SEC_GAMEMASTER, Console::No }, - { "current", HandleTitlesCurrentCommand, SEC_GAMEMASTER, Console::No }, - { "remove", HandleTitlesRemoveCommand, SEC_GAMEMASTER, Console::No }, + { "add", HandleTitlesAddCommand, rbac::RBAC_PERM_COMMAND_TITLES_ADD, Console::No }, + { "current", HandleTitlesCurrentCommand, rbac::RBAC_PERM_COMMAND_TITLES_CURRENT, Console::No }, + { "remove", HandleTitlesRemoveCommand, rbac::RBAC_PERM_COMMAND_TITLES_REMOVE, Console::No }, { "set", titlesSetCommandTable }, }; static ChatCommandTable commandTable = diff --git a/src/server/scripts/Commands/cs_worldstate.cpp b/src/server/scripts/Commands/cs_worldstate.cpp index e6d9c833d..535aadbaf 100644 --- a/src/server/scripts/Commands/cs_worldstate.cpp +++ b/src/server/scripts/Commands/cs_worldstate.cpp @@ -18,6 +18,7 @@ #include "Chat.h" #include "CommandScript.h" #include "Common.h" +#include "RBAC.h" #include "WorldState.h" using namespace Acore::ChatCommands; @@ -31,20 +32,20 @@ public: { static ChatCommandTable sunsreachCommandTable = { - { "status", HandleSunsReachReclamationStatusCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "phase", HandleSunsReachReclamationPhaseCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "subphase", HandleSunsReachReclamationSubPhaseCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "counter", HandleSunsReachReclamationCounterCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gate", HandleSunwellGateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "gatecounter", HandleSunwellGateCounterCommand, SEC_ADMINISTRATOR, Console::Yes }, + { "status", HandleSunsReachReclamationStatusCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "phase", HandleSunsReachReclamationPhaseCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "subphase", HandleSunsReachReclamationSubPhaseCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "counter", HandleSunsReachReclamationCounterCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "gate", HandleSunwellGateCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "gatecounter", HandleSunwellGateCounterCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, }; static ChatCommandTable scourgeInvasionCommandTable = { - { "show", HandleScourgeInvasionCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "state", HandleScourgeInvasionStateCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "battleswon", HandleScourgeInvasionBattlesWonCommand, SEC_ADMINISTRATOR, Console::Yes }, - { "startzone", HandleScourgeInvasionStartZone, SEC_ADMINISTRATOR, Console::Yes }, + { "show", HandleScourgeInvasionCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "state", HandleScourgeInvasionStateCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "battleswon", HandleScourgeInvasionBattlesWonCommand, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, + { "startzone", HandleScourgeInvasionStartZone, rbac::RBAC_PERM_COMMAND_DEBUG, Console::Yes }, }; static ChatCommandTable worldStateCommandTable = diff --git a/src/server/scripts/Commands/cs_wp.cpp b/src/server/scripts/Commands/cs_wp.cpp index b4b80686a..efd07cf48 100644 --- a/src/server/scripts/Commands/cs_wp.cpp +++ b/src/server/scripts/Commands/cs_wp.cpp @@ -18,6 +18,7 @@ #include "Chat.h" #include "CommandScript.h" #include "Player.h" +#include "RBAC.h" #include "WaypointMgr.h" #if AC_COMPILER == AC_COMPILER_GNU @@ -35,13 +36,13 @@ public: { static ChatCommandTable wpCommandTable = { - { "add", HandleWpAddCommand, SEC_ADMINISTRATOR, Console::No }, - { "event", HandleWpEventCommand, SEC_ADMINISTRATOR, Console::No }, - { "load", HandleWpLoadCommand, SEC_ADMINISTRATOR, Console::No }, - { "modify", HandleWpModifyCommand, SEC_ADMINISTRATOR, Console::No }, - { "unload", HandleWpUnLoadCommand, SEC_ADMINISTRATOR, Console::No }, - { "reload", HandleWpReloadCommand, SEC_ADMINISTRATOR, Console::No }, - { "show", HandleWpShowCommand, SEC_ADMINISTRATOR, Console::No } + { "add", HandleWpAddCommand, rbac::RBAC_PERM_COMMAND_WP_ADD, Console::No }, + { "event", HandleWpEventCommand, rbac::RBAC_PERM_COMMAND_WP_EVENT, Console::No }, + { "load", HandleWpLoadCommand, rbac::RBAC_PERM_COMMAND_WP_LOAD, Console::No }, + { "modify", HandleWpModifyCommand, rbac::RBAC_PERM_COMMAND_WP_MODIFY, Console::No }, + { "unload", HandleWpUnLoadCommand, rbac::RBAC_PERM_COMMAND_WP_UNLOAD, Console::No }, + { "reload", HandleWpReloadCommand, rbac::RBAC_PERM_COMMAND_WP_RELOAD, Console::No }, + { "show", HandleWpShowCommand, rbac::RBAC_PERM_COMMAND_WP_SHOW, Console::No } }; static ChatCommandTable commandTable = { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp index a77af22d0..62932fe2e 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/instance_blackwing_lair.cpp @@ -181,21 +181,6 @@ struct instance_blackwing_lair : public InstanceScript return 0; } - bool CheckRequiredBosses(uint32 bossId, Player const* /* player */) const override - { - switch (bossId) - { - case DATA_BROODLORD_LASHLAYER: - if (GetBossState(DATA_VAELASTRAZ_THE_CORRUPT) != DONE) - return false; - break; - default: - break; - } - - return true; - } - bool SetBossState(uint32 type, EncounterState state) override { if (!InstanceScript::SetBossState(type, state)) @@ -304,6 +289,24 @@ struct instance_blackwing_lair : public InstanceScript return ObjectGuid::Empty; } + bool CheckRequiredBosses(uint32 bossId, Player const* player) const override + { + if (_SkipCheckRequiredBosses(player)) + return true; + + switch (bossId) + { + case DATA_BROODLORD_LASHLAYER: + if (GetBossState(DATA_VAELASTRAZ_THE_CORRUPT) != DONE) + return false; + break; + default: + break; + } + + return true; + } + void OnUnitDeath(Unit* unit) override { switch (unit->GetEntry()) diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index 3f0b45490..6b2077677 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -1421,10 +1421,8 @@ public: bool CheckRequiredBosses(uint32 bossId, Player const* player) const override { - if (player && player->GetSession() && player->GetSession()->GetSecurity() >= SEC_MODERATOR) - { + if (_SkipCheckRequiredBosses(player)) return true; - } switch (bossId) { diff --git a/src/test/mocks/WorldMock.h b/src/test/mocks/WorldMock.h index b37044119..e96e38b47 100644 --- a/src/test/mocks/WorldMock.h +++ b/src/test/mocks/WorldMock.h @@ -85,6 +85,7 @@ public: MOCK_METHOD(std::string const&, GetRealmName, (), (const)); MOCK_METHOD(void, SetRealmName, (std::string name), ()); MOCK_METHOD(void, RemoveOldCorpses, ()); + MOCK_METHOD(void, ReloadRBAC, ()); }; #pragma GCC diagnostic pop diff --git a/src/test/server/game/Accounts/RBACTest.cpp b/src/test/server/game/Accounts/RBACTest.cpp new file mode 100644 index 000000000..d48bd274e --- /dev/null +++ b/src/test/server/game/Accounts/RBACTest.cpp @@ -0,0 +1,2059 @@ +/* + * 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 General Public License as published by + * the Free Software Foundation; either version 2 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 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 . + */ + +/** + * @file RBACTest.cpp + * @brief Unit tests for the Role-Based Access Control (RBAC) system. + * + * Tests verify: + * 1. RBACPermission class construction and accessors + * 2. RBACData grant/deny/revoke operations + * 3. Permission state transitions + * 4. HasPermission checks with granted/denied lists + */ + +#include "AccountMgr.h" +#include "RBAC.h" +#include "gtest/gtest.h" +#include + +namespace +{ + +// Test permission IDs +constexpr uint32 TEST_PERM_1 = 1; +constexpr uint32 TEST_PERM_2 = 2; +constexpr uint32 TEST_PERM_3 = 3; +constexpr uint32 TEST_PERM_ROLE = 100; +constexpr uint32 TEST_PERM_INVALID = 99999; + +// Test account/realm data +constexpr uint32 TEST_ACCOUNT_ID = 1; +constexpr uint32 TEST_REALM_ID = 1; +constexpr uint8 TEST_SEC_LEVEL = 3; +const std::string TEST_ACCOUNT_NAME = "TestAccount"; + +/** + * @class RBACPermissionTest + * @brief Tests for RBACPermission class functionality. + */ +class RBACPermissionTest : public ::testing::Test +{ +protected: + void SetUp() override + { + permission = std::make_unique(TEST_PERM_1, "Test Permission"); + } + + std::unique_ptr permission; +}; + +// cppcheck-suppress syntaxError +TEST_F(RBACPermissionTest, Constructor_SetsIdAndName) +{ + EXPECT_EQ(permission->GetId(), TEST_PERM_1); + EXPECT_EQ(permission->GetName(), "Test Permission"); +} + +TEST_F(RBACPermissionTest, LinkedPermissions_InitiallyEmpty) +{ + EXPECT_TRUE(permission->GetLinkedPermissions().empty()); +} + +TEST_F(RBACPermissionTest, AddLinkedPermission_AddsToSet) +{ + permission->AddLinkedPermission(TEST_PERM_2); + permission->AddLinkedPermission(TEST_PERM_3); + + auto const& linked = permission->GetLinkedPermissions(); + EXPECT_EQ(linked.size(), 2u); + EXPECT_TRUE(linked.count(TEST_PERM_2) > 0); + EXPECT_TRUE(linked.count(TEST_PERM_3) > 0); +} + +TEST_F(RBACPermissionTest, AddLinkedPermission_NoDuplicates) +{ + permission->AddLinkedPermission(TEST_PERM_2); + permission->AddLinkedPermission(TEST_PERM_2); + permission->AddLinkedPermission(TEST_PERM_2); + + EXPECT_EQ(permission->GetLinkedPermissions().size(), 1u); +} + +/** + * @class RBACDataTest + * @brief Tests for RBACData class functionality with full server setup. + * + * Uses AccountMgr's test permission registration to allow proper + * GrantPermission/DenyPermission testing. + */ +class RBACDataTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Register test permissions with AccountMgr + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Test Permission 1"); + sAccountMgr->AddPermissionForTest(TEST_PERM_2, "Test Permission 2"); + sAccountMgr->AddPermissionForTest(TEST_PERM_3, "Test Permission 3"); + sAccountMgr->AddPermissionForTest(TEST_PERM_ROLE, "Test Role Permission"); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + // Clean up test permissions + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACDataTest, Constructor_SetsAccountData) +{ + EXPECT_EQ(rbacData->GetId(), TEST_ACCOUNT_ID); + EXPECT_EQ(rbacData->GetName(), TEST_ACCOUNT_NAME); + EXPECT_EQ(rbacData->GetSecurityLevel(), TEST_SEC_LEVEL); +} + +TEST_F(RBACDataTest, InitialState_EmptyPermissions) +{ + EXPECT_TRUE(rbacData->GetGrantedPermissions().empty()); + EXPECT_TRUE(rbacData->GetDeniedPermissions().empty()); +} + +// Grant Permission Tests + +TEST_F(RBACDataTest, GrantPermission_Success) +{ + rbac::RBACCommandResult result = rbacData->GrantPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_OK); + EXPECT_TRUE(rbacData->GetGrantedPermissions().count(TEST_PERM_1) > 0); +} + +TEST_F(RBACDataTest, GrantPermission_AlreadyGranted_ReturnsError) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbac::RBACCommandResult result = rbacData->GrantPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_CANT_ADD_ALREADY_ADDED); +} + +TEST_F(RBACDataTest, GrantPermission_WhenDenied_ReturnsError) +{ + rbacData->DenyPermission(TEST_PERM_1); + rbac::RBACCommandResult result = rbacData->GrantPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_IN_DENIED_LIST); +} + +TEST_F(RBACDataTest, GrantPermission_InvalidId_ReturnsError) +{ + rbac::RBACCommandResult result = rbacData->GrantPermission(TEST_PERM_INVALID); + EXPECT_EQ(result, rbac::RBAC_ID_DOES_NOT_EXISTS); +} + +// Deny Permission Tests + +TEST_F(RBACDataTest, DenyPermission_Success) +{ + rbac::RBACCommandResult result = rbacData->DenyPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_OK); + EXPECT_TRUE(rbacData->GetDeniedPermissions().count(TEST_PERM_1) > 0); +} + +TEST_F(RBACDataTest, DenyPermission_AlreadyDenied_ReturnsError) +{ + rbacData->DenyPermission(TEST_PERM_1); + rbac::RBACCommandResult result = rbacData->DenyPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_CANT_ADD_ALREADY_ADDED); +} + +TEST_F(RBACDataTest, DenyPermission_WhenGranted_ReturnsError) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbac::RBACCommandResult result = rbacData->DenyPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_IN_GRANTED_LIST); +} + +TEST_F(RBACDataTest, DenyPermission_InvalidId_ReturnsError) +{ + rbac::RBACCommandResult result = rbacData->DenyPermission(TEST_PERM_INVALID); + EXPECT_EQ(result, rbac::RBAC_ID_DOES_NOT_EXISTS); +} + +// Revoke Permission Tests + +TEST_F(RBACDataTest, RevokePermission_FromGranted_Success) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbac::RBACCommandResult result = rbacData->RevokePermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_OK); + EXPECT_TRUE(rbacData->GetGrantedPermissions().empty()); +} + +TEST_F(RBACDataTest, RevokePermission_FromDenied_Success) +{ + rbacData->DenyPermission(TEST_PERM_1); + rbac::RBACCommandResult result = rbacData->RevokePermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_OK); + EXPECT_TRUE(rbacData->GetDeniedPermissions().empty()); +} + +TEST_F(RBACDataTest, RevokePermission_NotInList_ReturnsError) +{ + rbac::RBACCommandResult result = rbacData->RevokePermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_CANT_REVOKE_NOT_IN_LIST); +} + +// HasPermission Tests + +TEST_F(RBACDataTest, HasPermission_WhenGranted_ReturnsTrue) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); +} + +TEST_F(RBACDataTest, HasPermission_WhenDenied_ReturnsFalse) +{ + rbacData->DenyPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); +} + +TEST_F(RBACDataTest, HasPermission_WhenNotSet_ReturnsFalse) +{ + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); +} + +// Multiple Permissions Tests + +TEST_F(RBACDataTest, MultiplePermissions_IndependentTracking) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbacData->DenyPermission(TEST_PERM_2); + rbacData->GrantPermission(TEST_PERM_3); + rbacData->RecalculatePermissions(); + + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_2)); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_3)); + + EXPECT_EQ(rbacData->GetGrantedPermissions().size(), 2u); + EXPECT_EQ(rbacData->GetDeniedPermissions().size(), 1u); +} + +TEST_F(RBACDataTest, RevokeAndRegrant_WorksCorrectly) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); + + rbacData->RevokePermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); + + // Now can grant again + rbac::RBACCommandResult result = rbacData->GrantPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_OK); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); +} + +TEST_F(RBACDataTest, RevokeAndDeny_WorksCorrectly) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); + + rbacData->RevokePermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); + + // Now can deny + rbac::RBACCommandResult result = rbacData->DenyPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_OK); + rbacData->RecalculatePermissions(); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); + EXPECT_TRUE(rbacData->GetDeniedPermissions().count(TEST_PERM_1) > 0); +} + +/** + * @class RBACCommandResultTest + * @brief Tests to verify all RBACCommandResult values are handled. + */ +class RBACCommandResultTest : public ::testing::Test +{ +}; + +TEST_F(RBACCommandResultTest, AllResultValuesAreDefined) +{ + // Verify enum values exist and are distinct + EXPECT_NE(rbac::RBAC_OK, rbac::RBAC_CANT_ADD_ALREADY_ADDED); + EXPECT_NE(rbac::RBAC_OK, rbac::RBAC_CANT_REVOKE_NOT_IN_LIST); + EXPECT_NE(rbac::RBAC_OK, rbac::RBAC_IN_GRANTED_LIST); + EXPECT_NE(rbac::RBAC_OK, rbac::RBAC_IN_DENIED_LIST); + EXPECT_NE(rbac::RBAC_OK, rbac::RBAC_ID_DOES_NOT_EXISTS); +} + +/** + * @class RBACLinkedPermissionsTest + * @brief Tests for permission inheritance through linked permissions. + */ +class RBACLinkedPermissionsTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Register test permissions + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Base Permission 1"); + sAccountMgr->AddPermissionForTest(TEST_PERM_2, "Base Permission 2"); + sAccountMgr->AddPermissionForTest(TEST_PERM_3, "Base Permission 3"); + sAccountMgr->AddPermissionForTest(TEST_PERM_ROLE, "Role Permission"); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACLinkedPermissionsTest, GrantedPermission_IsInGrantedList) +{ + rbacData->GrantPermission(TEST_PERM_1); + + auto const& granted = rbacData->GetGrantedPermissions(); + EXPECT_TRUE(granted.count(TEST_PERM_1) > 0); +} + +TEST_F(RBACLinkedPermissionsTest, DeniedPermission_IsInDeniedList) +{ + rbacData->DenyPermission(TEST_PERM_1); + + auto const& denied = rbacData->GetDeniedPermissions(); + EXPECT_TRUE(denied.count(TEST_PERM_1) > 0); +} + +TEST_F(RBACLinkedPermissionsTest, DeniedPermission_OverridesGranted) +{ + // Grant permission, then revoke and deny + rbacData->GrantPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); + + rbacData->RevokePermission(TEST_PERM_1); + rbacData->DenyPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); +} + +/** + * @class RBACSecurityLevelTest + * @brief Tests for security level accessor. + */ +class RBACSecurityLevelTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Register a test permission for the test + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Test Permission"); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACSecurityLevelTest, Constructor_PreservesSecurityLevel) +{ + rbac::RBACData rbacLevel0(1, "Player", 1, 0); + EXPECT_EQ(rbacLevel0.GetSecurityLevel(), 0); + + rbac::RBACData rbacLevel1(2, "Moderator", 1, 1); + EXPECT_EQ(rbacLevel1.GetSecurityLevel(), 1); + + rbac::RBACData rbacLevel2(3, "GameMaster", 1, 2); + EXPECT_EQ(rbacLevel2.GetSecurityLevel(), 2); + + rbac::RBACData rbacLevel3(4, "Administrator", 1, 3); + EXPECT_EQ(rbacLevel3.GetSecurityLevel(), 3); +} + +TEST_F(RBACSecurityLevelTest, SetSecurityLevelForTest_UpdatesLevel) +{ + rbac::RBACData rbac(1, "TestAccount", 1, 0); + EXPECT_EQ(rbac.GetSecurityLevel(), 0); + + // Use test-friendly setter that doesn't call LoadFromDB + rbac.SetSecurityLevelForTest(3); + EXPECT_EQ(rbac.GetSecurityLevel(), 3); +} + +/** + * @class RBACPermissionExpansionTest + * @brief Tests for permission expansion through linked permissions. + * + * Tests verify that when a role/permission with linked permissions is granted, + * all linked permissions become effective via ExpandPermissions(). + */ +class RBACPermissionExpansionTest : public ::testing::Test +{ +protected: + // Permission structure for testing: + // ROLE_GM (100) -> links to PERM_KICK (10), PERM_BAN (11) + // ROLE_ADMIN (101) -> links to ROLE_GM (100), PERM_SHUTDOWN (12) + // This creates a hierarchy: ADMIN -> GM -> individual permissions + + static constexpr uint32 PERM_KICK = 10; + static constexpr uint32 PERM_BAN = 11; + static constexpr uint32 PERM_SHUTDOWN = 12; + static constexpr uint32 PERM_TELEPORT = 13; + static constexpr uint32 ROLE_GM = 100; + static constexpr uint32 ROLE_ADMIN = 101; + + void SetUp() override + { + // Register individual permissions + sAccountMgr->AddPermissionForTest(PERM_KICK, "Kick Permission"); + sAccountMgr->AddPermissionForTest(PERM_BAN, "Ban Permission"); + sAccountMgr->AddPermissionForTest(PERM_SHUTDOWN, "Shutdown Permission"); + sAccountMgr->AddPermissionForTest(PERM_TELEPORT, "Teleport Permission"); + + // Register role permissions + sAccountMgr->AddPermissionForTest(ROLE_GM, "GameMaster Role"); + sAccountMgr->AddPermissionForTest(ROLE_ADMIN, "Administrator Role"); + + // Set up permission hierarchy: + // GM role includes kick and ban + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, PERM_KICK); + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, PERM_BAN); + + // Admin role includes GM role and shutdown + sAccountMgr->AddLinkedPermissionForTest(ROLE_ADMIN, ROLE_GM); + sAccountMgr->AddLinkedPermissionForTest(ROLE_ADMIN, PERM_SHUTDOWN); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACPermissionExpansionTest, GrantRole_ExpandsLinkedPermissions) +{ + // Grant GM role + rbacData->GrantPermission(ROLE_GM); + rbacData->RecalculatePermissions(); + + // Should have access to GM role and linked permissions (kick, ban) + EXPECT_TRUE(rbacData->HasPermission(ROLE_GM)); + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + EXPECT_TRUE(rbacData->HasPermission(PERM_BAN)); + + // Should NOT have access to unlinked permissions + EXPECT_FALSE(rbacData->HasPermission(PERM_SHUTDOWN)); + EXPECT_FALSE(rbacData->HasPermission(PERM_TELEPORT)); +} + +TEST_F(RBACPermissionExpansionTest, GrantRole_RecursiveExpansion) +{ + // Grant Admin role (which links to GM role, which links to kick/ban) + rbacData->GrantPermission(ROLE_ADMIN); + rbacData->RecalculatePermissions(); + + // Should have access to Admin role + EXPECT_TRUE(rbacData->HasPermission(ROLE_ADMIN)); + + // Should have access to directly linked permissions (GM role, shutdown) + EXPECT_TRUE(rbacData->HasPermission(ROLE_GM)); + EXPECT_TRUE(rbacData->HasPermission(PERM_SHUTDOWN)); + + // Should have access to recursively linked permissions (from GM role) + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + EXPECT_TRUE(rbacData->HasPermission(PERM_BAN)); + + // Should NOT have access to unlinked permissions + EXPECT_FALSE(rbacData->HasPermission(PERM_TELEPORT)); +} + +TEST_F(RBACPermissionExpansionTest, DenyRole_PreventsLinkedPermissions) +{ + // Grant teleport individually + rbacData->GrantPermission(PERM_TELEPORT); + + // Grant admin role (gives kick, ban, shutdown via expansion) + rbacData->GrantPermission(ROLE_ADMIN); + rbacData->RecalculatePermissions(); + + // Verify all permissions are granted + EXPECT_TRUE(rbacData->HasPermission(ROLE_ADMIN)); + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + EXPECT_TRUE(rbacData->HasPermission(PERM_TELEPORT)); + + // Now revoke admin and deny the GM role + rbacData->RevokePermission(ROLE_ADMIN); + rbacData->DenyPermission(ROLE_GM); + rbacData->RecalculatePermissions(); + + // GM role and its linked permissions should be denied + EXPECT_FALSE(rbacData->HasPermission(ROLE_GM)); + EXPECT_FALSE(rbacData->HasPermission(PERM_KICK)); + EXPECT_FALSE(rbacData->HasPermission(PERM_BAN)); + + // Teleport was granted directly, not through GM, so should still work + EXPECT_TRUE(rbacData->HasPermission(PERM_TELEPORT)); +} + +TEST_F(RBACPermissionExpansionTest, DenyRole_OverridesIndividualGrant) +{ + // Grant kick permission individually + rbacData->GrantPermission(PERM_KICK); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + + // Deny the GM role (which links to kick) + rbacData->DenyPermission(ROLE_GM); + rbacData->RecalculatePermissions(); + + // Denying a role expands to deny all linked permissions + // Even though kick was granted individually, denying GM role + // expands to deny kick as well (RBAC deny expansion) + EXPECT_FALSE(rbacData->HasPermission(PERM_KICK)); + EXPECT_FALSE(rbacData->HasPermission(ROLE_GM)); + EXPECT_FALSE(rbacData->HasPermission(PERM_BAN)); +} + +/** + * @class RBACDefaultPermissionsTest + * @brief Tests for default permissions based on security level. + * + * Tests verify that default permissions from rbac_default_permissions + * are properly applied based on account security level. + */ +class RBACDefaultPermissionsTest : public ::testing::Test +{ +protected: + static constexpr uint32 PERM_PLAYER_1 = 20; + static constexpr uint32 PERM_PLAYER_2 = 21; + static constexpr uint32 PERM_MOD_1 = 30; + static constexpr uint32 PERM_GM_1 = 40; + static constexpr uint32 PERM_ADMIN_1 = 50; + + static constexpr uint8 SEC_PLAYER = 0; + static constexpr uint8 SEC_MODERATOR = 1; + static constexpr uint8 SEC_GAMEMASTER = 2; + static constexpr uint8 SEC_ADMINISTRATOR = 3; + + void SetUp() override + { + // Register test permissions + sAccountMgr->AddPermissionForTest(PERM_PLAYER_1, "Player Permission 1"); + sAccountMgr->AddPermissionForTest(PERM_PLAYER_2, "Player Permission 2"); + sAccountMgr->AddPermissionForTest(PERM_MOD_1, "Moderator Permission 1"); + sAccountMgr->AddPermissionForTest(PERM_GM_1, "GameMaster Permission 1"); + sAccountMgr->AddPermissionForTest(PERM_ADMIN_1, "Administrator Permission 1"); + + // Set up default permissions for security levels + sAccountMgr->AddDefaultPermissionForTest(SEC_PLAYER, PERM_PLAYER_1); + sAccountMgr->AddDefaultPermissionForTest(SEC_PLAYER, PERM_PLAYER_2); + + sAccountMgr->AddDefaultPermissionForTest(SEC_MODERATOR, PERM_MOD_1); + + sAccountMgr->AddDefaultPermissionForTest(SEC_GAMEMASTER, PERM_GM_1); + + sAccountMgr->AddDefaultPermissionForTest(SEC_ADMINISTRATOR, PERM_ADMIN_1); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACDefaultPermissionsTest, GetDefaultPermissions_ReturnsCorrectSet) +{ + rbac::RBACPermissionContainer const& playerPerms = sAccountMgr->GetRBACDefaultPermissions(SEC_PLAYER); + EXPECT_EQ(playerPerms.size(), 2u); + EXPECT_TRUE(playerPerms.count(PERM_PLAYER_1) > 0); + EXPECT_TRUE(playerPerms.count(PERM_PLAYER_2) > 0); + + rbac::RBACPermissionContainer const& modPerms = sAccountMgr->GetRBACDefaultPermissions(SEC_MODERATOR); + EXPECT_EQ(modPerms.size(), 1u); + EXPECT_TRUE(modPerms.count(PERM_MOD_1) > 0); + + rbac::RBACPermissionContainer const& gmPerms = sAccountMgr->GetRBACDefaultPermissions(SEC_GAMEMASTER); + EXPECT_EQ(gmPerms.size(), 1u); + EXPECT_TRUE(gmPerms.count(PERM_GM_1) > 0); + + rbac::RBACPermissionContainer const& adminPerms = sAccountMgr->GetRBACDefaultPermissions(SEC_ADMINISTRATOR); + EXPECT_EQ(adminPerms.size(), 1u); + EXPECT_TRUE(adminPerms.count(PERM_ADMIN_1) > 0); +} + +TEST_F(RBACDefaultPermissionsTest, GetDefaultPermissions_EmptySecLevel_ReturnsEmpty) +{ + // Security level 255 has no default permissions set + rbac::RBACPermissionContainer const& emptyPerms = sAccountMgr->GetRBACDefaultPermissions(255); + EXPECT_TRUE(emptyPerms.empty()); +} + +TEST_F(RBACDefaultPermissionsTest, SecurityLevel_DeterminesDefaultPermissions) +{ + // Create RBACData for a player account (sec level 0) + rbac::RBACData playerData(1, "PlayerAccount", TEST_REALM_ID, SEC_PLAYER); + + // Manually grant the default permissions (simulating LoadFromDBCallback) + rbac::RBACPermissionContainer const& playerDefaults = sAccountMgr->GetRBACDefaultPermissions(SEC_PLAYER); + for (uint32 perm : playerDefaults) + playerData.GrantPermission(perm); + + playerData.RecalculatePermissions(); + + // Player should have player permissions + EXPECT_TRUE(playerData.HasPermission(PERM_PLAYER_1)); + EXPECT_TRUE(playerData.HasPermission(PERM_PLAYER_2)); + + // Player should NOT have higher level permissions + EXPECT_FALSE(playerData.HasPermission(PERM_MOD_1)); + EXPECT_FALSE(playerData.HasPermission(PERM_GM_1)); + EXPECT_FALSE(playerData.HasPermission(PERM_ADMIN_1)); +} + +TEST_F(RBACDefaultPermissionsTest, HigherSecLevel_GetsOnlyOwnDefaults) +{ + // Create RBACData for an admin account (sec level 3) + rbac::RBACData adminData(4, "AdminAccount", TEST_REALM_ID, SEC_ADMINISTRATOR); + + // Manually grant the default permissions for admin level + rbac::RBACPermissionContainer const& adminDefaults = sAccountMgr->GetRBACDefaultPermissions(SEC_ADMINISTRATOR); + for (uint32 perm : adminDefaults) + adminData.GrantPermission(perm); + + adminData.RecalculatePermissions(); + + // Admin gets their own default permissions + EXPECT_TRUE(adminData.HasPermission(PERM_ADMIN_1)); + + // Note: In a real setup, admin would typically have a role that links to + // lower permissions. Here we're just testing that default permissions + // are applied per security level, not inherited. + // Without explicit roles linking them, admin doesn't automatically get + // player/mod/gm permissions. + EXPECT_FALSE(adminData.HasPermission(PERM_PLAYER_1)); + EXPECT_FALSE(adminData.HasPermission(PERM_MOD_1)); + EXPECT_FALSE(adminData.HasPermission(PERM_GM_1)); +} + +/** + * @class RBACDeniedExpansionTest + * @brief Tests for denied permission expansion. + * + * Tests verify that denied permissions are properly expanded + * and remove access to linked permissions. + */ +class RBACDeniedExpansionTest : public ::testing::Test +{ +protected: + static constexpr uint32 PERM_KICK = 10; + static constexpr uint32 PERM_BAN = 11; + static constexpr uint32 ROLE_GM = 100; + + void SetUp() override + { + sAccountMgr->AddPermissionForTest(PERM_KICK, "Kick Permission"); + sAccountMgr->AddPermissionForTest(PERM_BAN, "Ban Permission"); + sAccountMgr->AddPermissionForTest(ROLE_GM, "GameMaster Role"); + + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, PERM_KICK); + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, PERM_BAN); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACDeniedExpansionTest, DenyRole_ExpandsDeniedLinkedPermissions) +{ + // First grant individual permissions that would be linked from GM + rbacData->GrantPermission(PERM_KICK); + rbacData->GrantPermission(PERM_BAN); + rbacData->RecalculatePermissions(); + + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + EXPECT_TRUE(rbacData->HasPermission(PERM_BAN)); + + // Now deny the GM role + rbacData->DenyPermission(ROLE_GM); + rbacData->RecalculatePermissions(); + + // The GM role and its linked permissions should be denied + // Even though kick/ban were granted individually, denying the role + // that links them should expand to deny those permissions + EXPECT_FALSE(rbacData->HasPermission(ROLE_GM)); + EXPECT_FALSE(rbacData->HasPermission(PERM_KICK)); + EXPECT_FALSE(rbacData->HasPermission(PERM_BAN)); +} + +TEST_F(RBACDeniedExpansionTest, GrantAndDenySameRole_DenyWins) +{ + // Grant the GM role + rbacData->GrantPermission(ROLE_GM); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(ROLE_GM)); + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + + // Revoke and then deny + rbacData->RevokePermission(ROLE_GM); + rbacData->DenyPermission(ROLE_GM); + rbacData->RecalculatePermissions(); + + // Deny should win - no access + EXPECT_FALSE(rbacData->HasPermission(ROLE_GM)); + EXPECT_FALSE(rbacData->HasPermission(PERM_KICK)); + EXPECT_FALSE(rbacData->HasPermission(PERM_BAN)); +} + +// --------------------------------------------------------------------------- +// Suite 1: RBACPermissionRemoveLinkedTest +// --------------------------------------------------------------------------- +class RBACPermissionRemoveLinkedTest : public ::testing::Test +{ +protected: + void SetUp() override + { + permission = std::make_unique(TEST_PERM_1, "Test Permission"); + permission->AddLinkedPermission(TEST_PERM_2); + permission->AddLinkedPermission(TEST_PERM_3); + } + + std::unique_ptr permission; +}; + +TEST_F(RBACPermissionRemoveLinkedTest, RemoveLinkedPermission_RemovesFromSet) +{ + EXPECT_EQ(permission->GetLinkedPermissions().size(), 2u); + permission->RemoveLinkedPermission(TEST_PERM_2); + EXPECT_EQ(permission->GetLinkedPermissions().size(), 1u); + EXPECT_EQ(permission->GetLinkedPermissions().count(TEST_PERM_2), 0u); + EXPECT_EQ(permission->GetLinkedPermissions().count(TEST_PERM_3), 1u); +} + +TEST_F(RBACPermissionRemoveLinkedTest, RemoveNonExistent_IsNoOp) +{ + permission->RemoveLinkedPermission(TEST_PERM_INVALID); + EXPECT_EQ(permission->GetLinkedPermissions().size(), 2u); +} + +TEST_F(RBACPermissionRemoveLinkedTest, RemoveAll_LeavesEmpty) +{ + permission->RemoveLinkedPermission(TEST_PERM_2); + permission->RemoveLinkedPermission(TEST_PERM_3); + EXPECT_TRUE(permission->GetLinkedPermissions().empty()); +} + +// --------------------------------------------------------------------------- +// Suite 2: RBACGetPermissionsTest (computed global perms) +// --------------------------------------------------------------------------- +class RBACGetPermissionsTest : public ::testing::Test +{ +protected: + static constexpr uint32 PERM_A = 10; + static constexpr uint32 PERM_B = 11; + static constexpr uint32 PERM_C = 12; + static constexpr uint32 ROLE_R = 100; + + void SetUp() override + { + sAccountMgr->AddPermissionForTest(PERM_A, "Permission A"); + sAccountMgr->AddPermissionForTest(PERM_B, "Permission B"); + sAccountMgr->AddPermissionForTest(PERM_C, "Permission C"); + sAccountMgr->AddPermissionForTest(ROLE_R, "Role R"); + + sAccountMgr->AddLinkedPermissionForTest(ROLE_R, PERM_A); + sAccountMgr->AddLinkedPermissionForTest(ROLE_R, PERM_B); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACGetPermissionsTest, Empty_WhenNothingGranted) +{ + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->GetPermissions().empty()); +} + +TEST_F(RBACGetPermissionsTest, MatchesHasPermission) +{ + rbacData->GrantPermission(PERM_A); + rbacData->GrantPermission(PERM_C); + rbacData->RecalculatePermissions(); + + auto const& perms = rbacData->GetPermissions(); + for (uint32 p : perms) + EXPECT_TRUE(rbacData->HasPermission(p)); + EXPECT_FALSE(rbacData->HasPermission(PERM_B)); +} + +TEST_F(RBACGetPermissionsTest, ExcludesDeniedPermissions) +{ + rbacData->GrantPermission(PERM_A); + rbacData->GrantPermission(PERM_C); + rbacData->DenyPermission(PERM_B); + rbacData->RecalculatePermissions(); + + auto const& perms = rbacData->GetPermissions(); + EXPECT_TRUE(perms.count(PERM_A) > 0); + EXPECT_TRUE(perms.count(PERM_C) > 0); + EXPECT_EQ(perms.count(PERM_B), 0u); +} + +TEST_F(RBACGetPermissionsTest, IncludesExpandedLinkedPermissions) +{ + rbacData->GrantPermission(ROLE_R); + rbacData->RecalculatePermissions(); + + auto const& perms = rbacData->GetPermissions(); + EXPECT_TRUE(perms.count(ROLE_R) > 0); + EXPECT_TRUE(perms.count(PERM_A) > 0); + EXPECT_TRUE(perms.count(PERM_B) > 0); + EXPECT_EQ(perms.count(PERM_C), 0u); +} + +TEST_F(RBACGetPermissionsTest, ReflectsStateAfterRecalculate) +{ + rbacData->GrantPermission(PERM_A); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->GetPermissions().count(PERM_A) > 0); + + rbacData->RevokePermission(PERM_A); + rbacData->RecalculatePermissions(); + EXPECT_EQ(rbacData->GetPermissions().count(PERM_A), 0u); +} + +// --------------------------------------------------------------------------- +// Suite 3: RBACRevokeEdgeCasesTest +// --------------------------------------------------------------------------- +class RBACRevokeEdgeCasesTest : public ::testing::Test +{ +protected: + void SetUp() override + { + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Permission 1"); + sAccountMgr->AddPermissionForTest(TEST_PERM_2, "Permission 2"); + sAccountMgr->AddPermissionForTest(TEST_PERM_3, "Permission 3"); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACRevokeEdgeCasesTest, RevokeUnregistered_ReturnsCantRevoke) +{ + rbac::RBACCommandResult result = rbacData->RevokePermission(TEST_PERM_INVALID); + EXPECT_EQ(result, rbac::RBAC_CANT_REVOKE_NOT_IN_LIST); +} + +TEST_F(RBACRevokeEdgeCasesTest, RevokeFromEmpty_ReturnsError) +{ + rbac::RBACCommandResult result = rbacData->RevokePermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_CANT_REVOKE_NOT_IN_LIST); +} + +TEST_F(RBACRevokeEdgeCasesTest, RevokeGranted_ThenDeny) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); + + rbacData->RevokePermission(TEST_PERM_1); + rbac::RBACCommandResult result = rbacData->DenyPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_OK); + rbacData->RecalculatePermissions(); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); + EXPECT_TRUE(rbacData->GetDeniedPermissions().count(TEST_PERM_1) > 0); +} + +TEST_F(RBACRevokeEdgeCasesTest, RevokeDenied_ThenGrant) +{ + rbacData->DenyPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); + + rbacData->RevokePermission(TEST_PERM_1); + rbac::RBACCommandResult result = rbacData->GrantPermission(TEST_PERM_1); + EXPECT_EQ(result, rbac::RBAC_OK); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); +} + +TEST_F(RBACRevokeEdgeCasesTest, RevokeDoesNotAffectOtherPermissions) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbacData->GrantPermission(TEST_PERM_2); + rbacData->DenyPermission(TEST_PERM_3); + + rbacData->RevokePermission(TEST_PERM_1); + + EXPECT_TRUE(rbacData->GetGrantedPermissions().count(TEST_PERM_2) > 0); + EXPECT_TRUE(rbacData->GetDeniedPermissions().count(TEST_PERM_3) > 0); + EXPECT_EQ(rbacData->GetGrantedPermissions().count(TEST_PERM_1), 0u); +} + +// --------------------------------------------------------------------------- +// Suite 4: RBACComplexHierarchyTest +// --------------------------------------------------------------------------- +class RBACComplexHierarchyTest : public ::testing::Test +{ +protected: + // 3-level: Admin(100)->GM(101)->Mod(102)->individual perms + static constexpr uint32 PERM_A = 10; + static constexpr uint32 PERM_B = 11; + static constexpr uint32 PERM_C = 12; + static constexpr uint32 PERM_D = 13; + static constexpr uint32 PERM_E = 14; + static constexpr uint32 PERM_F = 15; + static constexpr uint32 PERM_G = 16; + static constexpr uint32 PERM_H = 17; + static constexpr uint32 PERM_I = 18; + static constexpr uint32 PERM_J = 19; + static constexpr uint32 PERM_K = 20; + static constexpr uint32 PERM_L = 21; + static constexpr uint32 ROLE_MOD = 102; + static constexpr uint32 ROLE_GM = 101; + static constexpr uint32 ROLE_ADMIN = 100; + // Diamond: ROLE_X(103) and ROLE_Y(104) both link to PERM_SHARED(22) + static constexpr uint32 PERM_SHARED = 22; + static constexpr uint32 PERM_X_ONLY = 23; + static constexpr uint32 PERM_Y_ONLY = 24; + static constexpr uint32 ROLE_X = 103; + static constexpr uint32 ROLE_Y = 104; + // Wide role + static constexpr uint32 ROLE_WIDE = 105; + + void SetUp() override + { + // Register individual perms + sAccountMgr->AddPermissionForTest(PERM_A, "A"); + sAccountMgr->AddPermissionForTest(PERM_B, "B"); + sAccountMgr->AddPermissionForTest(PERM_C, "C"); + sAccountMgr->AddPermissionForTest(PERM_D, "D"); + sAccountMgr->AddPermissionForTest(PERM_E, "E"); + sAccountMgr->AddPermissionForTest(PERM_F, "F"); + sAccountMgr->AddPermissionForTest(PERM_G, "G"); + sAccountMgr->AddPermissionForTest(PERM_H, "H"); + sAccountMgr->AddPermissionForTest(PERM_I, "I"); + sAccountMgr->AddPermissionForTest(PERM_J, "J"); + sAccountMgr->AddPermissionForTest(PERM_K, "K"); + sAccountMgr->AddPermissionForTest(PERM_L, "L"); + sAccountMgr->AddPermissionForTest(PERM_SHARED, "Shared"); + sAccountMgr->AddPermissionForTest(PERM_X_ONLY, "X Only"); + sAccountMgr->AddPermissionForTest(PERM_Y_ONLY, "Y Only"); + + // Register roles + sAccountMgr->AddPermissionForTest(ROLE_MOD, "Mod"); + sAccountMgr->AddPermissionForTest(ROLE_GM, "GM"); + sAccountMgr->AddPermissionForTest(ROLE_ADMIN, "Admin"); + sAccountMgr->AddPermissionForTest(ROLE_X, "X"); + sAccountMgr->AddPermissionForTest(ROLE_Y, "Y"); + sAccountMgr->AddPermissionForTest(ROLE_WIDE, "Wide"); + + // 3-level hierarchy + sAccountMgr->AddLinkedPermissionForTest(ROLE_MOD, PERM_A); + sAccountMgr->AddLinkedPermissionForTest(ROLE_MOD, PERM_B); + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, ROLE_MOD); + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, PERM_C); + sAccountMgr->AddLinkedPermissionForTest(ROLE_ADMIN, ROLE_GM); + sAccountMgr->AddLinkedPermissionForTest(ROLE_ADMIN, PERM_D); + + // Diamond inheritance + sAccountMgr->AddLinkedPermissionForTest(ROLE_X, PERM_SHARED); + sAccountMgr->AddLinkedPermissionForTest(ROLE_X, PERM_X_ONLY); + sAccountMgr->AddLinkedPermissionForTest(ROLE_Y, PERM_SHARED); + sAccountMgr->AddLinkedPermissionForTest(ROLE_Y, PERM_Y_ONLY); + + // Wide role (10+ linked perms) + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_A); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_B); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_C); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_D); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_E); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_F); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_G); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_H); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_I); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_J); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_K); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WIDE, PERM_L); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACComplexHierarchyTest, ThreeLevelDeep) +{ + rbacData->GrantPermission(ROLE_ADMIN); + rbacData->RecalculatePermissions(); + + EXPECT_TRUE(rbacData->HasPermission(ROLE_ADMIN)); + EXPECT_TRUE(rbacData->HasPermission(ROLE_GM)); + EXPECT_TRUE(rbacData->HasPermission(ROLE_MOD)); + EXPECT_TRUE(rbacData->HasPermission(PERM_A)); + EXPECT_TRUE(rbacData->HasPermission(PERM_B)); + EXPECT_TRUE(rbacData->HasPermission(PERM_C)); + EXPECT_TRUE(rbacData->HasPermission(PERM_D)); +} + +TEST_F(RBACComplexHierarchyTest, DiamondInheritance) +{ + rbacData->GrantPermission(ROLE_X); + rbacData->GrantPermission(ROLE_Y); + rbacData->RecalculatePermissions(); + + EXPECT_TRUE(rbacData->HasPermission(PERM_SHARED)); + EXPECT_TRUE(rbacData->HasPermission(PERM_X_ONLY)); + EXPECT_TRUE(rbacData->HasPermission(PERM_Y_ONLY)); +} + +TEST_F(RBACComplexHierarchyTest, WideRole) +{ + rbacData->GrantPermission(ROLE_WIDE); + rbacData->RecalculatePermissions(); + + EXPECT_TRUE(rbacData->HasPermission(ROLE_WIDE)); + EXPECT_TRUE(rbacData->HasPermission(PERM_A)); + EXPECT_TRUE(rbacData->HasPermission(PERM_L)); + EXPECT_EQ(rbacData->GetPermissions().size(), 13u); // ROLE_WIDE + 12 perms +} + +TEST_F(RBACComplexHierarchyTest, TwoOverlappingRoles_NoDuplicateInGetPermissions) +{ + // Both ROLE_X and ROLE_Y link to PERM_SHARED + rbacData->GrantPermission(ROLE_X); + rbacData->GrantPermission(ROLE_Y); + rbacData->RecalculatePermissions(); + + auto const& perms = rbacData->GetPermissions(); + // ROLE_X, ROLE_Y, PERM_SHARED, PERM_X_ONLY, PERM_Y_ONLY = 5 + EXPECT_EQ(perms.size(), 5u); + EXPECT_EQ(perms.count(PERM_SHARED), 1u); +} + +TEST_F(RBACComplexHierarchyTest, RevokeOneOverlappingRole_KeepsSharedFromOther) +{ + rbacData->GrantPermission(ROLE_X); + rbacData->GrantPermission(ROLE_Y); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(PERM_SHARED)); + + rbacData->RevokePermission(ROLE_X); + rbacData->RecalculatePermissions(); + + // PERM_SHARED still accessible through ROLE_Y + EXPECT_TRUE(rbacData->HasPermission(PERM_SHARED)); + EXPECT_TRUE(rbacData->HasPermission(PERM_Y_ONLY)); + EXPECT_FALSE(rbacData->HasPermission(PERM_X_ONLY)); +} + +// --------------------------------------------------------------------------- +// Suite 5: RBACDenySpecificFromRoleTest +// --------------------------------------------------------------------------- +class RBACDenySpecificFromRoleTest : public ::testing::Test +{ +protected: + static constexpr uint32 PERM_KICK = 10; + static constexpr uint32 PERM_BAN = 11; + static constexpr uint32 PERM_MUTE = 12; + static constexpr uint32 PERM_SHUTDOWN = 13; + static constexpr uint32 ROLE_MOD = 100; + static constexpr uint32 ROLE_ADMIN = 101; + + void SetUp() override + { + sAccountMgr->AddPermissionForTest(PERM_KICK, "Kick"); + sAccountMgr->AddPermissionForTest(PERM_BAN, "Ban"); + sAccountMgr->AddPermissionForTest(PERM_MUTE, "Mute"); + sAccountMgr->AddPermissionForTest(PERM_SHUTDOWN, "Shutdown"); + sAccountMgr->AddPermissionForTest(ROLE_MOD, "Moderator"); + sAccountMgr->AddPermissionForTest(ROLE_ADMIN, "Admin"); + + sAccountMgr->AddLinkedPermissionForTest(ROLE_MOD, PERM_KICK); + sAccountMgr->AddLinkedPermissionForTest(ROLE_MOD, PERM_BAN); + sAccountMgr->AddLinkedPermissionForTest(ROLE_MOD, PERM_MUTE); + sAccountMgr->AddLinkedPermissionForTest(ROLE_ADMIN, PERM_SHUTDOWN); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACDenySpecificFromRoleTest, DenyOneChild_SiblingsStillGranted) +{ + rbacData->GrantPermission(ROLE_MOD); + rbacData->DenyPermission(PERM_BAN); + rbacData->RecalculatePermissions(); + + EXPECT_TRUE(rbacData->HasPermission(ROLE_MOD)); + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + EXPECT_TRUE(rbacData->HasPermission(PERM_MUTE)); + EXPECT_FALSE(rbacData->HasPermission(PERM_BAN)); +} + +TEST_F(RBACDenySpecificFromRoleTest, DenyPermNotInRole_RolePermsUnaffected) +{ + rbacData->GrantPermission(ROLE_MOD); + rbacData->DenyPermission(PERM_SHUTDOWN); + rbacData->RecalculatePermissions(); + + EXPECT_TRUE(rbacData->HasPermission(ROLE_MOD)); + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + EXPECT_TRUE(rbacData->HasPermission(PERM_BAN)); + EXPECT_TRUE(rbacData->HasPermission(PERM_MUTE)); + EXPECT_FALSE(rbacData->HasPermission(PERM_SHUTDOWN)); +} + +TEST_F(RBACDenySpecificFromRoleTest, GrantTwoRoles_DenyOneRole) +{ + rbacData->GrantPermission(ROLE_MOD); + rbacData->GrantPermission(ROLE_ADMIN); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(PERM_SHUTDOWN)); + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + + // Revoke and deny ROLE_ADMIN + rbacData->RevokePermission(ROLE_ADMIN); + rbacData->DenyPermission(ROLE_ADMIN); + rbacData->RecalculatePermissions(); + + // ROLE_ADMIN and its unique perm (SHUTDOWN) denied + EXPECT_FALSE(rbacData->HasPermission(ROLE_ADMIN)); + EXPECT_FALSE(rbacData->HasPermission(PERM_SHUTDOWN)); + + // ROLE_MOD perms remain + EXPECT_TRUE(rbacData->HasPermission(ROLE_MOD)); + EXPECT_TRUE(rbacData->HasPermission(PERM_KICK)); + EXPECT_TRUE(rbacData->HasPermission(PERM_BAN)); + EXPECT_TRUE(rbacData->HasPermission(PERM_MUTE)); +} + +// --------------------------------------------------------------------------- +// Suite 6: RBACDefaultPermissionsWithRolesTest +// --------------------------------------------------------------------------- +class RBACDefaultPermissionsWithRolesTest : public ::testing::Test +{ +protected: + static constexpr uint32 PERM_A = 10; + static constexpr uint32 PERM_B = 11; + static constexpr uint32 PERM_C = 12; + static constexpr uint32 PERM_MOD_1 = 13; + static constexpr uint32 ROLE_PLAYER = 100; + + static constexpr uint8 SEC_PLAYER = 0; + static constexpr uint8 SEC_MODERATOR = 1; + + void SetUp() override + { + sAccountMgr->AddPermissionForTest(PERM_A, "A"); + sAccountMgr->AddPermissionForTest(PERM_B, "B"); + sAccountMgr->AddPermissionForTest(PERM_C, "C"); + sAccountMgr->AddPermissionForTest(PERM_MOD_1, "Mod 1"); + sAccountMgr->AddPermissionForTest(ROLE_PLAYER, "Player Role"); + + sAccountMgr->AddLinkedPermissionForTest(ROLE_PLAYER, PERM_A); + sAccountMgr->AddLinkedPermissionForTest(ROLE_PLAYER, PERM_B); + + // Default: Player gets ROLE_PLAYER, Moderator gets PERM_MOD_1 + sAccountMgr->AddDefaultPermissionForTest(SEC_PLAYER, ROLE_PLAYER); + sAccountMgr->AddDefaultPermissionForTest(SEC_MODERATOR, PERM_MOD_1); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACDefaultPermissionsWithRolesTest, DefaultRoleExpandsLinked) +{ + rbac::RBACData data(1, "Player", TEST_REALM_ID, SEC_PLAYER); + + // Simulate LoadFromDBCallback: grant defaults then recalculate + rbac::RBACPermissionContainer const& defaults = sAccountMgr->GetRBACDefaultPermissions(SEC_PLAYER); + for (uint32 perm : defaults) + data.GrantPermission(perm); + data.RecalculatePermissions(); + + EXPECT_TRUE(data.HasPermission(ROLE_PLAYER)); + EXPECT_TRUE(data.HasPermission(PERM_A)); + EXPECT_TRUE(data.HasPermission(PERM_B)); +} + +TEST_F(RBACDefaultPermissionsWithRolesTest, DefaultRole_DenyLinkedChild) +{ + rbac::RBACData data(1, "Player", TEST_REALM_ID, SEC_PLAYER); + + rbac::RBACPermissionContainer const& defaults = sAccountMgr->GetRBACDefaultPermissions(SEC_PLAYER); + for (uint32 perm : defaults) + data.GrantPermission(perm); + + data.DenyPermission(PERM_B); + data.RecalculatePermissions(); + + EXPECT_TRUE(data.HasPermission(ROLE_PLAYER)); + EXPECT_TRUE(data.HasPermission(PERM_A)); + EXPECT_FALSE(data.HasPermission(PERM_B)); +} + +TEST_F(RBACDefaultPermissionsWithRolesTest, SecLevels_NoCrossContamination) +{ + rbac::RBACData playerData(1, "Player", TEST_REALM_ID, SEC_PLAYER); + rbac::RBACData modData(2, "Mod", TEST_REALM_ID, SEC_MODERATOR); + + // Grant defaults for each + for (uint32 p : sAccountMgr->GetRBACDefaultPermissions(SEC_PLAYER)) + playerData.GrantPermission(p); + playerData.RecalculatePermissions(); + + for (uint32 p : sAccountMgr->GetRBACDefaultPermissions(SEC_MODERATOR)) + modData.GrantPermission(p); + modData.RecalculatePermissions(); + + // Player has player perms, not mod perms + EXPECT_TRUE(playerData.HasPermission(PERM_A)); + EXPECT_FALSE(playerData.HasPermission(PERM_MOD_1)); + + // Mod has mod perms, not player perms (unless explicitly granted) + EXPECT_TRUE(modData.HasPermission(PERM_MOD_1)); + EXPECT_FALSE(modData.HasPermission(PERM_A)); +} + +// --------------------------------------------------------------------------- +// Suite 7: RBACPermissionEnumConsistencyTest +// --------------------------------------------------------------------------- +class RBACPermissionEnumConsistencyTest : public ::testing::Test +{ +protected: + void SetUp() override + { + sAccountMgr->AddPermissionForTest(500, "Perm 500"); + sAccountMgr->AddPermissionForTest(700, "Perm 700"); + sAccountMgr->AddPermissionForTest(900, "Perm 900"); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACPermissionEnumConsistencyTest, MaxGreaterThanHighestDefined) +{ + EXPECT_GT(static_cast(rbac::RBAC_PERM_MAX), 911u); +} + +TEST_F(RBACPermissionEnumConsistencyTest, RoleIdsWithinRange) +{ + EXPECT_LT(static_cast(rbac::RBAC_ROLE_ADMINISTRATOR), static_cast(rbac::RBAC_PERM_MAX)); + EXPECT_LT(static_cast(rbac::RBAC_ROLE_GAMEMASTER), static_cast(rbac::RBAC_PERM_MAX)); + EXPECT_LT(static_cast(rbac::RBAC_ROLE_MODERATOR), static_cast(rbac::RBAC_PERM_MAX)); + EXPECT_LT(static_cast(rbac::RBAC_ROLE_PLAYER), static_cast(rbac::RBAC_PERM_MAX)); +} + +TEST_F(RBACPermissionEnumConsistencyTest, NonContiguousIds_Work) +{ + rbac::RBACData data(1, "Test", TEST_REALM_ID, 0); + + data.GrantPermission(500); + data.GrantPermission(900); + data.DenyPermission(700); + data.RecalculatePermissions(); + + EXPECT_TRUE(data.HasPermission(500)); + EXPECT_TRUE(data.HasPermission(900)); + EXPECT_FALSE(data.HasPermission(700)); +} + +// --------------------------------------------------------------------------- +// Suite 8: RBACEmptyAndBoundaryTest +// --------------------------------------------------------------------------- +class RBACEmptyAndBoundaryTest : public ::testing::Test +{ +protected: + void SetUp() override + { + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Permission 1"); + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACEmptyAndBoundaryTest, FreshData_AllEmpty) +{ + EXPECT_TRUE(rbacData->GetGrantedPermissions().empty()); + EXPECT_TRUE(rbacData->GetDeniedPermissions().empty()); + EXPECT_TRUE(rbacData->GetPermissions().empty()); +} + +TEST_F(RBACEmptyAndBoundaryTest, HasPermission_Zero_ReturnsFalse) +{ + EXPECT_FALSE(rbacData->HasPermission(0)); +} + +TEST_F(RBACEmptyAndBoundaryTest, HasPermission_Max_ReturnsFalse) +{ + EXPECT_FALSE(rbacData->HasPermission(rbac::RBAC_PERM_MAX)); +} + +TEST_F(RBACEmptyAndBoundaryTest, GrantDenyRevokeSequence) +{ + rbac::RBACCommandResult r1 = rbacData->GrantPermission(TEST_PERM_1); + EXPECT_EQ(r1, rbac::RBAC_OK); + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(TEST_PERM_1)); + + rbac::RBACCommandResult r2 = rbacData->RevokePermission(TEST_PERM_1); + EXPECT_EQ(r2, rbac::RBAC_OK); + + rbac::RBACCommandResult r3 = rbacData->DenyPermission(TEST_PERM_1); + EXPECT_EQ(r3, rbac::RBAC_OK); + rbacData->RecalculatePermissions(); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); + + rbac::RBACCommandResult r4 = rbacData->RevokePermission(TEST_PERM_1); + EXPECT_EQ(r4, rbac::RBAC_OK); + rbacData->RecalculatePermissions(); + EXPECT_FALSE(rbacData->HasPermission(TEST_PERM_1)); +} + +TEST_F(RBACEmptyAndBoundaryTest, MultipleRecalculates_Idempotent) +{ + rbacData->GrantPermission(TEST_PERM_1); + rbacData->RecalculatePermissions(); + auto perms1 = rbacData->GetPermissions(); + + rbacData->RecalculatePermissions(); + auto perms2 = rbacData->GetPermissions(); + + rbacData->RecalculatePermissions(); + auto perms3 = rbacData->GetPermissions(); + + EXPECT_EQ(perms1, perms2); + EXPECT_EQ(perms2, perms3); +} + +TEST_F(RBACEmptyAndBoundaryTest, RecalculateOnEmpty_Safe) +{ + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->GetPermissions().empty()); + + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->GetPermissions().empty()); +} + +// --------------------------------------------------------------------------- +// Suite 9: RBACCircularLinkProtectionTest +// --------------------------------------------------------------------------- +class RBACCircularLinkProtectionTest : public ::testing::Test +{ +protected: + static constexpr uint32 PERM_A = 10; + static constexpr uint32 PERM_B = 11; + static constexpr uint32 PERM_C = 12; + + void SetUp() override + { + sAccountMgr->AddPermissionForTest(PERM_A, "A"); + sAccountMgr->AddPermissionForTest(PERM_B, "B"); + sAccountMgr->AddPermissionForTest(PERM_C, "C"); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACCircularLinkProtectionTest, MutualLink_BothGranted) +{ + sAccountMgr->AddLinkedPermissionForTest(PERM_A, PERM_B); + sAccountMgr->AddLinkedPermissionForTest(PERM_B, PERM_A); + + rbac::RBACData data(1, "Test", TEST_REALM_ID, 0); + data.GrantPermission(PERM_A); + data.RecalculatePermissions(); + + EXPECT_TRUE(data.HasPermission(PERM_A)); + EXPECT_TRUE(data.HasPermission(PERM_B)); +} + +TEST_F(RBACCircularLinkProtectionTest, SelfReferencing_Terminates) +{ + sAccountMgr->AddLinkedPermissionForTest(PERM_A, PERM_A); + + rbac::RBACData data(1, "Test", TEST_REALM_ID, 0); + data.GrantPermission(PERM_A); + data.RecalculatePermissions(); + + EXPECT_TRUE(data.HasPermission(PERM_A)); + EXPECT_EQ(data.GetPermissions().size(), 1u); +} + +TEST_F(RBACCircularLinkProtectionTest, CircularChainOf3_AllGranted) +{ + sAccountMgr->AddLinkedPermissionForTest(PERM_A, PERM_B); + sAccountMgr->AddLinkedPermissionForTest(PERM_B, PERM_C); + sAccountMgr->AddLinkedPermissionForTest(PERM_C, PERM_A); + + rbac::RBACData data(1, "Test", TEST_REALM_ID, 0); + data.GrantPermission(PERM_A); + data.RecalculatePermissions(); + + EXPECT_TRUE(data.HasPermission(PERM_A)); + EXPECT_TRUE(data.HasPermission(PERM_B)); + EXPECT_TRUE(data.HasPermission(PERM_C)); + EXPECT_EQ(data.GetPermissions().size(), 3u); +} + +// --------------------------------------------------------------------------- +// Suite 10: RBACFullRoleHierarchyTest (mirrors actual 192-199 structure) +// --------------------------------------------------------------------------- +class RBACFullRoleHierarchyTest : public ::testing::Test +{ +protected: + // Roles + static constexpr uint32 ROLE_PLAYER = 199; + static constexpr uint32 ROLE_MOD = 198; + static constexpr uint32 ROLE_GM = 197; + static constexpr uint32 ROLE_ADMIN = 196; + + // Individual perms + static constexpr uint32 PERM_PLAYER_1 = 10; + static constexpr uint32 PERM_PLAYER_2 = 11; + static constexpr uint32 PERM_MOD_1 = 20; + static constexpr uint32 PERM_MOD_2 = 21; + static constexpr uint32 PERM_GM_1 = 30; + static constexpr uint32 PERM_GM_2 = 31; + static constexpr uint32 PERM_ADMIN_1 = 40; + static constexpr uint32 PERM_ADMIN_2 = 41; + + void SetUp() override + { + // Register individual perms + sAccountMgr->AddPermissionForTest(PERM_PLAYER_1, "Player 1"); + sAccountMgr->AddPermissionForTest(PERM_PLAYER_2, "Player 2"); + sAccountMgr->AddPermissionForTest(PERM_MOD_1, "Mod 1"); + sAccountMgr->AddPermissionForTest(PERM_MOD_2, "Mod 2"); + sAccountMgr->AddPermissionForTest(PERM_GM_1, "GM 1"); + sAccountMgr->AddPermissionForTest(PERM_GM_2, "GM 2"); + sAccountMgr->AddPermissionForTest(PERM_ADMIN_1, "Admin 1"); + sAccountMgr->AddPermissionForTest(PERM_ADMIN_2, "Admin 2"); + + // Register roles + sAccountMgr->AddPermissionForTest(ROLE_PLAYER, "Player Role"); + sAccountMgr->AddPermissionForTest(ROLE_MOD, "Moderator Role"); + sAccountMgr->AddPermissionForTest(ROLE_GM, "GameMaster Role"); + sAccountMgr->AddPermissionForTest(ROLE_ADMIN, "Administrator Role"); + + // Player(199) -> basic perms + sAccountMgr->AddLinkedPermissionForTest(ROLE_PLAYER, PERM_PLAYER_1); + sAccountMgr->AddLinkedPermissionForTest(ROLE_PLAYER, PERM_PLAYER_2); + + // Mod(198) -> Player + mod perms + sAccountMgr->AddLinkedPermissionForTest(ROLE_MOD, ROLE_PLAYER); + sAccountMgr->AddLinkedPermissionForTest(ROLE_MOD, PERM_MOD_1); + sAccountMgr->AddLinkedPermissionForTest(ROLE_MOD, PERM_MOD_2); + + // GM(197) -> Mod + gm perms + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, ROLE_MOD); + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, PERM_GM_1); + sAccountMgr->AddLinkedPermissionForTest(ROLE_GM, PERM_GM_2); + + // Admin(196) -> GM + admin perms + sAccountMgr->AddLinkedPermissionForTest(ROLE_ADMIN, ROLE_GM); + sAccountMgr->AddLinkedPermissionForTest(ROLE_ADMIN, PERM_ADMIN_1); + sAccountMgr->AddLinkedPermissionForTest(ROLE_ADMIN, PERM_ADMIN_2); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACFullRoleHierarchyTest, GrantPlayerRole_OnlyPlayerPerms) +{ + rbac::RBACData data(1, "Player", TEST_REALM_ID, 0); + data.GrantPermission(ROLE_PLAYER); + data.RecalculatePermissions(); + + EXPECT_TRUE(data.HasPermission(ROLE_PLAYER)); + EXPECT_TRUE(data.HasPermission(PERM_PLAYER_1)); + EXPECT_TRUE(data.HasPermission(PERM_PLAYER_2)); + + EXPECT_FALSE(data.HasPermission(PERM_MOD_1)); + EXPECT_FALSE(data.HasPermission(PERM_GM_1)); + EXPECT_FALSE(data.HasPermission(PERM_ADMIN_1)); + // 3 total: ROLE_PLAYER + 2 player perms + EXPECT_EQ(data.GetPermissions().size(), 3u); +} + +TEST_F(RBACFullRoleHierarchyTest, GrantGMRole_FullChain) +{ + rbac::RBACData data(1, "GM", TEST_REALM_ID, 0); + data.GrantPermission(ROLE_GM); + data.RecalculatePermissions(); + + // GM + Mod + Player perms + EXPECT_TRUE(data.HasPermission(ROLE_GM)); + EXPECT_TRUE(data.HasPermission(ROLE_MOD)); + EXPECT_TRUE(data.HasPermission(ROLE_PLAYER)); + EXPECT_TRUE(data.HasPermission(PERM_PLAYER_1)); + EXPECT_TRUE(data.HasPermission(PERM_PLAYER_2)); + EXPECT_TRUE(data.HasPermission(PERM_MOD_1)); + EXPECT_TRUE(data.HasPermission(PERM_MOD_2)); + EXPECT_TRUE(data.HasPermission(PERM_GM_1)); + EXPECT_TRUE(data.HasPermission(PERM_GM_2)); + + EXPECT_FALSE(data.HasPermission(PERM_ADMIN_1)); + EXPECT_FALSE(data.HasPermission(PERM_ADMIN_2)); + // 9 total: 3 roles + 6 perms + EXPECT_EQ(data.GetPermissions().size(), 9u); +} + +TEST_F(RBACFullRoleHierarchyTest, GrantAdminRole_AllPerms) +{ + rbac::RBACData data(1, "Admin", TEST_REALM_ID, 0); + data.GrantPermission(ROLE_ADMIN); + data.RecalculatePermissions(); + + EXPECT_TRUE(data.HasPermission(ROLE_ADMIN)); + EXPECT_TRUE(data.HasPermission(ROLE_GM)); + EXPECT_TRUE(data.HasPermission(ROLE_MOD)); + EXPECT_TRUE(data.HasPermission(ROLE_PLAYER)); + EXPECT_TRUE(data.HasPermission(PERM_PLAYER_1)); + EXPECT_TRUE(data.HasPermission(PERM_PLAYER_2)); + EXPECT_TRUE(data.HasPermission(PERM_MOD_1)); + EXPECT_TRUE(data.HasPermission(PERM_MOD_2)); + EXPECT_TRUE(data.HasPermission(PERM_GM_1)); + EXPECT_TRUE(data.HasPermission(PERM_GM_2)); + EXPECT_TRUE(data.HasPermission(PERM_ADMIN_1)); + EXPECT_TRUE(data.HasPermission(PERM_ADMIN_2)); + // 12 total: 4 roles + 8 perms + EXPECT_EQ(data.GetPermissions().size(), 12u); +} + +TEST_F(RBACFullRoleHierarchyTest, GrantAdmin_DenyModRole_ModAndPlayerDenied) +{ + rbac::RBACData data(1, "Admin", TEST_REALM_ID, 0); + data.GrantPermission(ROLE_ADMIN); + data.DenyPermission(ROLE_MOD); + data.RecalculatePermissions(); + + // Admin + GM perms remain + EXPECT_TRUE(data.HasPermission(ROLE_ADMIN)); + EXPECT_TRUE(data.HasPermission(ROLE_GM)); + EXPECT_TRUE(data.HasPermission(PERM_GM_1)); + EXPECT_TRUE(data.HasPermission(PERM_GM_2)); + EXPECT_TRUE(data.HasPermission(PERM_ADMIN_1)); + EXPECT_TRUE(data.HasPermission(PERM_ADMIN_2)); + + // Mod role and its full chain denied (Mod + Player perms) + EXPECT_FALSE(data.HasPermission(ROLE_MOD)); + EXPECT_FALSE(data.HasPermission(ROLE_PLAYER)); + EXPECT_FALSE(data.HasPermission(PERM_MOD_1)); + EXPECT_FALSE(data.HasPermission(PERM_MOD_2)); + EXPECT_FALSE(data.HasPermission(PERM_PLAYER_1)); + EXPECT_FALSE(data.HasPermission(PERM_PLAYER_2)); +} + +TEST_F(RBACFullRoleHierarchyTest, GrantAdmin_DenyOneCommandPerm) +{ + rbac::RBACData data(1, "Admin", TEST_REALM_ID, 0); + data.GrantPermission(ROLE_ADMIN); + data.DenyPermission(PERM_GM_1); + data.RecalculatePermissions(); + + // Only PERM_GM_1 is denied + EXPECT_FALSE(data.HasPermission(PERM_GM_1)); + + // Everything else still works + EXPECT_TRUE(data.HasPermission(ROLE_ADMIN)); + EXPECT_TRUE(data.HasPermission(ROLE_GM)); + EXPECT_TRUE(data.HasPermission(ROLE_MOD)); + EXPECT_TRUE(data.HasPermission(ROLE_PLAYER)); + EXPECT_TRUE(data.HasPermission(PERM_GM_2)); + EXPECT_TRUE(data.HasPermission(PERM_ADMIN_1)); + EXPECT_TRUE(data.HasPermission(PERM_ADMIN_2)); + EXPECT_TRUE(data.HasPermission(PERM_MOD_1)); + EXPECT_TRUE(data.HasPermission(PERM_PLAYER_1)); + // 11 total: 12 - 1 denied + EXPECT_EQ(data.GetPermissions().size(), 11u); +} + +// --------------------------------------------------------------------------- +// Suite 11: RBACPermissionDefaultConstructorTest +// --------------------------------------------------------------------------- +class RBACPermissionDefaultConstructorTest : public ::testing::Test +{ +}; + +TEST_F(RBACPermissionDefaultConstructorTest, DefaultConstructor_ZeroIdEmptyName) +{ + rbac::RBACPermission perm; + EXPECT_EQ(perm.GetId(), 0u); + EXPECT_EQ(perm.GetName(), ""); + EXPECT_TRUE(perm.GetLinkedPermissions().empty()); +} + +TEST_F(RBACPermissionDefaultConstructorTest, ConstructorWithIdOnly) +{ + rbac::RBACPermission perm(42); + EXPECT_EQ(perm.GetId(), 42u); + EXPECT_EQ(perm.GetName(), ""); + EXPECT_TRUE(perm.GetLinkedPermissions().empty()); +} + +// --------------------------------------------------------------------------- +// Suite 12: RBACAccountMgrPermissionLookupTest +// --------------------------------------------------------------------------- +class RBACAccountMgrPermissionLookupTest : public ::testing::Test +{ +protected: + void SetUp() override + { + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Permission One"); + sAccountMgr->AddPermissionForTest(TEST_PERM_2, "Permission Two"); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACAccountMgrPermissionLookupTest, GetRBACPermission_ValidId_ReturnsPermission) +{ + rbac::RBACPermission const* perm = sAccountMgr->GetRBACPermission(TEST_PERM_1); + ASSERT_NE(perm, nullptr); + EXPECT_EQ(perm->GetId(), TEST_PERM_1); + EXPECT_EQ(perm->GetName(), "Permission One"); +} + +TEST_F(RBACAccountMgrPermissionLookupTest, GetRBACPermission_InvalidId_ReturnsNull) +{ + rbac::RBACPermission const* perm = sAccountMgr->GetRBACPermission(TEST_PERM_INVALID); + EXPECT_EQ(perm, nullptr); +} + +TEST_F(RBACAccountMgrPermissionLookupTest, GetRBACPermissionList_ContainsRegistered) +{ + rbac::RBACPermissionsContainer const& list = sAccountMgr->GetRBACPermissionList(); + EXPECT_TRUE(list.find(TEST_PERM_1) != list.end()); + EXPECT_TRUE(list.find(TEST_PERM_2) != list.end()); + EXPECT_TRUE(list.find(TEST_PERM_INVALID) == list.end()); +} + +TEST_F(RBACAccountMgrPermissionLookupTest, GetRBACPermissionList_SizeMatchesRegistered) +{ + rbac::RBACPermissionsContainer const& list = sAccountMgr->GetRBACPermissionList(); + EXPECT_EQ(list.size(), 2u); +} + +// --------------------------------------------------------------------------- +// Suite 13: RBACAccountMgrTestHelperEdgeCasesTest +// --------------------------------------------------------------------------- +class RBACAccountMgrTestHelperEdgeCasesTest : public ::testing::Test +{ +protected: + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACAccountMgrTestHelperEdgeCasesTest, AddPermissionForTest_DuplicateIsNoOp) +{ + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Original Name"); + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Different Name"); + + rbac::RBACPermission const* perm = sAccountMgr->GetRBACPermission(TEST_PERM_1); + ASSERT_NE(perm, nullptr); + EXPECT_EQ(perm->GetName(), "Original Name"); +} + +TEST_F(RBACAccountMgrTestHelperEdgeCasesTest, AddLinkedPermission_UnregisteredParent_NoOp) +{ + // Link to non-existent parent: should silently do nothing + sAccountMgr->AddLinkedPermissionForTest(TEST_PERM_INVALID, TEST_PERM_1); + EXPECT_EQ(sAccountMgr->GetRBACPermission(TEST_PERM_INVALID), nullptr); +} + +TEST_F(RBACAccountMgrTestHelperEdgeCasesTest, ClearPermissions_RemovesEverything) +{ + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "One"); + sAccountMgr->AddPermissionForTest(TEST_PERM_2, "Two"); + sAccountMgr->AddDefaultPermissionForTest(0, TEST_PERM_1); + + sAccountMgr->ClearPermissionsForTest(); + + EXPECT_EQ(sAccountMgr->GetRBACPermission(TEST_PERM_1), nullptr); + EXPECT_EQ(sAccountMgr->GetRBACPermission(TEST_PERM_2), nullptr); + EXPECT_TRUE(sAccountMgr->GetRBACPermissionList().empty()); + EXPECT_TRUE(sAccountMgr->GetRBACDefaultPermissions(0).empty()); +} + +// --------------------------------------------------------------------------- +// Suite 14: RBACExpandPermissionsDanglingLinkTest +// --------------------------------------------------------------------------- +class RBACExpandPermissionsDanglingLinkTest : public ::testing::Test +{ +protected: + static constexpr uint32 PERM_VALID = 10; + static constexpr uint32 PERM_DANGLING = 9999; + static constexpr uint32 ROLE_WITH_DANGLING = 100; + + void SetUp() override + { + sAccountMgr->AddPermissionForTest(PERM_VALID, "Valid"); + sAccountMgr->AddPermissionForTest(ROLE_WITH_DANGLING, "Role With Dangling"); + + // Role links to valid perm AND an unregistered perm ID + sAccountMgr->AddLinkedPermissionForTest(ROLE_WITH_DANGLING, PERM_VALID); + sAccountMgr->AddLinkedPermissionForTest(ROLE_WITH_DANGLING, PERM_DANGLING); + + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACExpandPermissionsDanglingLinkTest, DanglingLink_SkippedGracefully) +{ + rbacData->GrantPermission(ROLE_WITH_DANGLING); + rbacData->RecalculatePermissions(); + + // Valid perm expanded, dangling silently skipped + EXPECT_TRUE(rbacData->HasPermission(ROLE_WITH_DANGLING)); + EXPECT_TRUE(rbacData->HasPermission(PERM_VALID)); + EXPECT_FALSE(rbacData->HasPermission(PERM_DANGLING)); + // Only 2: the role + the valid perm (dangling not included) + EXPECT_EQ(rbacData->GetPermissions().size(), 2u); +} + +TEST_F(RBACExpandPermissionsDanglingLinkTest, DenyDanglingLink_SkippedGracefully) +{ + rbacData->GrantPermission(PERM_VALID); + // PERM_DANGLING can't be denied since it's not registered + rbac::RBACCommandResult result = rbacData->DenyPermission(PERM_DANGLING); + EXPECT_EQ(result, rbac::RBAC_ID_DOES_NOT_EXISTS); + + rbacData->RecalculatePermissions(); + EXPECT_TRUE(rbacData->HasPermission(PERM_VALID)); +} + +// --------------------------------------------------------------------------- +// Suite 15: RBACLoadFromDBCallbackSimulationTest +// --------------------------------------------------------------------------- +class RBACLoadFromDBCallbackSimulationTest : public ::testing::Test +{ +protected: + static constexpr uint32 PERM_A = 10; + static constexpr uint32 PERM_B = 11; + static constexpr uint32 ROLE_PLAYER = 100; + + static constexpr uint8 SEC_PLAYER = 0; + + void SetUp() override + { + sAccountMgr->AddPermissionForTest(PERM_A, "A"); + sAccountMgr->AddPermissionForTest(PERM_B, "B"); + sAccountMgr->AddPermissionForTest(ROLE_PLAYER, "Player Role"); + + sAccountMgr->AddLinkedPermissionForTest(ROLE_PLAYER, PERM_A); + sAccountMgr->AddLinkedPermissionForTest(ROLE_PLAYER, PERM_B); + + sAccountMgr->AddDefaultPermissionForTest(SEC_PLAYER, ROLE_PLAYER); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACLoadFromDBCallbackSimulationTest, NullResult_AppliesDefaultsAndRecalculates) +{ + rbac::RBACData data(1, "Player", TEST_REALM_ID, SEC_PLAYER); + + // Simulate LoadFromDBCallback with no DB rows (nullptr result) + data.LoadFromDBCallback(nullptr); + + // Default permission (ROLE_PLAYER) should be granted and expanded + EXPECT_TRUE(data.HasPermission(ROLE_PLAYER)); + EXPECT_TRUE(data.HasPermission(PERM_A)); + EXPECT_TRUE(data.HasPermission(PERM_B)); + EXPECT_EQ(data.GetPermissions().size(), 3u); +} + +TEST_F(RBACLoadFromDBCallbackSimulationTest, NullResult_NoDefaultsForHighSecLevel) +{ + // Sec level 3 has no defaults configured + rbac::RBACData data(1, "Admin", TEST_REALM_ID, 3); + data.LoadFromDBCallback(nullptr); + + EXPECT_TRUE(data.GetPermissions().empty()); +} + +// --------------------------------------------------------------------------- +// Suite 16: RBACDoubleRevokeTest +// --------------------------------------------------------------------------- +class RBACDoubleRevokeTest : public ::testing::Test +{ +protected: + void SetUp() override + { + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Permission 1"); + rbacData = std::make_unique(TEST_ACCOUNT_ID, TEST_ACCOUNT_NAME, TEST_REALM_ID, TEST_SEC_LEVEL); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } + + std::unique_ptr rbacData; +}; + +TEST_F(RBACDoubleRevokeTest, RevokeGrantedTwice_SecondFails) +{ + rbacData->GrantPermission(TEST_PERM_1); + EXPECT_EQ(rbacData->RevokePermission(TEST_PERM_1), rbac::RBAC_OK); + EXPECT_EQ(rbacData->RevokePermission(TEST_PERM_1), rbac::RBAC_CANT_REVOKE_NOT_IN_LIST); +} + +TEST_F(RBACDoubleRevokeTest, RevokeDeniedTwice_SecondFails) +{ + rbacData->DenyPermission(TEST_PERM_1); + EXPECT_EQ(rbacData->RevokePermission(TEST_PERM_1), rbac::RBAC_OK); + EXPECT_EQ(rbacData->RevokePermission(TEST_PERM_1), rbac::RBAC_CANT_REVOKE_NOT_IN_LIST); +} + +// --------------------------------------------------------------------------- +// Suite 17: RBACMultipleInstancesIndependenceTest +// --------------------------------------------------------------------------- +class RBACMultipleInstancesIndependenceTest : public ::testing::Test +{ +protected: + void SetUp() override + { + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Permission 1"); + sAccountMgr->AddPermissionForTest(TEST_PERM_2, "Permission 2"); + sAccountMgr->AddPermissionForTest(TEST_PERM_3, "Permission 3"); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACMultipleInstancesIndependenceTest, TwoInstances_NoCrossContamination) +{ + rbac::RBACData data1(1, "Account1", TEST_REALM_ID, 0); + rbac::RBACData data2(2, "Account2", TEST_REALM_ID, 0); + + data1.GrantPermission(TEST_PERM_1); + data2.GrantPermission(TEST_PERM_2); + + data1.RecalculatePermissions(); + data2.RecalculatePermissions(); + + EXPECT_TRUE(data1.HasPermission(TEST_PERM_1)); + EXPECT_FALSE(data1.HasPermission(TEST_PERM_2)); + + EXPECT_FALSE(data2.HasPermission(TEST_PERM_1)); + EXPECT_TRUE(data2.HasPermission(TEST_PERM_2)); +} + +TEST_F(RBACMultipleInstancesIndependenceTest, ModifyingOne_DoesNotAffectOther) +{ + rbac::RBACData data1(1, "Account1", TEST_REALM_ID, 0); + rbac::RBACData data2(2, "Account2", TEST_REALM_ID, 0); + + data1.GrantPermission(TEST_PERM_1); + data1.GrantPermission(TEST_PERM_2); + data2.GrantPermission(TEST_PERM_1); + + data1.RecalculatePermissions(); + data2.RecalculatePermissions(); + + // Revoke from data1, data2 unaffected + data1.RevokePermission(TEST_PERM_1); + data1.RecalculatePermissions(); + + EXPECT_FALSE(data1.HasPermission(TEST_PERM_1)); + EXPECT_TRUE(data2.HasPermission(TEST_PERM_1)); +} + +// --------------------------------------------------------------------------- +// Suite 18: RBACSetSecurityLevelForTestBehaviorTest +// --------------------------------------------------------------------------- +class RBACSetSecurityLevelForTestBehaviorTest : public ::testing::Test +{ +protected: + void SetUp() override + { + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Permission 1"); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACSetSecurityLevelForTestBehaviorTest, DoesNotRecalculate) +{ + rbac::RBACData data(1, "Test", TEST_REALM_ID, 0); + data.GrantPermission(TEST_PERM_1); + data.RecalculatePermissions(); + EXPECT_TRUE(data.HasPermission(TEST_PERM_1)); + + // Changing sec level via test helper should NOT clear/recalculate perms + data.SetSecurityLevelForTest(3); + EXPECT_EQ(data.GetSecurityLevel(), 3); + EXPECT_TRUE(data.HasPermission(TEST_PERM_1)); +} + +TEST_F(RBACSetSecurityLevelForTestBehaviorTest, UpdatesLevelOnly) +{ + rbac::RBACData data(1, "Test", TEST_REALM_ID, 0); + EXPECT_EQ(data.GetSecurityLevel(), 0); + + data.SetSecurityLevelForTest(1); + EXPECT_EQ(data.GetSecurityLevel(), 1); + + data.SetSecurityLevelForTest(255); + EXPECT_EQ(data.GetSecurityLevel(), 255); +} + +// --------------------------------------------------------------------------- +// Suite 19: RBACDataAccessorsTest +// --------------------------------------------------------------------------- +class RBACDataAccessorsTest : public ::testing::Test +{ +protected: + void SetUp() override + { + sAccountMgr->AddPermissionForTest(TEST_PERM_1, "Permission 1"); + } + + void TearDown() override + { + sAccountMgr->ClearPermissionsForTest(); + } +}; + +TEST_F(RBACDataAccessorsTest, GetName_ReturnsAccountName) +{ + rbac::RBACData data(42, "MyAccount", TEST_REALM_ID, 0); + EXPECT_EQ(data.GetName(), "MyAccount"); +} + +TEST_F(RBACDataAccessorsTest, GetName_EmptyString) +{ + rbac::RBACData data(1, "", TEST_REALM_ID, 0); + EXPECT_EQ(data.GetName(), ""); +} + +TEST_F(RBACDataAccessorsTest, GetId_ReturnsAccountId) +{ + rbac::RBACData data(42, "Test", TEST_REALM_ID, 0); + EXPECT_EQ(data.GetId(), 42u); +} + +TEST_F(RBACDataAccessorsTest, GrantedDeniedGlobal_AllConsistent) +{ + rbac::RBACData data(1, "Test", TEST_REALM_ID, 0); + + data.GrantPermission(TEST_PERM_1); + EXPECT_EQ(data.GetGrantedPermissions().size(), 1u); + EXPECT_TRUE(data.GetDeniedPermissions().empty()); + + // Before recalculate, global perms are still empty + EXPECT_TRUE(data.GetPermissions().empty()); + + data.RecalculatePermissions(); + // After recalculate, global perms reflect granted + EXPECT_EQ(data.GetPermissions().size(), 1u); + EXPECT_TRUE(data.HasPermission(TEST_PERM_1)); +} + +} // namespace diff --git a/src/test/server/game/Commands/GmVisibleCommandTest.cpp b/src/test/server/game/Commands/GmVisibleCommandTest.cpp index 67a7e9a2f..f916b079e 100644 --- a/src/test/server/game/Commands/GmVisibleCommandTest.cpp +++ b/src/test/server/game/Commands/GmVisibleCommandTest.cpp @@ -29,11 +29,6 @@ #include "gmock/gmock.h" #include "gtest/gtest.h" #include -#include - -#ifndef TEST_F -#define TEST_F(fixture, name) void fixture##_##name() -#endif using namespace testing; @@ -111,6 +106,7 @@ protected: session = new WorldSession(1, "gm", 0, nullptr, SEC_GAMEMASTER, EXPANSION_WRATH_OF_THE_LICH_KING, 0, LOCALE_enUS, 0, false, false, 0); + session->InitRBACDataForTest(); player = new TestPlayer(session); player->ForceInitValues(); @@ -133,20 +129,14 @@ protected: player = nullptr; } - void ExecuteCommand(std::string_view text) + void SimulateGmVisibleOff() { - if (text == ".gm visible off") - { - ApplyGmVisibleState(false); - } - else if (text == ".gm visible on") - { - ApplyGmVisibleState(true); - } - else - { - FAIL() << "Unsupported test command: " << text; - } + player->SetServerSideVisibility(SERVERSIDE_VISIBILITY_GM, session->GetSecurity()); + } + + void SimulateGmVisibleOn() + { + player->SetServerSideVisibility(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER); } static void EnsureScriptRegistriesInitialized() @@ -167,52 +157,31 @@ protected: NiceMock* worldMock = nullptr; WorldSession* session = nullptr; TestPlayer* player = nullptr; - -private: - void ApplyGmVisibleState(bool makeVisible) - { - constexpr uint32 VISUAL_AURA = 37800; - - if (makeVisible) - { - player->RemoveAurasDueToSpell(VISUAL_AURA); - player->SetGMVisible(true); - } - else - { - player->AddAura(VISUAL_AURA, player); - player->SetGMVisible(false); - } - - player->UpdateObjectVisibility(); - } }; +// cppcheck-suppress syntaxError TEST_F(GmVisibleCommandTest, SetsPlayerInvisibleAndInvokesHook) { - ExecuteCommand(".gm visible off"); + SimulateGmVisibleOff(); EXPECT_EQ(TestVisibilityScript::CallCount, 1u); EXPECT_EQ(TestVisibilityScript::LastPlayer, player); EXPECT_EQ(TestVisibilityScript::LastType, SERVERSIDE_VISIBILITY_GM); EXPECT_EQ(TestVisibilityScript::LastSecurity, session->GetSecurity()); EXPECT_EQ(player->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM), uint32(session->GetSecurity())); - EXPECT_FALSE(player->isGMVisible()); } TEST_F(GmVisibleCommandTest, SetsPlayerVisibleAndInvokesHook) { - // Ensure the player starts from invisible state to test the opposite transition as well. - ExecuteCommand(".gm visible off"); + SimulateGmVisibleOff(); TestVisibilityScript::Reset(); - ExecuteCommand(".gm visible on"); + SimulateGmVisibleOn(); EXPECT_EQ(TestVisibilityScript::CallCount, 1u); EXPECT_EQ(TestVisibilityScript::LastPlayer, player); EXPECT_EQ(TestVisibilityScript::LastType, SERVERSIDE_VISIBILITY_GM); EXPECT_EQ(TestVisibilityScript::LastSecurity, SEC_PLAYER); EXPECT_EQ(player->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM), uint32(SEC_PLAYER)); - EXPECT_TRUE(player->isGMVisible()); } } From 866bcc1b6288171ae7bf2b1cad3d9261a61b41e2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 May 2026 12:20:42 +0000 Subject: [PATCH 73/92] chore(DB): import pending files Referenced commit(s): 779331fc4b1d35577c34923c90459764f5834c04 --- .../rev_1770173499134350791.sql => db_auth/2026_05_01_00.sql} | 1 + .../rev_1770173911371830386.sql => db_world/2026_05_01_00.sql} | 1 + .../rev_1777553106653644140.sql => db_world/2026_05_01_01.sql} | 1 + 3 files changed, 3 insertions(+) rename data/sql/updates/{pending_db_auth/rev_1770173499134350791.sql => db_auth/2026_05_01_00.sql} (99%) rename data/sql/updates/{pending_db_world/rev_1770173911371830386.sql => db_world/2026_05_01_00.sql} (96%) rename data/sql/updates/{pending_db_world/rev_1777553106653644140.sql => db_world/2026_05_01_01.sql} (95%) diff --git a/data/sql/updates/pending_db_auth/rev_1770173499134350791.sql b/data/sql/updates/db_auth/2026_05_01_00.sql similarity index 99% rename from data/sql/updates/pending_db_auth/rev_1770173499134350791.sql rename to data/sql/updates/db_auth/2026_05_01_00.sql index 1f86ccdd8..0f271638d 100644 --- a/data/sql/updates/pending_db_auth/rev_1770173499134350791.sql +++ b/data/sql/updates/db_auth/2026_05_01_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_24_00 -> 2026_05_01_00 -- RBAC System for AzerothCore -- Ported from TrinityCore diff --git a/data/sql/updates/pending_db_world/rev_1770173911371830386.sql b/data/sql/updates/db_world/2026_05_01_00.sql similarity index 96% rename from data/sql/updates/pending_db_world/rev_1770173911371830386.sql rename to data/sql/updates/db_world/2026_05_01_00.sql index c91c489bc..ceab53ed7 100644 --- a/data/sql/updates/pending_db_world/rev_1770173911371830386.sql +++ b/data/sql/updates/db_world/2026_05_01_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_04_30_03 -> 2026_05_01_00 -- RBAC Language strings (uses {} format placeholders for Acore::StringFormat) DELETE FROM `acore_string` WHERE `entry` BETWEEN 63 AND 79; INSERT INTO `acore_string` (`entry`, `content_default`) VALUES diff --git a/data/sql/updates/pending_db_world/rev_1777553106653644140.sql b/data/sql/updates/db_world/2026_05_01_01.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1777553106653644140.sql rename to data/sql/updates/db_world/2026_05_01_01.sql index b1bd2ea86..52d8e428d 100644 --- a/data/sql/updates/pending_db_world/rev_1777553106653644140.sql +++ b/data/sql/updates/db_world/2026_05_01_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_05_01_00 -> 2026_05_01_01 -- RBAC command help strings DELETE FROM `acore_string` WHERE `entry` BETWEEN 83 AND 86; INSERT INTO `acore_string` (`entry`, `content_default`) VALUES From de8b5e5726650ceb9da36846e1fd3e121b2bfc04 Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 1 May 2026 16:48:22 +0200 Subject: [PATCH 74/92] fix(Core/Pets): Mob solod by pet no longer grant exp/loot to its master (#25676) --- src/server/game/Entities/Unit/Unit.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 06dfe6734..ea82c2e28 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1229,7 +1229,7 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage { uint32 unDamage = health < damage ? health : damage; bool damagedByPlayer = unDamage && attacker && (attacker->IsPlayer() || attacker->m_movedByPlayer != nullptr - || attacker->GetCharmerOrOwnerGUID().IsPlayer()); + || attacker->GetCharmerGUID().IsPlayer()); victim->ToCreature()->LowerPlayerDamageReq(unDamage, damagedByPlayer); } } From 68a8f16709fb604a4411792d157109e2c9acbd17 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Fri, 1 May 2026 20:19:35 +0200 Subject: [PATCH 75/92] fix(DB/Script): Creatures in Chapel of the Crimson Flame are no longer visible until the player delivers the quest Bloody Breakout. (#25678) --- data/sql/updates/pending_db_world/rev_1777647860.sql | 9 +++++++++ src/server/game/Spells/SpellMgr.cpp | 1 + 2 files changed, 10 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777647860.sql diff --git a/data/sql/updates/pending_db_world/rev_1777647860.sql b/data/sql/updates/pending_db_world/rev_1777647860.sql new file mode 100644 index 000000000..dc7d9a645 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777647860.sql @@ -0,0 +1,9 @@ + +-- Set Aura for Scarlet Inquisitor (corpse) +UPDATE `creature_template_addon` SET `auras` = '29266 52951' WHERE `entry` = 29029; + +-- Set Aura for Death Knights and Dummy +UPDATE `creature_template_addon` SET `auras` = '52951' WHERE `entry` IN (29030, 29031, 29038); + +-- Set Aura for Plaguefist and Prisoners +UPDATE `creature_addon` SET `auras` = '52951' WHERE (`guid` IN (129947, 129905, 129950, 129955, 129959, 129961, 129964, 129965, 129966, 129967, 129968)); diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index af70ec147..011c8ec19 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -3157,6 +3157,7 @@ void SpellMgr::LoadSpellInfoCustomAttributes() // Exceptions case 44801: // Spectral Invisibility (Kalecgos, SWP) case 46021: // Spectral Realm (SWP) + case 52951: // Chapel Invisibility (DK starting zone) break; default: spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST; From 0905acb1c25cc02dc9b642e38a024b56e5b1022a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 May 2026 18:20:42 +0000 Subject: [PATCH 76/92] chore(DB): import pending files Referenced commit(s): 68a8f16709fb604a4411792d157109e2c9acbd17 --- .../rev_1777647860.sql => db_world/2026_05_01_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777647860.sql => db_world/2026_05_01_02.sql} (91%) diff --git a/data/sql/updates/pending_db_world/rev_1777647860.sql b/data/sql/updates/db_world/2026_05_01_02.sql similarity index 91% rename from data/sql/updates/pending_db_world/rev_1777647860.sql rename to data/sql/updates/db_world/2026_05_01_02.sql index dc7d9a645..c8e05a356 100644 --- a/data/sql/updates/pending_db_world/rev_1777647860.sql +++ b/data/sql/updates/db_world/2026_05_01_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_05_01_01 -> 2026_05_01_02 -- Set Aura for Scarlet Inquisitor (corpse) UPDATE `creature_template_addon` SET `auras` = '29266 52951' WHERE `entry` = 29029; From 89275a5c4a154d5440b41241a3eb4dae2390406c Mon Sep 17 00:00:00 2001 From: sogladev Date: Fri, 1 May 2026 22:10:50 +0200 Subject: [PATCH 77/92] fix(DB/SmartAI): Crypt Guard can no longer be pulled without Anub'Rekhan (#25677) Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../sql/updates/pending_db_world/rev_1777646567833919567.sql | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777646567833919567.sql diff --git a/data/sql/updates/pending_db_world/rev_1777646567833919567.sql b/data/sql/updates/pending_db_world/rev_1777646567833919567.sql new file mode 100644 index 000000000..2522eccf6 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777646567833919567.sql @@ -0,0 +1,5 @@ +-- +-- Add an explicit CallForHelp action with a 60-yard radius because CallForAssistance's default 10-yard range is too small. +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 16573) AND (`source_type` = 0) AND (`id` = 6); +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 +(16573, 0, 6, 0, 4, 0, 100, 0, 0, 0, 0, 0, 0, 0, 39, 60, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Crypt Guard - On Aggro - Call For Help'); From 64d4dfff24421326dc57b0e0c88df86cda220f1f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 May 2026 20:12:04 +0000 Subject: [PATCH 78/92] chore(DB): import pending files Referenced commit(s): 89275a5c4a154d5440b41241a3eb4dae2390406c --- .../rev_1777646567833919567.sql => db_world/2026_05_01_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777646567833919567.sql => db_world/2026_05_01_03.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1777646567833919567.sql b/data/sql/updates/db_world/2026_05_01_03.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1777646567833919567.sql rename to data/sql/updates/db_world/2026_05_01_03.sql index 2522eccf6..6ca527460 100644 --- a/data/sql/updates/pending_db_world/rev_1777646567833919567.sql +++ b/data/sql/updates/db_world/2026_05_01_03.sql @@ -1,3 +1,4 @@ +-- DB update 2026_05_01_02 -> 2026_05_01_03 -- -- Add an explicit CallForHelp action with a 60-yard radius because CallForAssistance's default 10-yard range is too small. DELETE FROM `smart_scripts` WHERE (`entryorguid` = 16573) AND (`source_type` = 0) AND (`id` = 6); From 0c5d5c448c3db15d3a1b50b359c13e782d9db664 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Fri, 1 May 2026 23:16:34 +0200 Subject: [PATCH 79/92] fix(DB/Auras): Taunka Soldiers, Greatmother Icemist and Roanauk Icemist are now invisible until their related quests are completed. (#25681) --- .../pending_db_world/rev_1777666032.sql | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777666032.sql diff --git a/data/sql/updates/pending_db_world/rev_1777666032.sql b/data/sql/updates/pending_db_world/rev_1777666032.sql new file mode 100644 index 000000000..b3a3948f6 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777666032.sql @@ -0,0 +1,27 @@ + +-- Set Quest Pre-requisite From the Depths of Azjol-Nerub for Strength of Icemist. +UPDATE `quest_template_addon` SET `PrevQuestID` = 12036 WHERE (`ID` = 12063); + +-- Delete wrong guid (Taunka Soldier) +DELETE FROM `creature` WHERE (`id1` = 26437) AND (`guid` IN (102363)); +DELETE FROM `creature_addon` WHERE (`guid` IN (102363)); + +-- Set Reward Spells +UPDATE `quest_template` SET `RewardSpell` = 46997 WHERE `ID` = 11978; +UPDATE `quest_template` SET `RewardSpell` = 47418 WHERE `ID` = 12069; + +DELETE FROM `spell_area` WHERE (`spell` IN (46997, 47418)); +INSERT INTO `spell_area` (`spell`, `area`, `quest_start`, `quest_end`, `aura_spell`, `racemask`, `gender`, `autocast`, `quest_start_status`, `quest_end_status`) VALUES +(46997, 4165, 11978, 0, 0, 0, 2, 1, 64, 0), +(47418, 4165, 12069, 0, 0, 0, 2, 1, 64, 0); + +-- Set Auras (Taunka Soldiers & Greatmother Icemist) +UPDATE `creature_addon` SET `auras` = '46996' WHERE (`guid` IN (102326, 102327, 102328, 102329, 102330, 102331, 102332, 102333, 102337, 102338, 102340, 102341, 98402)); + +-- Set Unit Flags for Roanauk Icemist (Sniffed) +UPDATE `creature_template` SET `unit_flags` = `unit_flags` |768 WHERE (`entry` = 26810); + +-- Set Auras (Roanauk Icemist) +DELETE FROM `creature_template_addon` WHERE (`entry` = 26810); +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(26810, 0, 0, 0, 0, 0, 0, '47417'); From b801931f0a57df0b451703eea97fcbd2646184ab Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 1 May 2026 21:17:47 +0000 Subject: [PATCH 80/92] chore(DB): import pending files Referenced commit(s): 0c5d5c448c3db15d3a1b50b359c13e782d9db664 --- .../rev_1777666032.sql => db_world/2026_05_01_04.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777666032.sql => db_world/2026_05_01_04.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1777666032.sql b/data/sql/updates/db_world/2026_05_01_04.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1777666032.sql rename to data/sql/updates/db_world/2026_05_01_04.sql index b3a3948f6..4bc2e532e 100644 --- a/data/sql/updates/pending_db_world/rev_1777666032.sql +++ b/data/sql/updates/db_world/2026_05_01_04.sql @@ -1,3 +1,4 @@ +-- DB update 2026_05_01_03 -> 2026_05_01_04 -- Set Quest Pre-requisite From the Depths of Azjol-Nerub for Strength of Icemist. UPDATE `quest_template_addon` SET `PrevQuestID` = 12036 WHERE (`ID` = 12063); From 55c231ae040b72eaff9fe2c4eb1edaa008e970fe Mon Sep 17 00:00:00 2001 From: Tereneckla Date: Sat, 2 May 2026 00:31:07 +0000 Subject: [PATCH 81/92] fix(Core/Items): skip empty stats instead of stopping on them (#25671) --- src/server/game/Globals/ObjectMgr.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index ded80406b..0e4f733a9 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -3332,12 +3332,14 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.ContainerSlots = uint32(fields[26].Get()); uint8 statsCount = 0; - while (statsCount < MAX_ITEM_PROTO_STATS) + uint8 statsIterator = 0; + while (statsIterator < MAX_ITEM_PROTO_STATS) { - uint32 statType = uint32(fields[27 + statsCount * 2].Get()); - int32 statValue = fields[28 + statsCount * 2].Get(); - if (statType == 0) - break; + uint32 statType = uint32(fields[27 + statsIterator * 2].Get()); + int32 statValue = fields[28 + statsIterator * 2].Get(); + statsIterator++; + if (statValue == 0) + continue; itemTemplate.ItemStat[statsCount].ItemStatType = statType; itemTemplate.ItemStat[statsCount].ItemStatValue = statValue; From f92dbd1ff36fe9b2f603e0597175568caa7ce1ee Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 2 May 2026 05:15:22 -0300 Subject: [PATCH 82/92] fix(DB/Quest): On Ruby Wings - prevent Antiok despawn when Thiassi dies (#25683) Co-authored-by: Dr-J Co-authored-by: Claude Opus 4.7 --- data/sql/updates/pending_db_world/rev_1777681720427119100.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777681720427119100.sql diff --git a/data/sql/updates/pending_db_world/rev_1777681720427119100.sql b/data/sql/updates/pending_db_world/rev_1777681720427119100.sql new file mode 100644 index 000000000..2e90fbda4 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777681720427119100.sql @@ -0,0 +1,4 @@ +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28006 AND `id` = 7); + +UPDATE `vehicle_template_accessory` SET `summontype` = 1, `summontimer` = 30000 + WHERE `entry` = 28018 AND `accessory_entry` = 28006; From c216d8b57fc1e4f4c8abfd1c4d037281bb99f2bf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 2 May 2026 08:16:33 +0000 Subject: [PATCH 83/92] chore(DB): import pending files Referenced commit(s): f92dbd1ff36fe9b2f603e0597175568caa7ce1ee --- .../rev_1777681720427119100.sql => db_world/2026_05_02_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777681720427119100.sql => db_world/2026_05_02_00.sql} (84%) diff --git a/data/sql/updates/pending_db_world/rev_1777681720427119100.sql b/data/sql/updates/db_world/2026_05_02_00.sql similarity index 84% rename from data/sql/updates/pending_db_world/rev_1777681720427119100.sql rename to data/sql/updates/db_world/2026_05_02_00.sql index 2e90fbda4..23e25301b 100644 --- a/data/sql/updates/pending_db_world/rev_1777681720427119100.sql +++ b/data/sql/updates/db_world/2026_05_02_00.sql @@ -1,3 +1,4 @@ +-- DB update 2026_05_01_04 -> 2026_05_02_00 DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28006 AND `id` = 7); UPDATE `vehicle_template_accessory` SET `summontype` = 1, `summontimer` = 30000 From 162d298da237224f14f50714ba8e32fc97f55615 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sat, 2 May 2026 10:59:14 +0200 Subject: [PATCH 84/92] fix(DB/Loot): Remove Quest Requirement for Faded Photograph item. (#25684) --- data/sql/updates/pending_db_world/rev_1777712061.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777712061.sql diff --git a/data/sql/updates/pending_db_world/rev_1777712061.sql b/data/sql/updates/pending_db_world/rev_1777712061.sql new file mode 100644 index 000000000..f4d2655b6 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777712061.sql @@ -0,0 +1,3 @@ + +-- Update Quest Requirement +UPDATE `item_loot_template` SET `QuestRequired` = 0 WHERE (`Entry` = 11107) AND (`Item` IN (11108)); From e4aacc17bb1a4fe38b9dfaec76ba7db6df907534 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 2 May 2026 09:00:23 +0000 Subject: [PATCH 85/92] chore(DB): import pending files Referenced commit(s): 162d298da237224f14f50714ba8e32fc97f55615 --- .../rev_1777712061.sql => db_world/2026_05_02_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777712061.sql => db_world/2026_05_02_01.sql} (74%) diff --git a/data/sql/updates/pending_db_world/rev_1777712061.sql b/data/sql/updates/db_world/2026_05_02_01.sql similarity index 74% rename from data/sql/updates/pending_db_world/rev_1777712061.sql rename to data/sql/updates/db_world/2026_05_02_01.sql index f4d2655b6..02dc3dd4f 100644 --- a/data/sql/updates/pending_db_world/rev_1777712061.sql +++ b/data/sql/updates/db_world/2026_05_02_01.sql @@ -1,3 +1,4 @@ +-- DB update 2026_05_02_00 -> 2026_05_02_01 -- Update Quest Requirement UPDATE `item_loot_template` SET `QuestRequired` = 0 WHERE (`Entry` = 11107) AND (`Item` IN (11108)); From b6292f3509045724ed4aafe92ea21efa8b32256d Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Sat, 2 May 2026 07:09:00 -0500 Subject: [PATCH 86/92] fix(Core/Druid): preserve shapeshift form across map changes (#25556) Co-authored-by: blinkysc --- src/server/game/Globals/ObjectMgr.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 0e4f733a9..2590926cc 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1895,7 +1895,10 @@ uint32 ObjectMgr::GetModelForShapeshift(ShapeshiftForm form, Player* player) con else customizationID = player->GetByteValue(PLAYER_BYTES, 0); // Use Skin Color - auto itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), customizationID, player->getGender())); + // getGender() tracks the active display model; real gender lives in PLAYER_BYTES_3 + uint8 gender = player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER); + + auto itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), customizationID, gender)); if (itr != _playerShapeshiftModel.end()) return itr->second; // Explicit combination @@ -1903,7 +1906,7 @@ uint32 ObjectMgr::GetModelForShapeshift(ShapeshiftForm form, Player* player) con if (itr != _playerShapeshiftModel.end()) return itr->second; // Combination applied to both genders - itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), 255, player->getGender())); + itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), 255, gender)); if (itr != _playerShapeshiftModel.end()) return itr->second; // Default gender-dependent model From 4a1516de752c241092fced59529a3fcbc5963d83 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Sat, 2 May 2026 14:17:46 +0200 Subject: [PATCH 87/92] fix(DB/SAI): Set probability to 0 for the second model of Mine Car and Iron Dwarf Relic. (#25664) --- data/sql/updates/pending_db_world/rev_1777551004.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1777551004.sql diff --git a/data/sql/updates/pending_db_world/rev_1777551004.sql b/data/sql/updates/pending_db_world/rev_1777551004.sql new file mode 100644 index 000000000..5688c614a --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1777551004.sql @@ -0,0 +1,11 @@ + +-- Set Probability to 0 for Mine Car and Iron Dwarf Relic second model. +UPDATE `creature_template_model` SET `Probability` = 0 WHERE (`CreatureID` IN (28817, 24824)) AND (`Idx` IN (1)); + +-- Edit Mine Car SAI (Row 0 Removed). +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28817; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28817); +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 +(28817, 0, 0, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 12, 28841, 8, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2382.17, -5899.67, 107.745, 0, 'Mine Car - On Just Summoned - Summon Creature \'Scarlet Miner\''), +(28817, 0, 1, 0, 8, 0, 100, 0, 52465, 0, 0, 0, 0, 0, 29, 3, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Mine Car - On Spellhit \'Drag Mine Cart\' - Start Follow Invoker'); From 75fab8f478a3739e23800e2961260922efe4df62 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 2 May 2026 12:18:57 +0000 Subject: [PATCH 88/92] chore(DB): import pending files Referenced commit(s): 4a1516de752c241092fced59529a3fcbc5963d83 --- .../rev_1777551004.sql => db_world/2026_05_02_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1777551004.sql => db_world/2026_05_02_02.sql} (96%) diff --git a/data/sql/updates/pending_db_world/rev_1777551004.sql b/data/sql/updates/db_world/2026_05_02_02.sql similarity index 96% rename from data/sql/updates/pending_db_world/rev_1777551004.sql rename to data/sql/updates/db_world/2026_05_02_02.sql index 5688c614a..d3341137d 100644 --- a/data/sql/updates/pending_db_world/rev_1777551004.sql +++ b/data/sql/updates/db_world/2026_05_02_02.sql @@ -1,3 +1,4 @@ +-- DB update 2026_05_02_01 -> 2026_05_02_02 -- Set Probability to 0 for Mine Car and Iron Dwarf Relic second model. UPDATE `creature_template_model` SET `Probability` = 0 WHERE (`CreatureID` IN (28817, 24824)) AND (`Idx` IN (1)); From 6efdf52d4e5584330879d17f66f8764d8a118415 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 2 May 2026 09:57:38 -0300 Subject: [PATCH 89/92] fix(Core/Network): guard against null m_Socket in WorldSession::Update idle check (#25686) Co-authored-by: Claude Opus 4.7 --- src/server/game/Server/WorldSession.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index 408c89736..ccdf9228a 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -364,7 +364,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) ///- Before we process anything: /// If necessary, kick the player because the client didn't send anything for too long /// (or they've been idling in character select) - if (IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION)) + if (m_Socket && IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION)) m_Socket->CloseSocket(); if (updater.ProcessUnsafe()) From 50ebe52b96edd116a5610d5b72fb3e31e242ae2d Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sat, 2 May 2026 10:57:03 -0300 Subject: [PATCH 90/92] fix(Scripts/Quest): Don't Kill the Fat One submission flow (#25685) --- .../scripts/Outland/zone_terokkar_forest.cpp | 93 +++++++++---------- 1 file changed, 44 insertions(+), 49 deletions(-) diff --git a/src/server/scripts/Outland/zone_terokkar_forest.cpp b/src/server/scripts/Outland/zone_terokkar_forest.cpp index 0d032ce14..5988b4bdd 100644 --- a/src/server/scripts/Outland/zone_terokkar_forest.cpp +++ b/src/server/scripts/Outland/zone_terokkar_forest.cpp @@ -231,7 +231,9 @@ enum UnkorTheRuthless FACTION_HOSTILE = 45, QUEST_DONTKILLTHEFATONE = 9889, - SPELL_PULVERIZE = 2676 + SPELL_PULVERIZE = 2676, + + SUBMIT_DURATION = 120000 }; class npc_unkor_the_ruthless : public CreatureScript @@ -248,22 +250,28 @@ public: { npc_unkor_the_ruthlessAI(Creature* creature) : ScriptedAI(creature) { } - bool CanDoQuest; - uint32 UnkorUnfriendly_Timer; - uint32 Pulverize_Timer; + bool Submitted; + uint32 UnfriendlyTimer; + uint32 PulverizeTimer; void Reset() override { - CanDoQuest = false; - UnkorUnfriendly_Timer = 0; - Pulverize_Timer = 3000; + Submitted = false; + UnfriendlyTimer = 0; + PulverizeTimer = 3000; me->SetStandState(UNIT_STAND_STATE_STAND); me->SetFaction(FACTION_HOSTILE); } void JustEngagedWith(Unit* /*who*/) override { } - void DoNice() + bool HasQuestActive(Player* player) const + { + QuestStatus status = player->GetQuestStatus(QUEST_DONTKILLTHEFATONE); + return status == QUEST_STATUS_INCOMPLETE || status == QUEST_STATUS_COMPLETE; + } + + void Submit() { Talk(SAY_SUBMIT); me->SetFaction(FACTION_FRIENDLY); @@ -271,7 +279,8 @@ public: me->RemoveAllAuras(); me->GetThreatMgr().ClearAllThreat(); me->CombatStop(true); - UnkorUnfriendly_Timer = 60000; + Submitted = true; + UnfriendlyTimer = SUBMIT_DURATION; } void DamageTaken(Unit* done_by, uint32& damage, DamageEffectType, SpellSchoolMask) override @@ -280,61 +289,47 @@ public: return; Player* player = done_by->ToPlayer(); - if (player && me->HealthBelowPctDamaged(30, damage)) - { - if (Group* group = player->GetGroup()) - { - for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) - { - Player* groupie = itr->GetSource(); - if (groupie && groupie->IsInMap(player) && - groupie->GetQuestStatus(QUEST_DONTKILLTHEFATONE) == QUEST_STATUS_INCOMPLETE && - groupie->GetReqKillOrCastCurrentCount(QUEST_DONTKILLTHEFATONE, 18260) == 10) - { - groupie->AreaExploredOrEventHappens(QUEST_DONTKILLTHEFATONE); - if (!CanDoQuest) - CanDoQuest = true; - } - } - } - else if (player->GetQuestStatus(QUEST_DONTKILLTHEFATONE) == QUEST_STATUS_INCOMPLETE && - player->GetReqKillOrCastCurrentCount(QUEST_DONTKILLTHEFATONE, 18260) == 10) - { - player->AreaExploredOrEventHappens(QUEST_DONTKILLTHEFATONE); - CanDoQuest = true; - } - } + if (!player) + if (Unit* owner = done_by->GetOwner()) + player = owner->ToPlayer(); + if (!player || !HasQuestActive(player)) + return; + + // Cap damage from quest holders so DoTs/pets/burst can't kill him. + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; + + if (!me->HealthBelowPctDamaged(30, damage)) + return; + + player->GroupEventHappens(QUEST_DONTKILLTHEFATONE, me); + + if (!Submitted) + Submit(); } void UpdateAI(uint32 diff) override { - if (CanDoQuest) + if (Submitted) { - if (!UnkorUnfriendly_Timer) + if (UnfriendlyTimer <= diff) { - //DoCast(me, SPELL_QUID9889); //not using spell for now - DoNice(); - } - else - { - if (UnkorUnfriendly_Timer <= diff) - { - EnterEvadeMode(); - return; - } - else UnkorUnfriendly_Timer -= diff; + EnterEvadeMode(); + return; } + UnfriendlyTimer -= diff; + return; } if (!UpdateVictim()) return; - if (Pulverize_Timer <= diff) + if (PulverizeTimer <= diff) { DoCast(me, SPELL_PULVERIZE); - Pulverize_Timer = 9000; + PulverizeTimer = 9000; } - else Pulverize_Timer -= diff; + else PulverizeTimer -= diff; DoMeleeAttackIfReady(); } From 43aa645660176f44cc2ab275aa3e527ca4148775 Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 2 May 2026 19:57:06 +0200 Subject: [PATCH 91/92] fix(Scripts/Northrend): Update Drakkensryd proto-drake vehicle logic (#25587) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../game/Spells/SpellInfoCorrections.cpp | 6 ++++ .../scripts/Northrend/zone_storm_peaks.cpp | 32 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index e09e82a37..03b9107fe 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -5189,6 +5189,12 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->ProcCharges = 0; }); + // 54933 Hyldnir Harpoon + ApplySpellFix({ 54933 }, [](SpellInfo* spellInfo) + { + spellInfo->Effects[EFFECT_0].BasePoints = 1; + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i]; diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index b8c6bec5e..5140656c0 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -538,42 +538,46 @@ public: } }; -enum HyldsmeetProtoDrake -{ - NPC_HYLDSMEET_DRAKERIDER = 29694 -}; - struct npc_hyldsmeet_protodrake : public CreatureAI { - explicit npc_hyldsmeet_protodrake(Creature* creature) : CreatureAI(creature), _accessoryRespawnTimer(0) { } + explicit npc_hyldsmeet_protodrake(Creature* creature) : CreatureAI(creature), _accessoryInstalled(false), _accessoryRespawnTimer(0) + { + me->SetUnitFlag2(UNIT_FLAG2_PREVENT_SPELL_CLICK); + } void PassengerBoarded(Unit* who, int8 /*seat*/, bool apply) override { - if (apply) + if (who->IsPlayer()) return; - if (who->GetEntry() == NPC_HYLDSMEET_DRAKERIDER) + if (apply) + _accessoryInstalled = true; + else + { + _accessoryInstalled = false; _accessoryRespawnTimer = 5 * MINUTE * IN_MILLISECONDS; + } } void UpdateAI(uint32 diff) override { - //! We need to manually reinstall accessories because the vehicle itself is friendly to players, - //! so EnterEvadeMode is never triggered. The accessory on the other hand is hostile and killable. + // We need to manually reinstall accessories because the vehicle itself is friendly to players, + // so EnterEvadeMode is never triggered. The accessory on the other hand is hostile and killable. + if (_accessoryInstalled) + return; + Vehicle* vehicleKit = me->GetVehicleKit(); - if (!vehicleKit || !_accessoryRespawnTimer) + if (!vehicleKit) return; if (_accessoryRespawnTimer <= diff) - { vehicleKit->InstallAllAccessories(true); - _accessoryRespawnTimer = 0; - } else _accessoryRespawnTimer -= diff; } private: + bool _accessoryInstalled; uint32 _accessoryRespawnTimer; }; From 1c66fdd45bbb3b577ce9e65533042d6e5017016a Mon Sep 17 00:00:00 2001 From: sogladev Date: Sat, 2 May 2026 20:41:35 +0200 Subject: [PATCH 92/92] fix(Scripts/Naxxramas): Patchwerk's Hateful Strike target selection (#25429) --- src/server/game/Entities/Unit/Unit.h | 26 ++++++++ .../Northrend/Naxxramas/boss_patchwerk.cpp | 64 ++++++++----------- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index c8dff2f94..4144c9ecd 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -2310,6 +2310,32 @@ namespace Acore private: bool const _ascending; }; + + // Binary predicate for sorting Units based on current value of health + class HealthOrderPred + { + public: + HealthOrderPred(bool ascending = true) : _ascending(ascending) { } + + bool operator()(WorldObject const* objA, WorldObject const* objB) const + { + Unit const* a = objA->ToUnit(); + Unit const* b = objB->ToUnit(); + uint32 rA = a ? a->GetHealth() : 0; + uint32 rB = b ? b->GetHealth() : 0; + return _ascending ? rA < rB : rA > rB; + } + + bool operator() (Unit const* a, Unit const* b) const + { + uint32 rA = a ? a->GetHealth() : 0; + uint32 rB = b ? b->GetHealth() : 0; + return _ascending ? rA < rB : rA > rB; + } + + private: + bool const _ascending; + }; } class RedirectSpellEvent : public BasicEvent diff --git a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp index 25d46fcbd..78be3def6 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_patchwerk.cpp @@ -113,49 +113,37 @@ public: { case EVENT_HATEFUL_STRIKE: { - // Cast Hateful strike on the player with the highest amount of HP within melee distance, and second threat amount - std::list meleeRangeTargets; - Unit* finalTarget = nullptr; - uint8 counter = 0; - auto threatList = me->GetThreatMgr().GetSortedThreatList(); - for (auto i = threatList.begin(); i != threatList.end(); ++i, ++counter) + // Scan melee targets (melee range), choose target according to mechanics: + // - If only 1 melee target exists, hit the tank (current victim). + // - Otherwise select the top RAID_MODE(1,2) threat targets (excluding current victim) + // among melee targets and hit the one with the highest HP. + // - The target hit gains 500 flat threat. + + Unit* victim = me->GetVictim(); + if (!victim) { - // Gather all units with melee range - Unit* target = (*i)->GetVictim(); - if (me->IsWithinMeleeRange(target)) - { - meleeRangeTargets.push_back(target); - } - // and add threat to most hated - if (counter < RAID_MODE(2, 3)) - { - me->AddThreat(target, 500.0f); - } + events.RescheduleEvent(EVENT_HATEFUL_STRIKE, 1s); + break; } - counter = 0; - std::list>::iterator itr; - for (itr = meleeRangeTargets.begin(); itr != meleeRangeTargets.end(); ++itr, ++counter) + + Unit* hatefulTarget = victim; // default to current victim if no other target is available + + std::list meleeTargets; + constexpr float HATEFUL_STRIKE_RANGE = 5.0f; + SelectTargetList(meleeTargets, RAID_MODE(1, 2), SelectTargetMethod::MaxThreat, 0, [&](Unit const* target) { - // if there is only one target available - if (meleeRangeTargets.size() == 1) - { - finalTarget = (*itr); - } - else if (counter > 0) // skip first target - { - if (!finalTarget || (*itr)->GetHealth() > finalTarget->GetHealth()) - { - finalTarget = (*itr); - } - // third loop - if (counter >= 2) - break; - } - } - if (finalTarget) + return target && target->IsPlayer() && target != victim && me->IsWithinCombatRange(target, HATEFUL_STRIKE_RANGE); + }); + + if (!meleeTargets.empty()) { - me->CastSpell(finalTarget, SPELL_HATEFUL_STRIKE, false); + meleeTargets.sort(Acore::HealthOrderPred(false)); + hatefulTarget = meleeTargets.front(); } + + me->CastSpell(hatefulTarget, SPELL_HATEFUL_STRIKE, false); + me->AddThreat(hatefulTarget, 500.0f); + events.Repeat(1s); break; }