From 3b13fe620762c76d0bd6b0f4cf4f8bacffe82e05 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Sun, 29 Jun 2025 13:28:21 +0200 Subject: [PATCH 01/43] fix(Scripts/Spells): add script to GM spell CooldownAll (#22383) --- .../rev_1751148569961282780.sql | 3 +++ src/server/scripts/Spells/spell_generic.cpp | 17 +++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1751148569961282780.sql diff --git a/data/sql/updates/pending_db_world/rev_1751148569961282780.sql b/data/sql/updates/pending_db_world/rev_1751148569961282780.sql new file mode 100644 index 000000000..6af092896 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1751148569961282780.sql @@ -0,0 +1,3 @@ +-- +DELETE FROM `spell_script_names` WHERE `spell_id` = 29313 AND `ScriptName` = 'spell_gen_cooldown_all'; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (29313 , 'spell_gen_cooldown_all'); diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 0678db1e4..3c879a3e0 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -5431,6 +5431,22 @@ private: uint32 _spellId; }; +class spell_gen_cooldown_all : public SpellScript +{ + PrepareSpellScript(spell_gen_cooldown_all); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Player* player = GetHitPlayer()) + player->RemoveAllSpellCooldown(); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_gen_cooldown_all::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } +}; + void AddSC_generic_spell_scripts() { RegisterSpellScript(spell_silithyst); @@ -5593,4 +5609,5 @@ void AddSC_generic_spell_scripts() RegisterSpellScript(spell_gen_proc_on_victim); RegisterSpellScriptWithArgs(spell_gen_translocate, "spell_gen_translocate_down", SPELL_TRANSLOCATION_DOWN); RegisterSpellScriptWithArgs(spell_gen_translocate, "spell_gen_translocate_up", SPELL_TRANSLOCATION_UP); + RegisterSpellScript(spell_gen_cooldown_all); } From 9c2ea449b6988406a453cd479d33481ab5e3c207 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Jun 2025 11:29:19 +0000 Subject: [PATCH 02/43] chore(DB): import pending files Referenced commit(s): 3b13fe620762c76d0bd6b0f4cf4f8bacffe82e05 --- .../rev_1751148569961282780.sql => db_world/2025_06_29_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1751148569961282780.sql => db_world/2025_06_29_00.sql} (82%) diff --git a/data/sql/updates/pending_db_world/rev_1751148569961282780.sql b/data/sql/updates/db_world/2025_06_29_00.sql similarity index 82% rename from data/sql/updates/pending_db_world/rev_1751148569961282780.sql rename to data/sql/updates/db_world/2025_06_29_00.sql index 6af092896..695215628 100644 --- a/data/sql/updates/pending_db_world/rev_1751148569961282780.sql +++ b/data/sql/updates/db_world/2025_06_29_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_06_28_00 -> 2025_06_29_00 -- DELETE FROM `spell_script_names` WHERE `spell_id` = 29313 AND `ScriptName` = 'spell_gen_cooldown_all'; INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (29313 , 'spell_gen_cooldown_all'); From 57487b8244354a4b38582b39f371a2b668f3ecfc Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:58:53 -0400 Subject: [PATCH 03/43] fix(DB/Creature): Make Captured Raptor immune to player characters. (#22387) Co-authored-by: Ryan Turner <16946913+TheSCREWEDSoftware@users.noreply.github.com> --- data/sql/updates/pending_db_world/captured-raptor-unit-flag.sql | 1 + 1 file changed, 1 insertion(+) create mode 100644 data/sql/updates/pending_db_world/captured-raptor-unit-flag.sql diff --git a/data/sql/updates/pending_db_world/captured-raptor-unit-flag.sql b/data/sql/updates/pending_db_world/captured-raptor-unit-flag.sql new file mode 100644 index 000000000..60329771e --- /dev/null +++ b/data/sql/updates/pending_db_world/captured-raptor-unit-flag.sql @@ -0,0 +1 @@ +UPDATE `creature_template` SET `unit_flags` = `unit_flags` | 256 WHERE `entry` = 23741; From d00449dbd8d4c17ddc3125f954b6d0d201a89c22 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Jun 2025 18:59:54 +0000 Subject: [PATCH 04/43] chore(DB): import pending files Referenced commit(s): 57487b8244354a4b38582b39f371a2b668f3ecfc --- .../captured-raptor-unit-flag.sql => db_world/2025_06_29_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/captured-raptor-unit-flag.sql => db_world/2025_06_29_01.sql} (66%) diff --git a/data/sql/updates/pending_db_world/captured-raptor-unit-flag.sql b/data/sql/updates/db_world/2025_06_29_01.sql similarity index 66% rename from data/sql/updates/pending_db_world/captured-raptor-unit-flag.sql rename to data/sql/updates/db_world/2025_06_29_01.sql index 60329771e..e0caf3b31 100644 --- a/data/sql/updates/pending_db_world/captured-raptor-unit-flag.sql +++ b/data/sql/updates/db_world/2025_06_29_01.sql @@ -1 +1,2 @@ +-- DB update 2025_06_29_00 -> 2025_06_29_01 UPDATE `creature_template` SET `unit_flags` = `unit_flags` | 256 WHERE `entry` = 23741; From 563ef5d7d9c270ad0bc92808e4e92e70d56c8d5b Mon Sep 17 00:00:00 2001 From: Christian M Date: Sun, 29 Jun 2025 15:02:09 -0400 Subject: [PATCH 05/43] fix(Scripts/Spells): Death Knight Bone shield charges now have a 2 second delay between uses. (#22340) --- src/server/scripts/Spells/spell_dk.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 8e27678c0..c8cf3fb0d 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -530,11 +530,21 @@ class spell_dk_bone_shield : public AuraScript { PrepareAuraScript(spell_dk_bone_shield); + uint32 lastChargeUsedTime = 0; + void HandleProc(ProcEventInfo& eventInfo) { PreventDefaultAction(); + uint32 currentTime = getMSTime(); + // Checks for 2 seconds between uses of bone shield charges + if ((currentTime - lastChargeUsedTime) < 2000) + return; + if (!eventInfo.GetSpellInfo() || !eventInfo.GetSpellInfo()->IsTargetingArea()) + { DropCharge(); + lastChargeUsedTime = currentTime; + } } void Register() override From ae28b35f2c8ea94a026a88d69f7be7675e39051f Mon Sep 17 00:00:00 2001 From: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Date: Sun, 29 Jun 2025 21:02:55 +0200 Subject: [PATCH 06/43] feat(Scripts/Commands): Add guild ranks display to guild info command (#22380) --- .../pending_db_world/rev_1751142733581192500.sql | 5 +++++ src/server/game/Miscellaneous/Language.h | 4 +++- src/server/scripts/Commands/cs_guild.cpp | 16 +++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1751142733581192500.sql diff --git a/data/sql/updates/pending_db_world/rev_1751142733581192500.sql b/data/sql/updates/pending_db_world/rev_1751142733581192500.sql new file mode 100644 index 000000000..c4ef57308 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1751142733581192500.sql @@ -0,0 +1,5 @@ +-- +DELETE FROM `acore_string` WHERE `entry` IN (1184,1185); +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES +(1184, '| Guild Ranks:'), +(1185, '| {} - {}'); diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index b99ed999b..801ad3a48 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -972,7 +972,9 @@ enum AcoreStrings LANG_GUILD_INFO_BANK_GOLD = 1181, LANG_GUILD_INFO_MOTD = 1182, LANG_GUILD_INFO_EXTRA_INFO = 1183, - // Room for more level 3 1184-1198 not used + LANG_GUILD_INFO_RANKS = 1184, + LANG_GUILD_INFO_RANKS_LIST = 1185, + // Room for more level 3 1186-1198 not used // Debug commands LANG_DO_NOT_USE_6X_DEBUG_AREATRIGGER_LEFT = 1999, diff --git a/src/server/scripts/Commands/cs_guild.cpp b/src/server/scripts/Commands/cs_guild.cpp index b3ea9e0f6..9075f2db1 100644 --- a/src/server/scripts/Commands/cs_guild.cpp +++ b/src/server/scripts/Commands/cs_guild.cpp @@ -74,7 +74,7 @@ public: if (sGuildMgr->GetGuildByName(guildName)) { - handler->SendErrorMessage(LANG_GUILD_RENAME_ALREADY_EXISTS); + handler->SendErrorMessage(LANG_GUILD_RENAME_ALREADY_EXISTS, guildName); return false; } @@ -249,6 +249,20 @@ public: handler->PSendSysMessage(LANG_GUILD_INFO_BANK_GOLD, guild->GetTotalBankMoney() / 100 / 100); // Bank Gold (in gold coins) handler->PSendSysMessage(LANG_GUILD_INFO_MOTD, guild->GetMOTD()); // Message of the Day handler->PSendSysMessage(LANG_GUILD_INFO_EXTRA_INFO, guild->GetInfo()); // Extra Information + + QueryResult result = CharacterDatabase.Query("SELECT rid, rname FROM guild_rank WHERE guildid = {}", guild->GetId()); + if (result) + { + handler->PSendSysMessage(LANG_GUILD_INFO_RANKS); + do + { + Field* fields = result->Fetch(); + uint32 rid = fields[0].Get(); + std::string rname = fields[1].Get(); + + handler->PSendSysMessage(LANG_GUILD_INFO_RANKS_LIST, rid, rname); + } while (result->NextRow()); + } return true; } }; From c3cec7198ed08919794e20972fe62015417a9bff Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Jun 2025 19:03:11 +0000 Subject: [PATCH 07/43] chore(DB): import pending files Referenced commit(s): 563ef5d7d9c270ad0bc92808e4e92e70d56c8d5b --- .../rev_1751142733581192500.sql => db_world/2025_06_29_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1751142733581192500.sql => db_world/2025_06_29_02.sql} (79%) diff --git a/data/sql/updates/pending_db_world/rev_1751142733581192500.sql b/data/sql/updates/db_world/2025_06_29_02.sql similarity index 79% rename from data/sql/updates/pending_db_world/rev_1751142733581192500.sql rename to data/sql/updates/db_world/2025_06_29_02.sql index c4ef57308..cfc750bac 100644 --- a/data/sql/updates/pending_db_world/rev_1751142733581192500.sql +++ b/data/sql/updates/db_world/2025_06_29_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_06_29_01 -> 2025_06_29_02 -- DELETE FROM `acore_string` WHERE `entry` IN (1184,1185); INSERT INTO `acore_string` (`entry`, `content_default`) VALUES From 31012923c10df51e8ead11d7fbbb40b09ab2bad0 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Sun, 29 Jun 2025 16:11:11 -0300 Subject: [PATCH 08/43] =?UTF-8?q?fix(Core/Spells):=20Fix=20SPELL=5FEFFECT?= =?UTF-8?q?=5FLEAP=5FBACK=20not=20working=20on=20targeted=20=E2=80=A6=20(#?= =?UTF-8?q?22389)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rev_1751221090793496700.sql | 2 ++ src/server/game/Spells/SpellEffects.cpp | 2 +- .../ScarletEnclave/chapter5.cpp | 18 ------------------ 3 files changed, 3 insertions(+), 19 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1751221090793496700.sql diff --git a/data/sql/updates/pending_db_world/rev_1751221090793496700.sql b/data/sql/updates/pending_db_world/rev_1751221090793496700.sql new file mode 100644 index 000000000..67869a9b6 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1751221090793496700.sql @@ -0,0 +1,2 @@ +-- +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_chapter5_rebuke' AND `spell_id` = 53680; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index afe452a76..d176f869b 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -5037,7 +5037,7 @@ void Spell::EffectLeapBack(SpellEffIndex effIndex) float speedxy = m_spellInfo->Effects[effIndex].MiscValue / 10.0f; float speedz = damage / 10.0f; //1891: Disengage - m_caster->JumpTo(speedxy, speedz, m_spellInfo->SpellFamilyName != SPELLFAMILY_HUNTER); + unitTarget->JumpTo(speedxy, speedz, m_spellInfo->SpellFamilyName != SPELLFAMILY_HUNTER); if (m_caster->IsPlayer()) { diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp index 22c76c4d5..30ffb72b4 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp @@ -1200,23 +1200,6 @@ class spell_chapter5_light_of_dawn_aura : public AuraScript } }; -class spell_chapter5_rebuke : public SpellScript -{ - PrepareSpellScript(spell_chapter5_rebuke); - - void HandleLeapBack(SpellEffIndex effIndex) - { - PreventHitEffect(effIndex); - if (Unit* unitTarget = GetHitUnit()) - unitTarget->KnockbackFrom(2282.86f, -5263.45f, 40.0f, 8.0f); - } - - void Register() override - { - OnEffectLaunchTarget += SpellEffectFn(spell_chapter5_rebuke::HandleLeapBack, EFFECT_0, SPELL_EFFECT_LEAP_BACK); - } -}; - // 58552 - Return to Orgrimmar // 58533 - Return to Stormwind enum ReturnToCapital @@ -1317,6 +1300,5 @@ void AddSC_the_scarlet_enclave_c5() { new npc_highlord_darion_mograine(); RegisterSpellScript(spell_chapter5_light_of_dawn_aura); - RegisterSpellScript(spell_chapter5_rebuke); RegisterSpellScript(spell_chapter5_return_to_capital); } From ab5af303ab973127b4e77bcd148a6579e89881a7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 29 Jun 2025 19:12:09 +0000 Subject: [PATCH 09/43] chore(DB): import pending files Referenced commit(s): 31012923c10df51e8ead11d7fbbb40b09ab2bad0 --- .../rev_1751221090793496700.sql => db_world/2025_06_29_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1751221090793496700.sql => db_world/2025_06_29_03.sql} (70%) diff --git a/data/sql/updates/pending_db_world/rev_1751221090793496700.sql b/data/sql/updates/db_world/2025_06_29_03.sql similarity index 70% rename from data/sql/updates/pending_db_world/rev_1751221090793496700.sql rename to data/sql/updates/db_world/2025_06_29_03.sql index 67869a9b6..7ab848a07 100644 --- a/data/sql/updates/pending_db_world/rev_1751221090793496700.sql +++ b/data/sql/updates/db_world/2025_06_29_03.sql @@ -1,2 +1,3 @@ +-- DB update 2025_06_29_02 -> 2025_06_29_03 -- DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_chapter5_rebuke' AND `spell_id` = 53680; From 656694b383ad4a1bbc4a62edd88f212e24bfb9e2 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Mon, 30 Jun 2025 05:49:47 +0200 Subject: [PATCH 10/43] feat(Core/Command): add GM spectator to allow cross-faction `/follow` (#22393) --- .../rev_1751234046378828666.sql | 5 ++++ src/server/game/Entities/Player/Player.h | 4 +++ src/server/game/Entities/Unit/Unit.cpp | 4 +++ src/server/game/Miscellaneous/Language.h | 4 ++- src/server/scripts/Commands/cs_gm.cpp | 28 ++++++++++++++----- 5 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1751234046378828666.sql diff --git a/data/sql/updates/pending_db_world/rev_1751234046378828666.sql b/data/sql/updates/pending_db_world/rev_1751234046378828666.sql new file mode 100644 index 000000000..d20329423 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1751234046378828666.sql @@ -0,0 +1,5 @@ +-- +DELETE FROM `acore_string` WHERE `entry` IN (6617, 6618); +INSERT INTO `acore_string` (`entry`, `content_default`) VALUES +(6617, 'GM Spectator is ON'), +(6618, 'GM Spectator is OFF'); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 9ac8df486..24af62b65 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -597,6 +597,7 @@ enum PlayerExtraFlags PLAYER_EXTRA_SPECTATOR_ON = 0x0080, // Marks if player is spectactor PLAYER_EXTRA_PVP_DEATH = 0x0100, // store PvP death status until corpse creating. PLAYER_EXTRA_SHOW_DK_PET = 0x0400, // Marks if player should see ghoul on login screen + PLAYER_EXTRA_GM_SPECTATOR = 0x0800, }; // 2^n values @@ -1177,6 +1178,9 @@ public: 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; } + [[nodiscard]] bool IsGMSpectator() const { return m_ExtraFlags & PLAYER_EXTRA_GM_SPECTATOR; } + void SetGMSpectator(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_GM_SPECTATOR; else m_ExtraFlags &= ~PLAYER_EXTRA_GM_SPECTATOR; } + [[nodiscard]] bool isTaxiCheater() const { return m_ExtraFlags & PLAYER_EXTRA_TAXICHEAT; } void SetTaxiCheater(bool on) { if (on) m_ExtraFlags |= PLAYER_EXTRA_TAXICHEAT; else m_ExtraFlags &= ~PLAYER_EXTRA_TAXICHEAT; } [[nodiscard]] bool isGMVisible() const { return !(m_ExtraFlags & PLAYER_EXTRA_GM_INVISIBLE); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 72fb816a4..3d45ed10d 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -20791,6 +20791,10 @@ void Unit::PatchValuesUpdate(ByteBuffer& valuesUpdateBuf, BuildValuesCachePosPoi { valuesUpdateBuf.put(posPointers.UnitFieldFactionTemplatePos, uint32(target->GetFaction())); } + else if (target->IsGMSpectator() && IsControlledByPlayer()) + { + valuesUpdateBuf.put(posPointers.UnitFieldFactionTemplatePos, uint32(target->GetFaction())); + } } sScriptMgr->OnPatchValuesUpdate(this, valuesUpdateBuf, posPointers, target); diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 801ad3a48..3565a6cce 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1171,8 +1171,10 @@ enum AcoreStrings LANG_GM_ANNOUNCE_COLOR = 6615, LANG_GM_SILENCE = 6616, // "Silence is ON for %s" - Spell 1852 + LANG_GM_SPECTATOR_ON = 6617, + LANG_GM_SPECTATOR_OFF = 6618, - // Free strings 6617-7522 + // Free strings 6619-7522 LANG_WORLD_CLOSED = 7523, LANG_WORLD_OPENED = 7524, diff --git a/src/server/scripts/Commands/cs_gm.cpp b/src/server/scripts/Commands/cs_gm.cpp index 358d16b8b..302fb5803 100644 --- a/src/server/scripts/Commands/cs_gm.cpp +++ b/src/server/scripts/Commands/cs_gm.cpp @@ -38,13 +38,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 } + { "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 }, }; static ChatCommandTable commandTable = { @@ -236,6 +237,19 @@ public: handler->SendNotification(LANG_GM_OFF); return true; } + + static bool HandleGMSpectatorCommand(ChatHandler* handler, Optional enable) + { + Player* player = handler->GetSession()->GetPlayer(); + + if (enable.has_value()) + player->SetGMSpectator(*enable); + else + player->SetGMSpectator(!player->IsGMSpectator()); + handler->SendNotification(player->IsGMSpectator() ? LANG_GM_SPECTATOR_ON : LANG_GM_SPECTATOR_OFF); + + return true; + } }; void AddSC_gm_commandscript() From 904ddc72cc92bbd668c2a2a0b4da8e2a09de02cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 30 Jun 2025 03:50:49 +0000 Subject: [PATCH 11/43] chore(DB): import pending files Referenced commit(s): 656694b383ad4a1bbc4a62edd88f212e24bfb9e2 --- .../rev_1751234046378828666.sql => db_world/2025_06_30_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1751234046378828666.sql => db_world/2025_06_30_00.sql} (80%) diff --git a/data/sql/updates/pending_db_world/rev_1751234046378828666.sql b/data/sql/updates/db_world/2025_06_30_00.sql similarity index 80% rename from data/sql/updates/pending_db_world/rev_1751234046378828666.sql rename to data/sql/updates/db_world/2025_06_30_00.sql index d20329423..575919617 100644 --- a/data/sql/updates/pending_db_world/rev_1751234046378828666.sql +++ b/data/sql/updates/db_world/2025_06_30_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_06_29_03 -> 2025_06_30_00 -- DELETE FROM `acore_string` WHERE `entry` IN (6617, 6618); INSERT INTO `acore_string` (`entry`, `content_default`) VALUES From fd262c3ab109afec7d191c97d2b44f58eb4a1477 Mon Sep 17 00:00:00 2001 From: Takenbacon Date: Mon, 30 Jun 2025 08:14:12 -0700 Subject: [PATCH 12/43] feat(Core/Config): Improvements to config caching (#21647) --- src/common/Configuration/ConfigValueCache.h | 145 +++ src/server/game/Entities/Player/Player.cpp | 4 +- src/server/game/Entities/Unit/Unit.cpp | 4 +- src/server/game/Globals/ObjectMgr.cpp | 32 +- src/server/game/Handlers/CharacterHandler.cpp | 5 +- src/server/game/Loot/LootMgr.cpp | 7 +- src/server/game/Warden/WardenWin.cpp | 2 +- src/server/game/World/IWorld.h | 494 +------- src/server/game/World/World.cpp | 1077 ++--------------- src/server/game/World/World.h | 63 +- src/server/game/World/WorldConfig.cpp | 658 ++++++++++ src/server/game/World/WorldConfig.h | 487 ++++++++ src/test/mocks/WorldMock.h | 20 +- 13 files changed, 1443 insertions(+), 1555 deletions(-) create mode 100644 src/common/Configuration/ConfigValueCache.h create mode 100644 src/server/game/World/WorldConfig.cpp create mode 100644 src/server/game/World/WorldConfig.h diff --git a/src/common/Configuration/ConfigValueCache.h b/src/common/Configuration/ConfigValueCache.h new file mode 100644 index 000000000..e36d411ac --- /dev/null +++ b/src/common/Configuration/ConfigValueCache.h @@ -0,0 +1,145 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef CONFIGVALUECACHE_H +#define CONFIGVALUECACHE_H + +#include "Common.h" +#include "Config.h" +#include "Errors.h" +#include "Log.h" +#include + +template +class ConfigValueCache +{ + static_assert(std::is_enum_v); + +public: + enum class Reloadable : bool + { + No = false, + Yes = true + }; + + ConfigValueCache(ConfigEnum const configCount) + { + _configs.resize(static_cast(configCount)); + _reloading = false; + } + + void Initialize(bool reload) + { + _reloading = reload; + BuildConfigCache(); + _reloading = false; + VerifyAllConfigsLoaded(); + } + + template + void SetConfigValue(ConfigEnum const config, std::string const& configName, T const& defaultValue, Reloadable reloadable = Reloadable::Yes, std::function&& checker = {}, std::string const& validationErrorText = "") + { + uint32 const configIndex = static_cast(config); + ASSERT(configIndex < _configs.size(), "Config index out of bounds"); + T const& configValue = sConfigMgr->GetOption(configName, defaultValue); + + bool configValueChanged = false; + if (_reloading) + { + if (std::get(_configs[configIndex]) != configValue) + configValueChanged = true; + + if (reloadable == Reloadable::No) + { + if (configValueChanged) + LOG_ERROR("server.loading", "Server Config (Name: {}) cannot be changed by reload. A server restart is required to update this config value.", configName); + return; + } + } + else + ASSERT(_configs[configIndex].index() == 0, "Config overwriting an existing value"); + + if (checker && !checker(configValue)) + { + LOG_ERROR("server.loading", "Server Config (Name: {}) failed validation check '{}'. Default value '{}' will be used instead.", configName, validationErrorText, defaultValue); + _configs[configIndex] = defaultValue; + } + else + _configs[configIndex] = configValue; + } + + template + void OverwriteConfigValue(ConfigEnum const config, T const& value) + { + uint32 const configIndex = static_cast(config); + ASSERT(configIndex < _configs.size(), "Config index out of bounds"); + size_t const oldValueTypeIndex = _configs[configIndex].index(); + ASSERT(oldValueTypeIndex != 0, "Config value must already be set"); + _configs[configIndex] = value; + ASSERT(oldValueTypeIndex == _configs[configIndex].index(), "Config value type changed"); + } + + template + T GetConfigValue(ConfigEnum const config) const + { + uint32 const configIndex = static_cast(config); + ASSERT(configIndex < _configs.size(), "Config index out of bounds"); + ASSERT(_configs[configIndex].index() != 0, "Config value must already be set"); + + T const* value = std::get_if(&_configs[configIndex]); + ASSERT(value, "Wrong config variant type"); + + return *value; + } + + // Custom handling for string configs to convert from std::string to std::string_view + std::string_view GetConfigValue(ConfigEnum const config) const + { + uint32 const configIndex = static_cast(config); + ASSERT(configIndex < _configs.size(), "Config index out of bounds"); + ASSERT(_configs[configIndex].index() != 0, "Config value must already be set"); + + std::string const* stringValue = std::get_if(&_configs[configIndex]); + ASSERT(stringValue, "Wrong config variant type"); + + return std::string_view(*stringValue); + } + +protected: + virtual void BuildConfigCache() = 0; + +private: + void VerifyAllConfigsLoaded() + { + uint32 configIndex = 0; + for (auto const& variant : _configs) + { + if (variant.index() == 0) + { + LOG_ERROR("server.loading", "Server Config (Index: {}) is defined but not loaded, unable to continue.", configIndex); + ASSERT(false); + } + + ++configIndex; + } + } + + std::vector> _configs; + bool _reloading; +}; + +#endif diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 3ff49c4c8..95991766b 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -797,8 +797,8 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) if (type == DAMAGE_FALL) // DealDamage not apply item durability loss at self damage { LOG_DEBUG("entities.player", "Player::EnvironmentalDamage: Player '{}' ({}) fall to death, losing {} durability", - GetName(), GetGUID().ToString(), sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH)); - DurabilityLossAll(sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH), false); + GetName(), GetGUID().ToString(), sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH) / 100.0f); + DurabilityLossAll(sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH) / 100.0f, false); // durability lost message SendDurabilityLoss(); } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 3d45ed10d..924ddae88 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -17949,8 +17949,8 @@ void Unit::Kill(Unit* killer, Unit* victim, bool durabilityLoss, WeaponAttackTyp // only if not player and not controlled by player pet. And not at BG if ((durabilityLoss && !player && !plrVictim->InBattleground()) || (player && sWorld->getBoolConfig(CONFIG_DURABILITY_LOSS_IN_PVP))) { - LOG_DEBUG("entities.unit", "We are dead, losing {} percent durability", sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH)); - plrVictim->DurabilityLossAll(sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH), false); + LOG_DEBUG("entities.unit", "We are dead, losing {} percent durability", sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH) / 100.0f); + plrVictim->DurabilityLossAll(sWorld->getRate(RATE_DURABILITY_LOSS_ON_DEATH) / 100.0f, false); // durability lost message plrVictim->SendDurabilityLoss(); } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index ed2ed79ea..940f0b222 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2826,6 +2826,30 @@ void ObjectMgr::LoadItemLocales() LOG_INFO("server.loading", ">> Loaded {} Item Locale Strings in {} ms", (uint32)_itemLocaleStore.size(), GetMSTimeDiffToNow(oldMSTime)); } +ServerConfigs const qualityToBuyValueConfig[MAX_ITEM_QUALITY] = +{ + RATE_BUYVALUE_ITEM_POOR, // ITEM_QUALITY_POOR + RATE_BUYVALUE_ITEM_NORMAL, // ITEM_QUALITY_NORMAL + RATE_BUYVALUE_ITEM_UNCOMMON, // ITEM_QUALITY_UNCOMMON + RATE_BUYVALUE_ITEM_RARE, // ITEM_QUALITY_RARE + RATE_BUYVALUE_ITEM_EPIC, // ITEM_QUALITY_EPIC + RATE_BUYVALUE_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY + RATE_BUYVALUE_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT + RATE_BUYVALUE_ITEM_HEIRLOOM, // ITEM_QUALITY_HEIRLOOM +}; + +ServerConfigs const qualityToSellValueConfig[MAX_ITEM_QUALITY] = +{ + RATE_SELLVALUE_ITEM_POOR, // ITEM_QUALITY_POOR + RATE_SELLVALUE_ITEM_NORMAL, // ITEM_QUALITY_NORMAL + RATE_SELLVALUE_ITEM_UNCOMMON, // ITEM_QUALITY_UNCOMMON + RATE_SELLVALUE_ITEM_RARE, // ITEM_QUALITY_RARE + RATE_SELLVALUE_ITEM_EPIC, // ITEM_QUALITY_EPIC + RATE_SELLVALUE_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY + RATE_SELLVALUE_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT + RATE_SELLVALUE_ITEM_HEIRLOOM, // ITEM_QUALITY_HEIRLOOM +}; + void ObjectMgr::LoadItemTemplates() { uint32 oldMSTime = getMSTime(); @@ -2893,8 +2917,8 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.Flags = ItemFlags(fields[7].Get()); itemTemplate.Flags2 = ItemFlags2(fields[8].Get()); itemTemplate.BuyCount = uint32(fields[9].Get()); - itemTemplate.BuyPrice = int32(fields[10].Get() * sWorld->getRate((Rates)(RATE_BUYVALUE_ITEM_POOR + itemTemplate.Quality))); - itemTemplate.SellPrice = uint32(fields[11].Get() * sWorld->getRate((Rates)(RATE_SELLVALUE_ITEM_POOR + itemTemplate.Quality))); + itemTemplate.BuyPrice = int32(fields[10].Get()); + itemTemplate.SellPrice = uint32(fields[11].Get()); itemTemplate.InventoryType = uint32(fields[12].Get()); itemTemplate.AllowableClass = fields[13].Get(); itemTemplate.AllowableRace = fields[14].Get(); @@ -3376,6 +3400,10 @@ void ObjectMgr::LoadItemTemplates() itemTemplate.FlagsCu = static_cast(static_cast(itemTemplate.FlagsCu) & ~ITEM_FLAGS_CU_DURATION_REAL_TIME); } + // Set after checks to ensure valid item quality + itemTemplate.BuyPrice *= sWorld->getRate(qualityToBuyValueConfig[itemTemplate.Quality]); + itemTemplate.SellPrice *= sWorld->getRate(qualityToSellValueConfig[itemTemplate.Quality]); + // Fill categories map for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) if (itemTemplate.Spells[i].SpellId && itemTemplate.Spells[i].SpellCategory && itemTemplate.Spells[i].SpellCategoryCooldown) diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 38dd5fb9e..511a5b5d2 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -883,8 +883,9 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder) pCurrChar->SendCinematicStart(rEntry->CinematicSequence); // send new char string if not empty - if (!sWorld->GetNewCharString().empty()) - chH.PSendSysMessage("{}", sWorld->GetNewCharString()); + std::string_view newCharString = sWorld->getStringConfig(CONFIG_NEW_CHAR_STRING); + if (!newCharString.empty()) + chH.PSendSysMessage("{}", newCharString); } } diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 8997be846..b5bcbea31 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -30,7 +30,7 @@ #include "Util.h" #include "World.h" -static Rates const qualityToRate[MAX_ITEM_QUALITY] = +ServerConfigs const qualityToRate[] = { RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL @@ -322,8 +322,9 @@ bool LootStoreItem::Roll(bool rate, Player const* player, Loot& loot, LootStore return roll_chance_f(_chance * (rate ? sWorld->getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f)); ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(itemid); - - float qualityModifier = pProto && rate ? sWorld->getRate(qualityToRate[pProto->Quality]) : 1.0f; + float qualityModifier = 1.0f; + if (pProto && pProto->Quality < ITEM_QUALITY_HEIRLOOM && rate) + qualityModifier = sWorld->getRate(qualityToRate[pProto->Quality]); return roll_chance_f(_chance * qualityModifier); } diff --git a/src/server/game/Warden/WardenWin.cpp b/src/server/game/Warden/WardenWin.cpp index 64895f93e..520dd6d01 100644 --- a/src/server/game/Warden/WardenWin.cpp +++ b/src/server/game/Warden/WardenWin.cpp @@ -88,7 +88,7 @@ static uint16 GetCheckPacketSize(WardenCheck const* check) } // Returns config id for specific type id -static WorldIntConfigs GetMaxWardenChecksForType(uint8 type) +static ServerConfigs GetMaxWardenChecksForType(uint8 type) { // Should never be higher type than defined ASSERT(type < MAX_WARDEN_CHECK_TYPES); diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index 08f892eab..1ec85b71c 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -23,6 +23,7 @@ #include "Duration.h" #include "ObjectGuid.h" #include "SharedDefines.h" +#include "WorldConfig.h" #include class WorldPacket; @@ -58,479 +59,6 @@ enum ServerMessageType SERVER_MSG_RESTART_CANCELLED = 5 }; -/// Configuration elements -enum WorldBoolConfigs -{ - CONFIG_DURABILITY_LOSS_IN_PVP = 0, - CONFIG_ADDON_CHANNEL, - CONFIG_ALLOW_PLAYER_COMMANDS, - CONFIG_CLEAN_CHARACTER_DB, - CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, - CONFIG_ALLOW_TWO_SIDE_ACCOUNTS, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, - CONFIG_ALLOW_TWO_SIDE_WHO_LIST, - CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND, - CONFIG_ALLOW_TWO_SIDE_TRADE, - CONFIG_ALL_TAXI_PATHS, - CONFIG_INSTANCE_IGNORE_LEVEL, - CONFIG_INSTANCE_IGNORE_RAID, - CONFIG_INSTANCE_GMSUMMON_PLAYER, - CONFIG_INSTANCE_SHARED_ID, - CONFIG_ALLOW_GM_GROUP, - CONFIG_ALLOW_GM_FRIEND, - CONFIG_GM_LOWER_SECURITY, - CONFIG_SKILL_PROSPECTING, - CONFIG_SKILL_MILLING, - CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY, - CONFIG_WEATHER, - CONFIG_ALWAYS_MAX_SKILL_FOR_LEVEL, - CONFIG_QUEST_IGNORE_RAID, - CONFIG_DETECT_POS_COLLISION, - CONFIG_RESTRICTED_LFG_CHANNEL, - CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL, - CONFIG_TALENTS_INSPECTING, - CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, - CONFIG_CHAT_MUTE_FIRST_LOGIN, - CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP, - CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE, - CONFIG_DEATH_BONES_WORLD, - CONFIG_DEATH_BONES_BG_OR_ARENA, - CONFIG_DIE_COMMAND_MODE, - CONFIG_DECLINED_NAMES_USED, - CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG, - CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG, - CONFIG_BATTLEGROUND_CAST_DESERTER, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED, - CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE, - CONFIG_BATTLEGROUND_TRACK_DESERTERS, - CONFIG_BG_XP_FOR_KILL, - CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, - CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE, - CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY, - CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN, - CONFIG_VMAP_INDOOR_CHECK, - CONFIG_PET_LOS, - CONFIG_START_CUSTOM_SPELLS, - CONFIG_START_ALL_EXPLORED, - CONFIG_START_ALL_REP, - CONFIG_ALWAYS_MAXSKILL, - CONFIG_PVP_TOKEN_ENABLE, - CONFIG_NO_RESET_TALENT_COST, - CONFIG_SHOW_KICK_IN_WORLD, - CONFIG_SHOW_MUTE_IN_WORLD, - CONFIG_SHOW_BAN_IN_WORLD, - CONFIG_AUTOBROADCAST, - CONFIG_ALLOW_TICKETS, - CONFIG_DELETE_CHARACTER_TICKET_TRACE, - CONFIG_LFG_CAST_DESERTER, - CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, - CONFIG_PRESERVE_CUSTOM_CHANNELS, - CONFIG_PDUMP_NO_PATHS, - CONFIG_PDUMP_NO_OVERWRITE, - CONFIG_ENABLE_MMAPS, // pussywizard - CONFIG_ENABLE_LOGIN_AFTER_DC, // pussywizard - CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS, // pussywizard - CONFIG_QUEST_IGNORE_AUTO_ACCEPT, - CONFIG_QUEST_IGNORE_AUTO_COMPLETE, - CONFIG_QUEST_ENABLE_QUEST_TRACKER, - CONFIG_WARDEN_ENABLED, - CONFIG_ENABLE_CONTINENT_TRANSPORT, - CONFIG_ENABLE_CONTINENT_TRANSPORT_PRELOADING, - CONFIG_MINIGOB_MANABONK, - CONFIG_IP_BASED_ACTION_LOGGING, - CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA, - CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA, - CONFIG_CHECK_GOBJECT_LOS, - CONFIG_CLOSE_IDLE_CONNECTIONS, - CONFIG_LFG_LOCATION_ALL, // Player can join LFG anywhere - CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS, - CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE, - CONFIG_ITEMDELETE_METHOD, - CONFIG_ITEMDELETE_VENDOR, - CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE, - CONFIG_DEBUG_BATTLEGROUND, - CONFIG_DEBUG_ARENA, - CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PORTAL_CHECK_ILVL, - CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE, - CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID, - CONFIG_SET_BOP_ITEM_TRADEABLE, - CONFIG_ALLOW_LOGGING_IP_ADDRESSES_IN_DATABASE, - CONFIG_REALM_LOGIN_ENABLED, - CONFIG_PLAYER_SETTINGS_ENABLED, - CONFIG_ALLOW_JOIN_BG_AND_LFG, - CONFIG_MISS_CHANCE_MULTIPLIER_ONLY_FOR_PLAYERS, - CONFIG_LEAVE_GROUP_ON_LOGOUT, - CONFIG_QUEST_POI_ENABLED, - CONFIG_VMAP_BLIZZLIKE_PVP_LOS, - CONFIG_VMAP_BLIZZLIKE_LOS_OPEN_WORLD, - CONFIG_OBJECT_SPARKLES, - CONFIG_LOW_LEVEL_REGEN_BOOST, - CONFIG_OBJECT_QUEST_MARKERS, - CONFIG_STRICT_NAMES_RESERVED, - CONFIG_STRICT_NAMES_PROFANITY, - CONFIG_ALLOWS_RANK_MOD_FOR_PET_HEALTH, - CONFIG_MUNCHING_BLIZZLIKE, - CONFIG_ENABLE_DAZE, - CONFIG_SPELL_QUEUE_ENABLED, - BOOL_CONFIG_VALUE_COUNT -}; - -enum WorldFloatConfigs -{ - CONFIG_GROUP_XP_DISTANCE = 0, - CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE, - CONFIG_SIGHT_MONSTER, - CONFIG_LISTEN_RANGE_SAY, - CONFIG_LISTEN_RANGE_TEXTEMOTE, - CONFIG_LISTEN_RANGE_YELL, - CONFIG_CREATURE_LEASH_RADIUS, - CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS, - CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS, - CONFIG_CHANCE_OF_GM_SURVEY, - CONFIG_ARENA_WIN_RATING_MODIFIER_1, - CONFIG_ARENA_WIN_RATING_MODIFIER_2, - CONFIG_ARENA_LOSE_RATING_MODIFIER, - CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER, - CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT, - CONFIG_RESPAWN_DYNAMICRATE_CREATURE, - FLOAT_CONFIG_VALUE_COUNT -}; - -enum WorldIntConfigs -{ - CONFIG_COMPRESSION = 0, - CONFIG_INTERVAL_MAPUPDATE, - CONFIG_INTERVAL_CHANGEWEATHER, - CONFIG_INTERVAL_DISCONNECT_TOLERANCE, - CONFIG_INTERVAL_SAVE, - CONFIG_PORT_WORLD, - CONFIG_SOCKET_TIMEOUTTIME, - CONFIG_SESSION_ADD_DELAY, - CONFIG_GAME_TYPE, - CONFIG_REALM_ZONE, - CONFIG_STRICT_PLAYER_NAMES, - CONFIG_STRICT_CHARTER_NAMES, - CONFIG_STRICT_CHANNEL_NAMES, - CONFIG_STRICT_PET_NAMES, - CONFIG_MIN_PLAYER_NAME, - CONFIG_MIN_CHARTER_NAME, - CONFIG_MIN_PET_NAME, - CONFIG_CHARACTER_CREATING_DISABLED, - CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK, - CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK, - CONFIG_CHARACTERS_PER_ACCOUNT, - CONFIG_CHARACTERS_PER_REALM, - CONFIG_CREATURE_STOP_FOR_PLAYER, - CONFIG_HEROIC_CHARACTERS_PER_REALM, - CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER, - CONFIG_SKIP_CINEMATICS, - CONFIG_MAX_PLAYER_LEVEL, - CONFIG_MIN_DUALSPEC_LEVEL, - CONFIG_START_PLAYER_LEVEL, - CONFIG_START_HEROIC_PLAYER_LEVEL, - CONFIG_START_PLAYER_MONEY, - CONFIG_START_HEROIC_PLAYER_MONEY, - CONFIG_MAX_HONOR_POINTS, - CONFIG_MAX_HONOR_POINTS_MONEY_PER_POINT, - CONFIG_START_HONOR_POINTS, - CONFIG_MAX_ARENA_POINTS, - CONFIG_START_ARENA_POINTS, - CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL, - CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL_DIFFERENCE, - CONFIG_INSTANCE_RESET_TIME_HOUR, - CONFIG_INSTANCE_RESET_TIME_RELATIVE_TIMESTAMP, - CONFIG_INSTANCE_UNLOAD_DELAY, - CONFIG_MAX_PRIMARY_TRADE_SKILL, - CONFIG_MIN_PETITION_SIGNS, - CONFIG_GM_LOGIN_STATE, - CONFIG_GM_VISIBLE_STATE, - CONFIG_GM_ACCEPT_TICKETS, - CONFIG_GM_CHAT, - CONFIG_GM_WHISPERING_TO, - CONFIG_GM_LEVEL_IN_GM_LIST, - CONFIG_GM_LEVEL_IN_WHO_LIST, - CONFIG_START_GM_LEVEL, - CONFIG_GROUP_VISIBILITY, - CONFIG_MAIL_DELIVERY_DELAY, - CONFIG_UPTIME_UPDATE, - CONFIG_SKILL_CHANCE_ORANGE, - CONFIG_SKILL_CHANCE_YELLOW, - CONFIG_SKILL_CHANCE_GREEN, - CONFIG_SKILL_CHANCE_GREY, - CONFIG_SKILL_CHANCE_MINING_STEPS, - CONFIG_SKILL_CHANCE_SKINNING_STEPS, - CONFIG_SKILL_GAIN_CRAFTING, - CONFIG_SKILL_GAIN_DEFENSE, - CONFIG_SKILL_GAIN_GATHERING, - CONFIG_SKILL_GAIN_WEAPON, - CONFIG_MAX_OVERSPEED_PINGS, - CONFIG_EXPANSION, - CONFIG_CHATFLOOD_MESSAGE_COUNT, - CONFIG_CHATFLOOD_MESSAGE_DELAY, - CONFIG_CHATFLOOD_ADDON_MESSAGE_COUNT, - CONFIG_CHATFLOOD_ADDON_MESSAGE_DELAY, - CONFIG_CHATFLOOD_MUTE_TIME, - CONFIG_EVENT_ANNOUNCE, - CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY, - CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD, - CONFIG_CREATURE_FAMILY_FLEE_DELAY, - CONFIG_WORLD_BOSS_LEVEL_DIFF, - CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF, - CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF, - CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY, - CONFIG_CHAT_STRICT_LINK_CHECKING_KICK, - CONFIG_CHAT_CHANNEL_LEVEL_REQ, - CONFIG_CHAT_WHISPER_LEVEL_REQ, - CONFIG_CHAT_SAY_LEVEL_REQ, - CONFIG_PARTY_LEVEL_REQ, - CONFIG_CHAT_TIME_MUTE_FIRST_LOGIN, - CONFIG_TRADE_LEVEL_REQ, - CONFIG_TICKET_LEVEL_REQ, - CONFIG_AUCTION_LEVEL_REQ, - CONFIG_MAIL_LEVEL_REQ, - CONFIG_CORPSE_DECAY_NORMAL, - CONFIG_CORPSE_DECAY_RARE, - CONFIG_CORPSE_DECAY_ELITE, - CONFIG_CORPSE_DECAY_RAREELITE, - CONFIG_CORPSE_DECAY_WORLDBOSS, - CONFIG_DEATH_SICKNESS_LEVEL, - CONFIG_INSTANT_LOGOUT, - CONFIG_DISABLE_BREATHING, - CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER, - CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, - CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, - CONFIG_BATTLEGROUND_REPORT_AFK_TIMER, - CONFIG_BATTLEGROUND_REPORT_AFK, - CONFIG_BATTLEGROUND_INVITATION_TYPE, - CONFIG_BATTLEGROUND_PLAYER_RESPAWN, - CONFIG_BATTLEGROUND_RESTORATION_BUFF_RESPAWN, - CONFIG_BATTLEGROUND_BERSERKING_BUFF_RESPAWN, - CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_LEVEL, - CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS, - CONFIG_BATTLEGROUND_WARSONG_FLAGS, - CONFIG_BATTLEGROUND_ARATHI_CAPTUREPOINTS, - CONFIG_BATTLEGROUND_ALTERAC_REINFORCEMENTS, - CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH, - CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS, - CONFIG_WINTERGRASP_ENABLE, - CONFIG_ARENA_MAX_RATING_DIFFERENCE, - CONFIG_ARENA_RATING_DISCARD_TIMER, - CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, - CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, - CONFIG_ARENA_GAMES_REQUIRED, - CONFIG_ARENA_START_RATING, - CONFIG_LEGACY_ARENA_POINTS_CALC, - CONFIG_ARENA_START_PERSONAL_RATING, - CONFIG_ARENA_START_MATCHMAKER_RATING, - CONFIG_ARENA_QUEUE_ANNOUNCER_DETAIL, - CONFIG_HONOR_AFTER_DUEL, - CONFIG_PVP_TOKEN_MAP_TYPE, - CONFIG_PVP_TOKEN_ID, - CONFIG_PVP_TOKEN_COUNT, - CONFIG_ENABLE_SINFO_LOGIN, - CONFIG_PLAYER_ALLOW_COMMANDS, - CONFIG_NUMTHREADS, - CONFIG_LOGDB_CLEARINTERVAL, - CONFIG_LOGDB_CLEARTIME, - CONFIG_TELEPORT_TIMEOUT_NEAR, // pussywizard - CONFIG_TELEPORT_TIMEOUT_FAR, // pussywizard - CONFIG_MAX_ALLOWED_MMR_DROP, // pussywizard - CONFIG_CLIENTCACHE_VERSION, - CONFIG_GUILD_EVENT_LOG_COUNT, - CONFIG_GUILD_BANK_EVENT_LOG_COUNT, - CONFIG_MIN_LEVEL_STAT_SAVE, - CONFIG_RANDOM_BG_RESET_HOUR, - CONFIG_CALENDAR_DELETE_OLD_EVENTS_HOUR, - CONFIG_GUILD_RESET_HOUR, - CONFIG_CHARDELETE_KEEP_DAYS, - CONFIG_CHARDELETE_METHOD, - CONFIG_CHARDELETE_MIN_LEVEL, - CONFIG_AUTOBROADCAST_CENTER, - CONFIG_AUTOBROADCAST_INTERVAL, - CONFIG_AUTOBROADCAST_MIN_LEVEL_DISABLE, - CONFIG_MAX_RESULTS_LOOKUP_COMMANDS, - CONFIG_DB_PING_INTERVAL, - CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION, - CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS, - CONFIG_LFG_OPTIONSMASK, - CONFIG_MAX_INSTANCES_PER_HOUR, - CONFIG_WINTERGRASP_PLR_MAX, - CONFIG_WINTERGRASP_PLR_MIN, - CONFIG_WINTERGRASP_PLR_MIN_LVL, - CONFIG_WINTERGRASP_BATTLETIME, - CONFIG_WINTERGRASP_NOBATTLETIME, - CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, - CONFIG_PACKET_SPOOF_BANMODE, - CONFIG_PACKET_SPOOF_BANDURATION, - CONFIG_WARDEN_CLIENT_RESPONSE_DELAY, - CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF, - CONFIG_WARDEN_CLIENT_FAIL_ACTION, - CONFIG_WARDEN_CLIENT_BAN_DURATION, - CONFIG_WARDEN_NUM_MEM_CHECKS, - CONFIG_WARDEN_NUM_LUA_CHECKS, - CONFIG_WARDEN_NUM_OTHER_CHECKS, - CONFIG_BIRTHDAY_TIME, - CONFIG_SOCKET_TIMEOUTTIME_ACTIVE, - CONFIG_INSTANT_TAXI, - CONFIG_AFK_PREVENT_LOGOUT, - CONFIG_ICC_BUFF_HORDE, - CONFIG_ICC_BUFF_ALLIANCE, - CONFIG_ITEMDELETE_QUALITY, - CONFIG_ITEMDELETE_ITEM_LEVEL, - CONFIG_ITEMDELETE_KEEP_DAYS, - CONFIG_BG_REWARD_WINNER_HONOR_FIRST, - CONFIG_BG_REWARD_WINNER_ARENA_FIRST, - CONFIG_BG_REWARD_WINNER_HONOR_LAST, - CONFIG_BG_REWARD_WINNER_ARENA_LAST, - CONFIG_BG_REWARD_LOSER_HONOR_FIRST, - CONFIG_BG_REWARD_LOSER_HONOR_LAST, - CONFIG_CHARTER_COST_GUILD, - CONFIG_CHARTER_COST_ARENA_2v2, - CONFIG_CHARTER_COST_ARENA_3v3, - CONFIG_CHARTER_COST_ARENA_5v5, - CONFIG_MAX_WHO_LIST_RETURN, - CONFIG_WAYPOINT_MOVEMENT_STOP_TIME_FOR_PLAYER, - CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PRINT_MODE, - CONFIG_DUNGEON_ACCESS_REQUIREMENTS_OPTIONAL_STRING_ID, - CONFIG_GUILD_BANK_INITIAL_TABS, - CONFIG_GUILD_BANK_TAB_COST_0, - CONFIG_GUILD_BANK_TAB_COST_1, - CONFIG_GUILD_BANK_TAB_COST_2, - CONFIG_GUILD_BANK_TAB_COST_3, - CONFIG_GUILD_BANK_TAB_COST_4, - CONFIG_GUILD_BANK_TAB_COST_5, - CONFIG_GUILD_MEMBER_LIMIT, - CONFIG_GM_LEVEL_CHANNEL_MODERATION, - CONFIG_TOGGLE_XP_COST, - CONFIG_NPC_EVADE_IF_NOT_REACHABLE, - CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID, - CONFIG_FFA_PVP_TIMER, - CONFIG_LOOT_NEED_BEFORE_GREED_ILVL_RESTRICTION, - CONFIG_LFG_MAX_KICK_COUNT, - CONFIG_LFG_KICK_PREVENTION_TIMER, - CONFIG_CHANGE_FACTION_MAX_MONEY, - CONFIG_WATER_BREATH_TIMER, - CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD, - CONFIG_AUCTIONHOUSE_WORKERTHREADS, - CONFIG_SPELL_QUEUE_WINDOW, - CONFIG_SUNSREACH_COUNTER_MAX, - CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT, - CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE, - INT_CONFIG_VALUE_COUNT -}; - -/// Server rates -enum Rates -{ - RATE_HEALTH = 0, - RATE_POWER_MANA, - RATE_POWER_RAGE_INCOME, - RATE_POWER_RAGE_LOSS, - RATE_POWER_RUNICPOWER_INCOME, - RATE_POWER_RUNICPOWER_LOSS, - RATE_POWER_FOCUS, - RATE_POWER_ENERGY, - RATE_SKILL_DISCOVERY, - RATE_DROP_ITEM_POOR, - RATE_DROP_ITEM_NORMAL, - RATE_DROP_ITEM_UNCOMMON, - RATE_DROP_ITEM_RARE, - RATE_DROP_ITEM_EPIC, - RATE_DROP_ITEM_LEGENDARY, - RATE_DROP_ITEM_ARTIFACT, - RATE_DROP_ITEM_REFERENCED, - RATE_DROP_ITEM_REFERENCED_AMOUNT, - RATE_DROP_ITEM_GROUP_AMOUNT, - RATE_SELLVALUE_ITEM_POOR, - RATE_SELLVALUE_ITEM_NORMAL, - RATE_SELLVALUE_ITEM_UNCOMMON, - RATE_SELLVALUE_ITEM_RARE, - RATE_SELLVALUE_ITEM_EPIC, - RATE_SELLVALUE_ITEM_LEGENDARY, - RATE_SELLVALUE_ITEM_ARTIFACT, - RATE_SELLVALUE_ITEM_HEIRLOOM, - RATE_BUYVALUE_ITEM_POOR, - RATE_BUYVALUE_ITEM_NORMAL, - RATE_BUYVALUE_ITEM_UNCOMMON, - RATE_BUYVALUE_ITEM_RARE, - RATE_BUYVALUE_ITEM_EPIC, - RATE_BUYVALUE_ITEM_LEGENDARY, - RATE_BUYVALUE_ITEM_ARTIFACT, - RATE_BUYVALUE_ITEM_HEIRLOOM, - RATE_DROP_MONEY, - RATE_REWARD_QUEST_MONEY, - RATE_REWARD_BONUS_MONEY, - RATE_XP_KILL, - RATE_XP_BG_KILL_AV, - RATE_XP_BG_KILL_WSG, - RATE_XP_BG_KILL_AB, - RATE_XP_BG_KILL_EOTS, - RATE_XP_BG_KILL_SOTA, - RATE_XP_BG_KILL_IC, - RATE_XP_QUEST, - RATE_XP_QUEST_DF, - RATE_XP_EXPLORE, - RATE_XP_PET, - RATE_XP_PET_NEXT_LEVEL, - RATE_REPAIRCOST, - RATE_REPUTATION_GAIN, - RATE_REPUTATION_LOWLEVEL_KILL, - RATE_REPUTATION_LOWLEVEL_QUEST, - RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS, - RATE_CREATURE_NORMAL_HP, - RATE_CREATURE_ELITE_ELITE_HP, - RATE_CREATURE_ELITE_RAREELITE_HP, - RATE_CREATURE_ELITE_WORLDBOSS_HP, - RATE_CREATURE_ELITE_RARE_HP, - RATE_CREATURE_NORMAL_DAMAGE, - RATE_CREATURE_ELITE_ELITE_DAMAGE, - RATE_CREATURE_ELITE_RAREELITE_DAMAGE, - RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE, - RATE_CREATURE_ELITE_RARE_DAMAGE, - RATE_CREATURE_NORMAL_SPELLDAMAGE, - RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE, - RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE, - RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE, - RATE_CREATURE_ELITE_RARE_SPELLDAMAGE, - RATE_CREATURE_AGGRO, - RATE_REST_INGAME, - RATE_REST_OFFLINE_IN_TAVERN_OR_CITY, - RATE_REST_OFFLINE_IN_WILDERNESS, - RATE_REST_MAX_BONUS, - RATE_DAMAGE_FALL, - RATE_AUCTION_TIME, - RATE_AUCTION_DEPOSIT, - RATE_AUCTION_CUT, - RATE_HONOR, - RATE_ARENA_POINTS, - RATE_TALENT, - RATE_TALENT_PET, - RATE_CORPSE_DECAY_LOOTED, - RATE_INSTANCE_RESET_TIME, - RATE_DURABILITY_LOSS_ON_DEATH, - RATE_DURABILITY_LOSS_DAMAGE, - RATE_DURABILITY_LOSS_PARRY, - RATE_DURABILITY_LOSS_ABSORB, - RATE_DURABILITY_LOSS_BLOCK, - RATE_MOVESPEED_PLAYER, - RATE_MOVESPEED_NPC, - RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE, - RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER, - MAX_RATES -}; - class IWorld { public: @@ -542,8 +70,6 @@ public: virtual void LoadDBAllowedSecurityLevel() = 0; [[nodiscard]] virtual bool getAllowMovement() const = 0; virtual void SetAllowMovement(bool allow) = 0; - virtual void SetNewCharString(std::string const& str) = 0; - [[nodiscard]] virtual std::string const& GetNewCharString() const = 0; [[nodiscard]] virtual LocaleConstant GetDefaultDbcLocale() const = 0; [[nodiscard]] virtual std::string const& GetDataPath() const = 0; [[nodiscard]] virtual Seconds GetNextDailyQuestsResetTime() const = 0; @@ -558,14 +84,16 @@ public: virtual void ShutdownCancel() = 0; virtual void ShutdownMsg(bool show = false, Player* player = nullptr, const std::string& reason = std::string()) = 0; virtual void Update(uint32 diff) = 0; - virtual void setRate(Rates rate, float value) = 0; - [[nodiscard]] virtual float getRate(Rates rate) const = 0; - virtual void setBoolConfig(WorldBoolConfigs index, bool value) = 0; - [[nodiscard]] virtual bool getBoolConfig(WorldBoolConfigs index) const = 0; - virtual void setFloatConfig(WorldFloatConfigs index, float value) = 0; - [[nodiscard]] virtual float getFloatConfig(WorldFloatConfigs index) const = 0; - virtual void setIntConfig(WorldIntConfigs index, uint32 value) = 0; - [[nodiscard]] virtual uint32 getIntConfig(WorldIntConfigs index) const = 0; + virtual void setRate(ServerConfigs index, float value) = 0; + [[nodiscard]] virtual float getRate(ServerConfigs index) const = 0; + virtual void setBoolConfig(ServerConfigs index, bool value) = 0; + [[nodiscard]] virtual bool getBoolConfig(ServerConfigs index) const = 0; + virtual void setFloatConfig(ServerConfigs index, float value) = 0; + [[nodiscard]] virtual float getFloatConfig(ServerConfigs index) const = 0; + virtual void setIntConfig(ServerConfigs index, uint32 value) = 0; + [[nodiscard]] virtual uint32 getIntConfig(ServerConfigs index) const = 0; + virtual void setStringConfig(ServerConfigs index, std::string const& value) = 0; + virtual std::string_view getStringConfig(ServerConfigs index) const = 0; [[nodiscard]] virtual bool IsPvPRealm() const = 0; [[nodiscard]] virtual bool IsFFAPvPRealm() const = 0; virtual uint32 GetNextWhoListUpdateDelaySecs() = 0; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 00ffbf428..0ef09fdf2 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -127,11 +127,7 @@ World::World() _mail_expire_check_timer = 0s; _isClosed = false; _cleaningFlags = 0; - - memset(_rate_values, 0, sizeof(_rate_values)); - memset(_int_configs, 0, sizeof(_int_configs)); - memset(_bool_configs, 0, sizeof(_bool_configs)); - memset(_float_configs, 0, sizeof(_float_configs)); + _dbClientCacheVersion = 0; } /// World destructor @@ -191,802 +187,34 @@ void World::LoadConfigSettings(bool reload) if (!reload) sWorldSessionMgr->SetPlayerAmountLimit(sConfigMgr->GetOption("PlayerLimit", 1000)); - ///- Read ticket system setting from the config file - _bool_configs[CONFIG_ALLOW_TICKETS] = sConfigMgr->GetOption("AllowTickets", true); - _bool_configs[CONFIG_DELETE_CHARACTER_TICKET_TRACE] = sConfigMgr->GetOption("DeletedCharacterTicketTrace", false); + _worldConfig.Initialize(reload); - ///- Get string for new logins (newly created characters) - SetNewCharString(sConfigMgr->GetOption("PlayerStart.String", "")); + for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) + playerBaseMoveSpeed[i] = baseMoveSpeed[i] * getRate(RATE_MOVESPEED_PLAYER); - ///- Send server info on login? - _int_configs[CONFIG_ENABLE_SINFO_LOGIN] = sConfigMgr->GetOption("Server.LoginInfo", 0); - - ///- Read all rates from the config file - _rate_values[RATE_HEALTH] = sConfigMgr->GetOption("Rate.Health", 1); - if (_rate_values[RATE_HEALTH] < 0) - { - LOG_ERROR("server.loading", "Rate.Health ({}) must be > 0. Using 1 instead.", _rate_values[RATE_HEALTH]); - _rate_values[RATE_HEALTH] = 1; - } - _rate_values[RATE_POWER_MANA] = sConfigMgr->GetOption("Rate.Mana", 1); - if (_rate_values[RATE_POWER_MANA] < 0) - { - LOG_ERROR("server.loading", "Rate.Mana ({}) must be > 0. Using 1 instead.", _rate_values[RATE_POWER_MANA]); - _rate_values[RATE_POWER_MANA] = 1; - } - _rate_values[RATE_POWER_RAGE_INCOME] = sConfigMgr->GetOption("Rate.Rage.Income", 1); - _rate_values[RATE_POWER_RAGE_LOSS] = sConfigMgr->GetOption("Rate.Rage.Loss", 1); - if (_rate_values[RATE_POWER_RAGE_LOSS] < 0) - { - LOG_ERROR("server.loading", "Rate.Rage.Loss ({}) must be > 0. Using 1 instead.", _rate_values[RATE_POWER_RAGE_LOSS]); - _rate_values[RATE_POWER_RAGE_LOSS] = 1; - } - _rate_values[RATE_POWER_RUNICPOWER_INCOME] = sConfigMgr->GetOption("Rate.RunicPower.Income", 1); - _rate_values[RATE_POWER_RUNICPOWER_LOSS] = sConfigMgr->GetOption("Rate.RunicPower.Loss", 1); - if (_rate_values[RATE_POWER_RUNICPOWER_LOSS] < 0) - { - LOG_ERROR("server.loading", "Rate.RunicPower.Loss ({}) must be > 0. Using 1 instead.", _rate_values[RATE_POWER_RUNICPOWER_LOSS]); - _rate_values[RATE_POWER_RUNICPOWER_LOSS] = 1; - } - _rate_values[RATE_POWER_FOCUS] = sConfigMgr->GetOption("Rate.Focus", 1.0f); - _rate_values[RATE_POWER_ENERGY] = sConfigMgr->GetOption("Rate.Energy", 1.0f); - - _rate_values[RATE_SKILL_DISCOVERY] = sConfigMgr->GetOption("Rate.Skill.Discovery", 1.0f); - - _rate_values[RATE_DROP_ITEM_POOR] = sConfigMgr->GetOption("Rate.Drop.Item.Poor", 1.0f); - _rate_values[RATE_DROP_ITEM_NORMAL] = sConfigMgr->GetOption("Rate.Drop.Item.Normal", 1.0f); - _rate_values[RATE_DROP_ITEM_UNCOMMON] = sConfigMgr->GetOption("Rate.Drop.Item.Uncommon", 1.0f); - _rate_values[RATE_DROP_ITEM_RARE] = sConfigMgr->GetOption("Rate.Drop.Item.Rare", 1.0f); - _rate_values[RATE_DROP_ITEM_EPIC] = sConfigMgr->GetOption("Rate.Drop.Item.Epic", 1.0f); - _rate_values[RATE_DROP_ITEM_LEGENDARY] = sConfigMgr->GetOption("Rate.Drop.Item.Legendary", 1.0f); - _rate_values[RATE_DROP_ITEM_ARTIFACT] = sConfigMgr->GetOption("Rate.Drop.Item.Artifact", 1.0f); - _rate_values[RATE_DROP_ITEM_REFERENCED] = sConfigMgr->GetOption("Rate.Drop.Item.Referenced", 1.0f); - _rate_values[RATE_DROP_ITEM_REFERENCED_AMOUNT] = sConfigMgr->GetOption("Rate.Drop.Item.ReferencedAmount", 1.0f); - _rate_values[RATE_DROP_ITEM_GROUP_AMOUNT] = sConfigMgr->GetOption("Rate.Drop.Item.GroupAmount", 1.0f); - _rate_values[RATE_DROP_MONEY] = sConfigMgr->GetOption("Rate.Drop.Money", 1.0f); - - _rate_values[RATE_REWARD_QUEST_MONEY] = sConfigMgr->GetOption("Rate.RewardQuestMoney", 1.0f); - _rate_values[RATE_REWARD_BONUS_MONEY] = sConfigMgr->GetOption("Rate.RewardBonusMoney", 1.0f); - _rate_values[RATE_XP_KILL] = sConfigMgr->GetOption("Rate.XP.Kill", 1.0f); - _rate_values[RATE_XP_BG_KILL_AV] = sConfigMgr->GetOption("Rate.XP.BattlegroundKillAV", 1.0f); - _rate_values[RATE_XP_BG_KILL_WSG] = sConfigMgr->GetOption("Rate.XP.BattlegroundKillWSG", 1.0f); - _rate_values[RATE_XP_BG_KILL_AB] = sConfigMgr->GetOption("Rate.XP.BattlegroundKillAB", 1.0f); - _rate_values[RATE_XP_BG_KILL_EOTS] = sConfigMgr->GetOption("Rate.XP.BattlegroundKillEOTS", 1.0f); - _rate_values[RATE_XP_BG_KILL_SOTA] = sConfigMgr->GetOption("Rate.XP.BattlegroundKillSOTA", 1.0f); - _rate_values[RATE_XP_BG_KILL_IC] = sConfigMgr->GetOption("Rate.XP.BattlegroundKillIC", 1.0f); - _rate_values[RATE_XP_QUEST] = sConfigMgr->GetOption("Rate.XP.Quest", 1.0f); - _rate_values[RATE_XP_QUEST_DF] = sConfigMgr->GetOption("Rate.XP.Quest.DF", 1.0f); - _rate_values[RATE_XP_EXPLORE] = sConfigMgr->GetOption("Rate.XP.Explore", 1.0f); - _rate_values[RATE_XP_PET] = sConfigMgr->GetOption("Rate.XP.Pet", 1.0f); - _rate_values[RATE_XP_PET_NEXT_LEVEL] = sConfigMgr->GetOption("Rate.Pet.LevelXP", 0.05f); - _rate_values[RATE_REPAIRCOST] = sConfigMgr->GetOption("Rate.RepairCost", 1.0f); - - _rate_values[RATE_SELLVALUE_ITEM_POOR] = sConfigMgr->GetOption("Rate.SellValue.Item.Poor", 1.0f); - _rate_values[RATE_SELLVALUE_ITEM_NORMAL] = sConfigMgr->GetOption("Rate.SellValue.Item.Normal", 1.0f); - _rate_values[RATE_SELLVALUE_ITEM_UNCOMMON] = sConfigMgr->GetOption("Rate.SellValue.Item.Uncommon", 1.0f); - _rate_values[RATE_SELLVALUE_ITEM_RARE] = sConfigMgr->GetOption("Rate.SellValue.Item.Rare", 1.0f); - _rate_values[RATE_SELLVALUE_ITEM_EPIC] = sConfigMgr->GetOption("Rate.SellValue.Item.Epic", 1.0f); - _rate_values[RATE_SELLVALUE_ITEM_LEGENDARY] = sConfigMgr->GetOption("Rate.SellValue.Item.Legendary", 1.0f); - _rate_values[RATE_SELLVALUE_ITEM_ARTIFACT] = sConfigMgr->GetOption("Rate.SellValue.Item.Artifact", 1.0f); - _rate_values[RATE_SELLVALUE_ITEM_HEIRLOOM] = sConfigMgr->GetOption("Rate.SellValue.Item.Heirloom", 1.0f); - - _rate_values[ RATE_BUYVALUE_ITEM_POOR] = sConfigMgr->GetOption("Rate.BuyValue.Item.Poor", 1.0f); - _rate_values[ RATE_BUYVALUE_ITEM_NORMAL] = sConfigMgr->GetOption("Rate.BuyValue.Item.Normal", 1.0f); - _rate_values[ RATE_BUYVALUE_ITEM_UNCOMMON] = sConfigMgr->GetOption("Rate.BuyValue.Item.Uncommon", 1.0f); - _rate_values[ RATE_BUYVALUE_ITEM_RARE] = sConfigMgr->GetOption("Rate.BuyValue.Item.Rare", 1.0f); - _rate_values[ RATE_BUYVALUE_ITEM_EPIC] = sConfigMgr->GetOption("Rate.BuyValue.Item.Epic", 1.0f); - _rate_values[ RATE_BUYVALUE_ITEM_LEGENDARY] = sConfigMgr->GetOption("Rate.BuyValue.Item.Legendary", 1.0f); - _rate_values[RATE_BUYVALUE_ITEM_ARTIFACT] = sConfigMgr->GetOption("Rate.BuyValue.Item.Artifact", 1.0f); - _rate_values[RATE_BUYVALUE_ITEM_HEIRLOOM] = sConfigMgr->GetOption("Rate.BuyValue.Item.Heirloom", 1.0f); - - if (_rate_values[RATE_REPAIRCOST] < 0.0f) - { - LOG_ERROR("server.loading", "Rate.RepairCost ({}) must be >=0. Using 0.0 instead.", _rate_values[RATE_REPAIRCOST]); - _rate_values[RATE_REPAIRCOST] = 0.0f; - } - _rate_values[RATE_REPUTATION_GAIN] = sConfigMgr->GetOption("Rate.Reputation.Gain", 1.0f); - _rate_values[RATE_REPUTATION_LOWLEVEL_KILL] = sConfigMgr->GetOption("Rate.Reputation.LowLevel.Kill", 1.0f); - _rate_values[RATE_REPUTATION_LOWLEVEL_QUEST] = sConfigMgr->GetOption("Rate.Reputation.LowLevel.Quest", 1.0f); - _rate_values[RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS] = sConfigMgr->GetOption("Rate.Reputation.RecruitAFriendBonus", 0.1f); - _rate_values[RATE_CREATURE_NORMAL_DAMAGE] = sConfigMgr->GetOption("Rate.Creature.Normal.Damage", 1.0f); - _rate_values[RATE_CREATURE_ELITE_ELITE_DAMAGE] = sConfigMgr->GetOption("Rate.Creature.Elite.Elite.Damage", 1.0f); - _rate_values[RATE_CREATURE_ELITE_RAREELITE_DAMAGE] = sConfigMgr->GetOption("Rate.Creature.Elite.RAREELITE.Damage", 1.0f); - _rate_values[RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE] = sConfigMgr->GetOption("Rate.Creature.Elite.WORLDBOSS.Damage", 1.0f); - _rate_values[RATE_CREATURE_ELITE_RARE_DAMAGE] = sConfigMgr->GetOption("Rate.Creature.Elite.RARE.Damage", 1.0f); - _rate_values[RATE_CREATURE_NORMAL_HP] = sConfigMgr->GetOption("Rate.Creature.Normal.HP", 1.0f); - _rate_values[RATE_CREATURE_ELITE_ELITE_HP] = sConfigMgr->GetOption("Rate.Creature.Elite.Elite.HP", 1.0f); - _rate_values[RATE_CREATURE_ELITE_RAREELITE_HP] = sConfigMgr->GetOption("Rate.Creature.Elite.RAREELITE.HP", 1.0f); - _rate_values[RATE_CREATURE_ELITE_WORLDBOSS_HP] = sConfigMgr->GetOption("Rate.Creature.Elite.WORLDBOSS.HP", 1.0f); - _rate_values[RATE_CREATURE_ELITE_RARE_HP] = sConfigMgr->GetOption("Rate.Creature.Elite.RARE.HP", 1.0f); - _rate_values[RATE_CREATURE_NORMAL_SPELLDAMAGE] = sConfigMgr->GetOption("Rate.Creature.Normal.SpellDamage", 1.0f); - _rate_values[RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE] = sConfigMgr->GetOption("Rate.Creature.Elite.Elite.SpellDamage", 1.0f); - _rate_values[RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE] = sConfigMgr->GetOption("Rate.Creature.Elite.RAREELITE.SpellDamage", 1.0f); - _rate_values[RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE] = sConfigMgr->GetOption("Rate.Creature.Elite.WORLDBOSS.SpellDamage", 1.0f); - _rate_values[RATE_CREATURE_ELITE_RARE_SPELLDAMAGE] = sConfigMgr->GetOption("Rate.Creature.Elite.RARE.SpellDamage", 1.0f); - _rate_values[RATE_CREATURE_AGGRO] = sConfigMgr->GetOption("Rate.Creature.Aggro", 1.0f); - _rate_values[RATE_REST_INGAME] = sConfigMgr->GetOption("Rate.Rest.InGame", 1.0f); - _rate_values[RATE_REST_OFFLINE_IN_TAVERN_OR_CITY] = sConfigMgr->GetOption("Rate.Rest.Offline.InTavernOrCity", 1.0f); - _rate_values[RATE_REST_OFFLINE_IN_WILDERNESS] = sConfigMgr->GetOption("Rate.Rest.Offline.InWilderness", 1.0f); - _rate_values[RATE_REST_MAX_BONUS] = sConfigMgr->GetOption("Rate.Rest.MaxBonus", 1.5f); - _rate_values[RATE_DAMAGE_FALL] = sConfigMgr->GetOption("Rate.Damage.Fall", 1.0f); - _rate_values[RATE_AUCTION_TIME] = sConfigMgr->GetOption("Rate.Auction.Time", 1.0f); - _rate_values[RATE_AUCTION_DEPOSIT] = sConfigMgr->GetOption("Rate.Auction.Deposit", 1.0f); - _rate_values[RATE_AUCTION_CUT] = sConfigMgr->GetOption("Rate.Auction.Cut", 1.0f); - _rate_values[RATE_HONOR] = sConfigMgr->GetOption("Rate.Honor", 1.0f); - _rate_values[RATE_ARENA_POINTS] = sConfigMgr->GetOption("Rate.ArenaPoints", 1.0f); - _rate_values[RATE_INSTANCE_RESET_TIME] = sConfigMgr->GetOption("Rate.InstanceResetTime", 1.0f); - - _rate_values[RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE] = sConfigMgr->GetOption("Rate.MissChanceMultiplier.TargetCreature", 11.0f); - _rate_values[RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER] = sConfigMgr->GetOption("Rate.MissChanceMultiplier.TargetPlayer", 7.0f); - _bool_configs[CONFIG_MISS_CHANCE_MULTIPLIER_ONLY_FOR_PLAYERS] = sConfigMgr->GetOption("Rate.MissChanceMultiplier.OnlyAffectsPlayer", false); - - _rate_values[RATE_TALENT] = sConfigMgr->GetOption("Rate.Talent", 1.0f); - if (_rate_values[RATE_TALENT] < 0.0f) - { - LOG_ERROR("server.loading", "Rate.Talent ({}) must be > 0. Using 1 instead.", _rate_values[RATE_TALENT]); - _rate_values[RATE_TALENT] = 1.0f; - } - _rate_values[RATE_TALENT_PET] = sConfigMgr->GetOption("Rate.Talent.Pet", 1.0f); - if (_rate_values[RATE_TALENT_PET] < 0.0f) - { - LOG_ERROR("server.loading", "Rate.Talent.Pet ({}) must be > 0. Using 1 instead.", _rate_values[RATE_TALENT_PET]); - _rate_values[RATE_TALENT_PET] = 1.0f; - } - // Controls Player movespeed rate. - _rate_values[RATE_MOVESPEED_PLAYER] = sConfigMgr->GetOption("Rate.MoveSpeed.Player", 1.0f); - if (_rate_values[RATE_MOVESPEED_PLAYER] < 0) - { - LOG_ERROR("server.loading", "Rate.MoveSpeed.Player ({}) must be > 0. Using 1 instead.", _rate_values[RATE_MOVESPEED_PLAYER]); - _rate_values[RATE_MOVESPEED_PLAYER] = 1.0f; - } - for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) playerBaseMoveSpeed[i] = baseMoveSpeed[i] * _rate_values[RATE_MOVESPEED_PLAYER]; - - // Controls all npc movespeed rate. - _rate_values[RATE_MOVESPEED_NPC] = sConfigMgr->GetOption("Rate.MoveSpeed.NPC", 1.0f); - if (_rate_values[RATE_MOVESPEED_NPC] < 0) - { - LOG_ERROR("server.loading", "Rate.MoveSpeed.NPC ({}) must be > 0. Using 1 instead.", _rate_values[RATE_MOVESPEED_NPC]); - _rate_values[RATE_MOVESPEED_NPC] = 1.0f; - } - for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) baseMoveSpeed[i] *= _rate_values[RATE_MOVESPEED_NPC]; - - _rate_values[RATE_CORPSE_DECAY_LOOTED] = sConfigMgr->GetOption("Rate.Corpse.Decay.Looted", 0.5f); - - _rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = sConfigMgr->GetOption("DurabilityLoss.OnDeath", 10.0f); - if (_rate_values[RATE_DURABILITY_LOSS_ON_DEATH] < 0.0f) - { - LOG_ERROR("server.loading", "DurabilityLoss.OnDeath ({}) must be >=0. Using 0.0 instead.", _rate_values[RATE_DURABILITY_LOSS_ON_DEATH]); - _rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = 0.0f; - } - if (_rate_values[RATE_DURABILITY_LOSS_ON_DEATH] > 100.0f) - { - LOG_ERROR("server.loading", "DurabilityLoss.OnDeath ({}) must be <= 100. Using 100.0 instead.", _rate_values[RATE_DURABILITY_LOSS_ON_DEATH]); - _rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = 0.0f; - } - _rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = _rate_values[RATE_DURABILITY_LOSS_ON_DEATH] / 100.0f; - - _rate_values[RATE_DURABILITY_LOSS_DAMAGE] = sConfigMgr->GetOption("DurabilityLossChance.Damage", 0.5f); - if (_rate_values[RATE_DURABILITY_LOSS_DAMAGE] < 0.0f) - { - LOG_ERROR("server.loading", "DurabilityLossChance.Damage ({}) must be >=0. Using 0.0 instead.", _rate_values[RATE_DURABILITY_LOSS_DAMAGE]); - _rate_values[RATE_DURABILITY_LOSS_DAMAGE] = 0.0f; - } - _rate_values[RATE_DURABILITY_LOSS_ABSORB] = sConfigMgr->GetOption("DurabilityLossChance.Absorb", 0.5f); - if (_rate_values[RATE_DURABILITY_LOSS_ABSORB] < 0.0f) - { - LOG_ERROR("server.loading", "DurabilityLossChance.Absorb ({}) must be >=0. Using 0.0 instead.", _rate_values[RATE_DURABILITY_LOSS_ABSORB]); - _rate_values[RATE_DURABILITY_LOSS_ABSORB] = 0.0f; - } - _rate_values[RATE_DURABILITY_LOSS_PARRY] = sConfigMgr->GetOption("DurabilityLossChance.Parry", 0.05f); - if (_rate_values[RATE_DURABILITY_LOSS_PARRY] < 0.0f) - { - LOG_ERROR("server.loading", "DurabilityLossChance.Parry ({}) must be >=0. Using 0.0 instead.", _rate_values[RATE_DURABILITY_LOSS_PARRY]); - _rate_values[RATE_DURABILITY_LOSS_PARRY] = 0.0f; - } - _rate_values[RATE_DURABILITY_LOSS_BLOCK] = sConfigMgr->GetOption("DurabilityLossChance.Block", 0.05f); - if (_rate_values[RATE_DURABILITY_LOSS_BLOCK] < 0.0f) - { - LOG_ERROR("server.loading", "DurabilityLossChance.Block ({}) must be >=0. Using 0.0 instead.", _rate_values[RATE_DURABILITY_LOSS_BLOCK]); - _rate_values[RATE_DURABILITY_LOSS_BLOCK] = 0.0f; - } - - ///- Read other configuration items from the config file - - _bool_configs[CONFIG_DURABILITY_LOSS_IN_PVP] = sConfigMgr->GetOption("DurabilityLoss.InPvP", false); - - _int_configs[CONFIG_COMPRESSION] = sConfigMgr->GetOption("Compression", 1); - if (_int_configs[CONFIG_COMPRESSION] < 1 || _int_configs[CONFIG_COMPRESSION] > 9) - { - LOG_ERROR("server.loading", "Compression level ({}) must be in range 1..9. Using default compression level (1).", _int_configs[CONFIG_COMPRESSION]); - _int_configs[CONFIG_COMPRESSION] = 1; - } - _bool_configs[CONFIG_ADDON_CHANNEL] = sConfigMgr->GetOption("AddonChannel", true); - _bool_configs[CONFIG_CLEAN_CHARACTER_DB] = sConfigMgr->GetOption("CleanCharacterDB", false); - _int_configs[CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS] = sConfigMgr->GetOption("PersistentCharacterCleanFlags", 0); - _int_configs[CONFIG_CHAT_CHANNEL_LEVEL_REQ] = sConfigMgr->GetOption("ChatLevelReq.Channel", 1); - _int_configs[CONFIG_CHAT_WHISPER_LEVEL_REQ] = sConfigMgr->GetOption("ChatLevelReq.Whisper", 1); - _int_configs[CONFIG_CHAT_SAY_LEVEL_REQ] = sConfigMgr->GetOption("ChatLevelReq.Say", 1); - _int_configs[CONFIG_PARTY_LEVEL_REQ] = sConfigMgr->GetOption("PartyLevelReq", 1); - _int_configs[CONFIG_TRADE_LEVEL_REQ] = sConfigMgr->GetOption("LevelReq.Trade", 1); - _int_configs[CONFIG_TICKET_LEVEL_REQ] = sConfigMgr->GetOption("LevelReq.Ticket", 1); - _int_configs[CONFIG_AUCTION_LEVEL_REQ] = sConfigMgr->GetOption("LevelReq.Auction", 1); - _int_configs[CONFIG_MAIL_LEVEL_REQ] = sConfigMgr->GetOption("LevelReq.Mail", 1); - _bool_configs[CONFIG_ALLOW_PLAYER_COMMANDS] = sConfigMgr->GetOption("AllowPlayerCommands", 1); - _bool_configs[CONFIG_PRESERVE_CUSTOM_CHANNELS] = sConfigMgr->GetOption("PreserveCustomChannels", false); - _int_configs[CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION] = sConfigMgr->GetOption("PreserveCustomChannelDuration", 14); - _int_configs[CONFIG_INTERVAL_SAVE] = sConfigMgr->GetOption("PlayerSaveInterval", 15 * MINUTE * IN_MILLISECONDS); - _int_configs[CONFIG_INTERVAL_DISCONNECT_TOLERANCE] = sConfigMgr->GetOption("DisconnectToleranceInterval", 0); - _bool_configs[CONFIG_STATS_SAVE_ONLY_ON_LOGOUT] = sConfigMgr->GetOption("PlayerSave.Stats.SaveOnlyOnLogout", true); - - _int_configs[CONFIG_MIN_LEVEL_STAT_SAVE] = sConfigMgr->GetOption("PlayerSave.Stats.MinLevel", 0); - if (_int_configs[CONFIG_MIN_LEVEL_STAT_SAVE] > MAX_LEVEL || int32(_int_configs[CONFIG_MIN_LEVEL_STAT_SAVE]) < 0) - { - LOG_ERROR("server.loading", "PlayerSave.Stats.MinLevel ({}) must be in range 0..80. Using default, do not save character stats (0).", _int_configs[CONFIG_MIN_LEVEL_STAT_SAVE]); - _int_configs[CONFIG_MIN_LEVEL_STAT_SAVE] = 0; - } - - _int_configs[CONFIG_INTERVAL_MAPUPDATE] = sConfigMgr->GetOption("MapUpdateInterval", 10); - if (_int_configs[CONFIG_INTERVAL_MAPUPDATE] < MIN_MAP_UPDATE_DELAY) - { - LOG_ERROR("server.loading", "MapUpdateInterval ({}) must be greater {}. Use this minimal value.", _int_configs[CONFIG_INTERVAL_MAPUPDATE], MIN_MAP_UPDATE_DELAY); - _int_configs[CONFIG_INTERVAL_MAPUPDATE] = MIN_MAP_UPDATE_DELAY; - } - if (reload) - sMapMgr->SetMapUpdateInterval(_int_configs[CONFIG_INTERVAL_MAPUPDATE]); - - _int_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfigMgr->GetOption("ChangeWeatherInterval", 10 * MINUTE * IN_MILLISECONDS); + for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) + baseMoveSpeed[i] *= getRate(RATE_MOVESPEED_NPC); if (reload) { - uint32 val = sConfigMgr->GetOption("WorldServerPort", 8085); - if (val != _int_configs[CONFIG_PORT_WORLD]) - LOG_ERROR("server.loading", "WorldServerPort option can't be changed at worldserver.conf reload, using current value ({}).", _int_configs[CONFIG_PORT_WORLD]); - } - else - _int_configs[CONFIG_PORT_WORLD] = sConfigMgr->GetOption("WorldServerPort", 8085); + sMapMgr->SetMapUpdateInterval(getIntConfig(CONFIG_INTERVAL_MAPUPDATE)); - _bool_configs[CONFIG_CLOSE_IDLE_CONNECTIONS] = sConfigMgr->GetOption("CloseIdleConnections", true); - _int_configs[CONFIG_SOCKET_TIMEOUTTIME] = sConfigMgr->GetOption("SocketTimeOutTime", 900000); - _int_configs[CONFIG_SOCKET_TIMEOUTTIME_ACTIVE] = sConfigMgr->GetOption("SocketTimeOutTimeActive", 60000); - _int_configs[CONFIG_SESSION_ADD_DELAY] = sConfigMgr->GetOption("SessionAddDelay", 10000); - - _float_configs[CONFIG_GROUP_XP_DISTANCE] = sConfigMgr->GetOption("MaxGroupXPDistance", 74.0f); - _float_configs[CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE] = sConfigMgr->GetOption("MaxRecruitAFriendBonusDistance", 100.0f); - - /// \todo Add MonsterSight in worldserver.conf or put it as define - _float_configs[CONFIG_SIGHT_MONSTER] = sConfigMgr->GetOption("MonsterSight", 50); - - if (reload) - { - uint32 val = sConfigMgr->GetOption("GameType", 0); - if (val != _int_configs[CONFIG_GAME_TYPE]) - LOG_ERROR("server.loading", "GameType option can't be changed at worldserver.conf reload, using current value ({}).", _int_configs[CONFIG_GAME_TYPE]); - } - else - _int_configs[CONFIG_GAME_TYPE] = sConfigMgr->GetOption("GameType", 0); - - if (reload) - { - uint32 val = sConfigMgr->GetOption("RealmZone", REALM_ZONE_DEVELOPMENT); - if (val != _int_configs[CONFIG_REALM_ZONE]) - LOG_ERROR("server.loading", "RealmZone option can't be changed at worldserver.conf reload, using current value ({}).", _int_configs[CONFIG_REALM_ZONE]); - } - else - _int_configs[CONFIG_REALM_ZONE] = sConfigMgr->GetOption("RealmZone", REALM_ZONE_DEVELOPMENT); - - _bool_configs[CONFIG_STRICT_NAMES_RESERVED] = sConfigMgr->GetOption ("StrictNames.Reserved", true); - _bool_configs[CONFIG_STRICT_NAMES_PROFANITY] = sConfigMgr->GetOption ("StrictNames.Profanity", true); - _int_configs[CONFIG_STRICT_PLAYER_NAMES] = sConfigMgr->GetOption ("StrictPlayerNames", 0); - _int_configs[CONFIG_STRICT_CHARTER_NAMES] = sConfigMgr->GetOption ("StrictCharterNames", 0); - _int_configs[CONFIG_STRICT_CHANNEL_NAMES] = sConfigMgr->GetOption ("StrictChannelNames", 0); - _int_configs[CONFIG_STRICT_PET_NAMES] = sConfigMgr->GetOption ("StrictPetNames", 0); - - _bool_configs[CONFIG_ALLOW_TWO_SIDE_ACCOUNTS] = sConfigMgr->GetOption("AllowTwoSide.Accounts", true); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Calendar", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Chat", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Channel", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Group", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Guild", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Arena", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Auction", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Mail", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_WHO_LIST] = sConfigMgr->GetOption("AllowTwoSide.WhoList", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND] = sConfigMgr->GetOption("AllowTwoSide.AddFriend", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_TRADE] = sConfigMgr->GetOption("AllowTwoSide.Trade", false); - _bool_configs[CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE] = sConfigMgr->GetOption("AllowTwoSide.Interaction.Emote", false); - - _int_configs[CONFIG_MIN_PLAYER_NAME] = sConfigMgr->GetOption ("MinPlayerName", 2); - if (_int_configs[CONFIG_MIN_PLAYER_NAME] < 1 || _int_configs[CONFIG_MIN_PLAYER_NAME] > MAX_PLAYER_NAME) - { - LOG_ERROR("server.loading", "MinPlayerName ({}) must be in range 1..{}. Set to 2.", _int_configs[CONFIG_MIN_PLAYER_NAME], MAX_PLAYER_NAME); - _int_configs[CONFIG_MIN_PLAYER_NAME] = 2; - } - - _int_configs[CONFIG_MIN_CHARTER_NAME] = sConfigMgr->GetOption ("MinCharterName", 2); - if (_int_configs[CONFIG_MIN_CHARTER_NAME] < 1 || _int_configs[CONFIG_MIN_CHARTER_NAME] > MAX_CHARTER_NAME) - { - LOG_ERROR("server.loading", "MinCharterName ({}) must be in range 1..{}. Set to 2.", _int_configs[CONFIG_MIN_CHARTER_NAME], MAX_CHARTER_NAME); - _int_configs[CONFIG_MIN_CHARTER_NAME] = 2; - } - - _int_configs[CONFIG_MIN_PET_NAME] = sConfigMgr->GetOption ("MinPetName", 2); - if (_int_configs[CONFIG_MIN_PET_NAME] < 1 || _int_configs[CONFIG_MIN_PET_NAME] > MAX_PET_NAME) - { - LOG_ERROR("server.loading", "MinPetName ({}) must be in range 1..{}. Set to 2.", _int_configs[CONFIG_MIN_PET_NAME], MAX_PET_NAME); - _int_configs[CONFIG_MIN_PET_NAME] = 2; - } - - _int_configs[CONFIG_CHARTER_COST_GUILD] = sConfigMgr->GetOption("Guild.CharterCost", 1000); - _int_configs[CONFIG_CHARTER_COST_ARENA_2v2] = sConfigMgr->GetOption("ArenaTeam.CharterCost.2v2", 800000); - _int_configs[CONFIG_CHARTER_COST_ARENA_3v3] = sConfigMgr->GetOption("ArenaTeam.CharterCost.3v3", 1200000); - _int_configs[CONFIG_CHARTER_COST_ARENA_5v5] = sConfigMgr->GetOption("ArenaTeam.CharterCost.5v5", 2000000); - - _int_configs[CONFIG_MAX_WHO_LIST_RETURN] = sConfigMgr->GetOption("MaxWhoListReturns", 49); - - _int_configs[CONFIG_CHARACTER_CREATING_DISABLED] = sConfigMgr->GetOption("CharacterCreating.Disabled", 0); - _int_configs[CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK] = sConfigMgr->GetOption("CharacterCreating.Disabled.RaceMask", 0); - - _int_configs[CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK] = sConfigMgr->GetOption("CharacterCreating.Disabled.ClassMask", 0); - - _int_configs[CONFIG_CHARACTERS_PER_REALM] = sConfigMgr->GetOption("CharactersPerRealm", 10); - if (_int_configs[CONFIG_CHARACTERS_PER_REALM] < 1 || _int_configs[CONFIG_CHARACTERS_PER_REALM] > 10) - { - LOG_ERROR("server.loading", "CharactersPerRealm ({}) must be in range 1..10. Set to 10.", _int_configs[CONFIG_CHARACTERS_PER_REALM]); - _int_configs[CONFIG_CHARACTERS_PER_REALM] = 10; - } - - // must be after CONFIG_CHARACTERS_PER_REALM - _int_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = sConfigMgr->GetOption("CharactersPerAccount", 50); - if (_int_configs[CONFIG_CHARACTERS_PER_ACCOUNT] < _int_configs[CONFIG_CHARACTERS_PER_REALM]) - { - LOG_ERROR("server.loading", "CharactersPerAccount ({}) can't be less than CharactersPerRealm ({}).", _int_configs[CONFIG_CHARACTERS_PER_ACCOUNT], _int_configs[CONFIG_CHARACTERS_PER_REALM]); - _int_configs[CONFIG_CHARACTERS_PER_ACCOUNT] = _int_configs[CONFIG_CHARACTERS_PER_REALM]; - } - - _int_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM] = sConfigMgr->GetOption("HeroicCharactersPerRealm", 1); - if (int32(_int_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM]) < 0 || _int_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM] > 10) - { - LOG_ERROR("server.loading", "HeroicCharactersPerRealm ({}) must be in range 0..10. Set to 1.", _int_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM]); - _int_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM] = 1; - } - - _int_configs[CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER] = sConfigMgr->GetOption("CharacterCreating.MinLevelForHeroicCharacter", 55); - - _int_configs[CONFIG_SKIP_CINEMATICS] = sConfigMgr->GetOption("SkipCinematics", 0); - if (int32(_int_configs[CONFIG_SKIP_CINEMATICS]) < 0 || _int_configs[CONFIG_SKIP_CINEMATICS] > 2) - { - LOG_ERROR("server.loading", "SkipCinematics ({}) must be in range 0..2. Set to 0.", _int_configs[CONFIG_SKIP_CINEMATICS]); - _int_configs[CONFIG_SKIP_CINEMATICS] = 0; - } - - if (reload) - { - uint32 val = sConfigMgr->GetOption("MaxPlayerLevel", DEFAULT_MAX_LEVEL); - if (val != _int_configs[CONFIG_MAX_PLAYER_LEVEL]) - LOG_ERROR("server.loading", "MaxPlayerLevel option can't be changed at config reload, using current value ({}).", _int_configs[CONFIG_MAX_PLAYER_LEVEL]); - } - else - _int_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfigMgr->GetOption("MaxPlayerLevel", DEFAULT_MAX_LEVEL); - - if (_int_configs[CONFIG_MAX_PLAYER_LEVEL] > MAX_LEVEL || _int_configs[CONFIG_MAX_PLAYER_LEVEL] < 1) - { - LOG_ERROR("server.loading", "MaxPlayerLevel ({}) must be in range 1..{}. Set to {}.", _int_configs[CONFIG_MAX_PLAYER_LEVEL], MAX_LEVEL, MAX_LEVEL); - _int_configs[CONFIG_MAX_PLAYER_LEVEL] = MAX_LEVEL; - } - - _int_configs[CONFIG_MIN_DUALSPEC_LEVEL] = sConfigMgr->GetOption("MinDualSpecLevel", 40); - - _int_configs[CONFIG_START_PLAYER_LEVEL] = sConfigMgr->GetOption("StartPlayerLevel", 1); - if (_int_configs[CONFIG_START_PLAYER_LEVEL] < 1 || _int_configs[CONFIG_START_PLAYER_LEVEL] > _int_configs[CONFIG_MAX_PLAYER_LEVEL]) - { - LOG_ERROR("server.loading", "StartPlayerLevel ({}) must be in range 1..MaxPlayerLevel({}). Set to 1.", _int_configs[CONFIG_START_PLAYER_LEVEL], _int_configs[CONFIG_MAX_PLAYER_LEVEL]); - _int_configs[CONFIG_START_PLAYER_LEVEL] = 1; - } - - _int_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] = sConfigMgr->GetOption("StartHeroicPlayerLevel", 55); - if (_int_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] < 1 || _int_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] > _int_configs[CONFIG_MAX_PLAYER_LEVEL]) - { - LOG_ERROR("server.loading", "StartHeroicPlayerLevel ({}) must be in range 1..MaxPlayerLevel({}). Set to 55.", - _int_configs[CONFIG_START_HEROIC_PLAYER_LEVEL], _int_configs[CONFIG_MAX_PLAYER_LEVEL]); - _int_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] = 55; - } - - _int_configs[CONFIG_START_PLAYER_MONEY] = sConfigMgr->GetOption("StartPlayerMoney", 0); - if (int32(_int_configs[CONFIG_START_PLAYER_MONEY]) < 0 || int32(_int_configs[CONFIG_START_PLAYER_MONEY]) > MAX_MONEY_AMOUNT) - { - LOG_ERROR("server.loading", "StartPlayerMoney ({}) must be in range 0..{}. Set to {}.", _int_configs[CONFIG_START_PLAYER_MONEY], MAX_MONEY_AMOUNT, 0); - _int_configs[CONFIG_START_PLAYER_MONEY] = 0; - } - - _int_configs[CONFIG_START_HEROIC_PLAYER_MONEY] = sConfigMgr->GetOption("StartHeroicPlayerMoney", 2000); - if (int32(_int_configs[CONFIG_START_HEROIC_PLAYER_MONEY]) < 0 || int32(_int_configs[CONFIG_START_HEROIC_PLAYER_MONEY]) > MAX_MONEY_AMOUNT) - { - LOG_ERROR("server.loading", "StartHeroicPlayerMoney ({}) must be in range 0..{}. Set to {}.", _int_configs[CONFIG_START_HEROIC_PLAYER_MONEY], MAX_MONEY_AMOUNT, 2000); - _int_configs[CONFIG_START_HEROIC_PLAYER_MONEY] = 2000; - } - - _int_configs[CONFIG_MAX_HONOR_POINTS] = sConfigMgr->GetOption("MaxHonorPoints", 75000); - if (int32(_int_configs[CONFIG_MAX_HONOR_POINTS]) < 0) - { - LOG_ERROR("server.loading", "MaxHonorPoints ({}) can't be negative. Set to 0.", _int_configs[CONFIG_MAX_HONOR_POINTS]); - _int_configs[CONFIG_MAX_HONOR_POINTS] = 0; - } - - _int_configs[CONFIG_MAX_HONOR_POINTS_MONEY_PER_POINT] = sConfigMgr->GetOption("MaxHonorPointsMoneyPerPoint", 0); - if (int32(_int_configs[CONFIG_MAX_HONOR_POINTS_MONEY_PER_POINT]) < 0) - { - LOG_ERROR("server.loading", "MaxHonorPointsMoneyPerPoint ({}) can't be negative. Set to 0.", _int_configs[CONFIG_MAX_HONOR_POINTS_MONEY_PER_POINT]); - _int_configs[CONFIG_MAX_HONOR_POINTS_MONEY_PER_POINT] = 0; - } - - _int_configs[CONFIG_START_HONOR_POINTS] = sConfigMgr->GetOption("StartHonorPoints", 0); - if (int32(_int_configs[CONFIG_START_HONOR_POINTS]) < 0 || int32(_int_configs[CONFIG_START_HONOR_POINTS]) > int32(_int_configs[CONFIG_MAX_HONOR_POINTS])) - { - LOG_ERROR("server.loading", "StartHonorPoints ({}) must be in range 0..MaxHonorPoints({}). Set to {}.", - _int_configs[CONFIG_START_HONOR_POINTS], _int_configs[CONFIG_MAX_HONOR_POINTS], 0); - _int_configs[CONFIG_START_HONOR_POINTS] = 0; - } - - _int_configs[CONFIG_MAX_ARENA_POINTS] = sConfigMgr->GetOption("MaxArenaPoints", 10000); - if (int32(_int_configs[CONFIG_MAX_ARENA_POINTS]) < 0) - { - LOG_ERROR("server.loading", "MaxArenaPoints ({}) can't be negative. Set to 0.", _int_configs[CONFIG_MAX_ARENA_POINTS]); - _int_configs[CONFIG_MAX_ARENA_POINTS] = 0; - } - - _int_configs[CONFIG_START_ARENA_POINTS] = sConfigMgr->GetOption("StartArenaPoints", 0); - if (int32(_int_configs[CONFIG_START_ARENA_POINTS]) < 0 || int32(_int_configs[CONFIG_START_ARENA_POINTS]) > int32(_int_configs[CONFIG_MAX_ARENA_POINTS])) - { - LOG_ERROR("server.loading", "StartArenaPoints ({}) must be in range 0..MaxArenaPoints({}). Set to {}.", - _int_configs[CONFIG_START_ARENA_POINTS], _int_configs[CONFIG_MAX_ARENA_POINTS], 0); - _int_configs[CONFIG_START_ARENA_POINTS] = 0; - } - - _int_configs[CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL] = sConfigMgr->GetOption("RecruitAFriend.MaxLevel", 60); - if (_int_configs[CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL] > _int_configs[CONFIG_MAX_PLAYER_LEVEL] - || int32(_int_configs[CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL]) < 0) - { - LOG_ERROR("server.loading", "RecruitAFriend.MaxLevel ({}) must be in the range 0..MaxLevel({}). Set to {}.", - _int_configs[CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL], _int_configs[CONFIG_MAX_PLAYER_LEVEL], 60); - _int_configs[CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL] = 60; - } - - _int_configs[CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL_DIFFERENCE] = sConfigMgr->GetOption("RecruitAFriend.MaxDifference", 4); - _bool_configs[CONFIG_ALL_TAXI_PATHS] = sConfigMgr->GetOption("AllFlightPaths", false); - _int_configs[CONFIG_INSTANT_TAXI] = sConfigMgr->GetOption("InstantFlightPaths", 0); - - _bool_configs[CONFIG_INSTANCE_IGNORE_LEVEL] = sConfigMgr->GetOption("Instance.IgnoreLevel", false); - _bool_configs[CONFIG_INSTANCE_IGNORE_RAID] = sConfigMgr->GetOption("Instance.IgnoreRaid", false); - _bool_configs[CONFIG_INSTANCE_GMSUMMON_PLAYER] = sConfigMgr->GetOption("Instance.GMSummonPlayer", false); - _bool_configs[CONFIG_INSTANCE_SHARED_ID] = sConfigMgr->GetOption("Instance.SharedNormalHeroicId", false); - - _int_configs[CONFIG_INSTANCE_RESET_TIME_HOUR] = sConfigMgr->GetOption("Instance.ResetTimeHour", 4); - _int_configs[CONFIG_INSTANCE_RESET_TIME_RELATIVE_TIMESTAMP] = sConfigMgr->GetOption("Instance.ResetTimeRelativeTimestamp", 1135814400); - _int_configs[CONFIG_INSTANCE_UNLOAD_DELAY] = sConfigMgr->GetOption("Instance.UnloadDelay", 30 * MINUTE * IN_MILLISECONDS); - - _int_configs[CONFIG_MAX_PRIMARY_TRADE_SKILL] = sConfigMgr->GetOption("MaxPrimaryTradeSkill", 2); - _int_configs[CONFIG_MIN_PETITION_SIGNS] = sConfigMgr->GetOption("MinPetitionSigns", 9); - if (_int_configs[CONFIG_MIN_PETITION_SIGNS] > 9 || int32(_int_configs[CONFIG_MIN_PETITION_SIGNS]) < 0) - { - LOG_ERROR("server.loading", "MinPetitionSigns ({}) must be in range 0..9. Set to 9.", _int_configs[CONFIG_MIN_PETITION_SIGNS]); - _int_configs[CONFIG_MIN_PETITION_SIGNS] = 9; - } - - _int_configs[CONFIG_GM_LOGIN_STATE] = sConfigMgr->GetOption("GM.LoginState", 2); - _int_configs[CONFIG_GM_VISIBLE_STATE] = sConfigMgr->GetOption("GM.Visible", 2); - _int_configs[CONFIG_GM_CHAT] = sConfigMgr->GetOption("GM.Chat", 2); - _int_configs[CONFIG_GM_WHISPERING_TO] = sConfigMgr->GetOption("GM.WhisperingTo", 2); - - _int_configs[CONFIG_GM_LEVEL_IN_GM_LIST] = sConfigMgr->GetOption("GM.InGMList.Level", SEC_ADMINISTRATOR); - _int_configs[CONFIG_GM_LEVEL_IN_WHO_LIST] = sConfigMgr->GetOption("GM.InWhoList.Level", SEC_ADMINISTRATOR); - _int_configs[CONFIG_START_GM_LEVEL] = sConfigMgr->GetOption("GM.StartLevel", 1); - if (_int_configs[CONFIG_START_GM_LEVEL] < _int_configs[CONFIG_START_PLAYER_LEVEL]) - { - LOG_ERROR("server.loading", "GM.StartLevel ({}) must be in range StartPlayerLevel({})..{}. Set to {}.", - _int_configs[CONFIG_START_GM_LEVEL], _int_configs[CONFIG_START_PLAYER_LEVEL], MAX_LEVEL, _int_configs[CONFIG_START_PLAYER_LEVEL]); - _int_configs[CONFIG_START_GM_LEVEL] = _int_configs[CONFIG_START_PLAYER_LEVEL]; - } - else if (_int_configs[CONFIG_START_GM_LEVEL] > MAX_LEVEL) - { - LOG_ERROR("server.loading", "GM.StartLevel ({}) must be in range 1..{}. Set to {}.", _int_configs[CONFIG_START_GM_LEVEL], MAX_LEVEL, MAX_LEVEL); - _int_configs[CONFIG_START_GM_LEVEL] = MAX_LEVEL; - } - _bool_configs[CONFIG_ALLOW_GM_GROUP] = sConfigMgr->GetOption("GM.AllowInvite", false); - _bool_configs[CONFIG_ALLOW_GM_FRIEND] = sConfigMgr->GetOption("GM.AllowFriend", false); - _bool_configs[CONFIG_GM_LOWER_SECURITY] = sConfigMgr->GetOption("GM.LowerSecurity", false); - _float_configs[CONFIG_CHANCE_OF_GM_SURVEY] = sConfigMgr->GetOption("GM.TicketSystem.ChanceOfGMSurvey", 50.0f); - - _int_configs[CONFIG_GROUP_VISIBILITY] = sConfigMgr->GetOption("Visibility.GroupMode", 1); - - _bool_configs[CONFIG_OBJECT_SPARKLES] = sConfigMgr->GetOption("Visibility.ObjectSparkles", true); - - _bool_configs[CONFIG_LOW_LEVEL_REGEN_BOOST] = sConfigMgr->GetOption("EnableLowLevelRegenBoost", true); - - _bool_configs[CONFIG_OBJECT_QUEST_MARKERS] = sConfigMgr->GetOption("Visibility.ObjectQuestMarkers", true); - - _int_configs[CONFIG_MAIL_DELIVERY_DELAY] = sConfigMgr->GetOption("MailDeliveryDelay", HOUR); - - _int_configs[CONFIG_UPTIME_UPDATE] = sConfigMgr->GetOption("UpdateUptimeInterval", 10); - if (int32(_int_configs[CONFIG_UPTIME_UPDATE]) <= 0) - { - LOG_ERROR("server.loading", "UpdateUptimeInterval ({}) must be > 0, set to default 10.", _int_configs[CONFIG_UPTIME_UPDATE]); - _int_configs[CONFIG_UPTIME_UPDATE] = 1; - } - - if (reload) - { - _timers[WUPDATE_UPTIME].SetInterval(_int_configs[CONFIG_UPTIME_UPDATE]*MINUTE * IN_MILLISECONDS); + _timers[WUPDATE_UPTIME].SetInterval(getIntConfig(CONFIG_UPTIME_UPDATE) * MINUTE* IN_MILLISECONDS); _timers[WUPDATE_UPTIME].Reset(); - } - // log db cleanup interval - _int_configs[CONFIG_LOGDB_CLEARINTERVAL] = sConfigMgr->GetOption("LogDB.Opt.ClearInterval", 10); - if (int32(_int_configs[CONFIG_LOGDB_CLEARINTERVAL]) <= 0) - { - LOG_ERROR("server.loading", "LogDB.Opt.ClearInterval ({}) must be > 0, set to default 10.", _int_configs[CONFIG_LOGDB_CLEARINTERVAL]); - _int_configs[CONFIG_LOGDB_CLEARINTERVAL] = 10; - } - if (reload) - { - _timers[WUPDATE_CLEANDB].SetInterval(_int_configs[CONFIG_LOGDB_CLEARINTERVAL] * MINUTE * IN_MILLISECONDS); + _timers[WUPDATE_CLEANDB].SetInterval(getIntConfig(CONFIG_LOGDB_CLEARINTERVAL) * MINUTE * IN_MILLISECONDS); _timers[WUPDATE_CLEANDB].Reset(); + + _timers[WUPDATE_AUTOBROADCAST].SetInterval(getIntConfig(CONFIG_AUTOBROADCAST_INTERVAL)); + _timers[WUPDATE_AUTOBROADCAST].Reset(); } - _int_configs[CONFIG_LOGDB_CLEARTIME] = sConfigMgr->GetOption("LogDB.Opt.ClearTime", 1209600); // 14 days default - LOG_INFO("server.loading", "Will clear `logs` table of entries older than {} seconds every {} minutes.", - _int_configs[CONFIG_LOGDB_CLEARTIME], _int_configs[CONFIG_LOGDB_CLEARINTERVAL]); - _int_configs[CONFIG_TELEPORT_TIMEOUT_NEAR] = sConfigMgr->GetOption("TeleportTimeoutNear", 25); // pussywizard - _int_configs[CONFIG_TELEPORT_TIMEOUT_FAR] = sConfigMgr->GetOption("TeleportTimeoutFar", 45); // pussywizard - _int_configs[CONFIG_MAX_ALLOWED_MMR_DROP] = sConfigMgr->GetOption("MaxAllowedMMRDrop", 500); // pussywizard - _bool_configs[CONFIG_ENABLE_LOGIN_AFTER_DC] = sConfigMgr->GetOption("EnableLoginAfterDC", true); // pussywizard - _bool_configs[CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS] = sConfigMgr->GetOption("DontCacheRandomMovementPaths", false); - - _int_configs[CONFIG_SKILL_CHANCE_ORANGE] = sConfigMgr->GetOption("SkillChance.Orange", 100); - _int_configs[CONFIG_SKILL_CHANCE_YELLOW] = sConfigMgr->GetOption("SkillChance.Yellow", 75); - _int_configs[CONFIG_SKILL_CHANCE_GREEN] = sConfigMgr->GetOption("SkillChance.Green", 25); - _int_configs[CONFIG_SKILL_CHANCE_GREY] = sConfigMgr->GetOption("SkillChance.Grey", 0); - - _int_configs[CONFIG_SKILL_CHANCE_MINING_STEPS] = sConfigMgr->GetOption("SkillChance.MiningSteps", 0); - _int_configs[CONFIG_SKILL_CHANCE_SKINNING_STEPS] = sConfigMgr->GetOption("SkillChance.SkinningSteps", 0); - - _bool_configs[CONFIG_SKILL_PROSPECTING] = sConfigMgr->GetOption("SkillChance.Prospecting", false); - _bool_configs[CONFIG_SKILL_MILLING] = sConfigMgr->GetOption("SkillChance.Milling", false); - - _int_configs[CONFIG_SKILL_GAIN_CRAFTING] = sConfigMgr->GetOption("SkillGain.Crafting", 1); - - _int_configs[CONFIG_SKILL_GAIN_DEFENSE] = sConfigMgr->GetOption("SkillGain.Defense", 1); - - _int_configs[CONFIG_SKILL_GAIN_GATHERING] = sConfigMgr->GetOption("SkillGain.Gathering", 1); - - _int_configs[CONFIG_SKILL_GAIN_WEAPON] = sConfigMgr->GetOption("SkillGain.Weapon", 1); - - _int_configs[CONFIG_MAX_OVERSPEED_PINGS] = sConfigMgr->GetOption("MaxOverspeedPings", 2); - if (_int_configs[CONFIG_MAX_OVERSPEED_PINGS] != 0 && _int_configs[CONFIG_MAX_OVERSPEED_PINGS] < 2) + if (getIntConfig(CONFIG_CLIENTCACHE_VERSION) == 0) { - LOG_ERROR("server.loading", "MaxOverspeedPings ({}) must be in range 2..infinity (or 0 to disable check). Set to 2.", _int_configs[CONFIG_MAX_OVERSPEED_PINGS]); - _int_configs[CONFIG_MAX_OVERSPEED_PINGS] = 2; + _worldConfig.OverwriteConfigValue(CONFIG_CLIENTCACHE_VERSION, _dbClientCacheVersion); + LOG_INFO("server.loading", "Client cache version set to: {}", _dbClientCacheVersion); } - _bool_configs[CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY] = sConfigMgr->GetOption("SaveRespawnTimeImmediately", true); - _bool_configs[CONFIG_WEATHER] = sConfigMgr->GetOption("ActivateWeather", true); - - _int_configs[CONFIG_DISABLE_BREATHING] = sConfigMgr->GetOption("DisableWaterBreath", SEC_CONSOLE); - - _bool_configs[CONFIG_ALWAYS_MAX_SKILL_FOR_LEVEL] = sConfigMgr->GetOption("AlwaysMaxSkillForLevel", false); - - if (reload) - { - uint32 val = sConfigMgr->GetOption("Expansion", 2); - if (val != _int_configs[CONFIG_EXPANSION]) - LOG_ERROR("server.loading", "Expansion option can't be changed at worldserver.conf reload, using current value ({}).", _int_configs[CONFIG_EXPANSION]); - } - else - _int_configs[CONFIG_EXPANSION] = sConfigMgr->GetOption("Expansion", 2); - - _int_configs[CONFIG_CHATFLOOD_MESSAGE_COUNT] = sConfigMgr->GetOption("ChatFlood.MessageCount", 10); - _int_configs[CONFIG_CHATFLOOD_MESSAGE_DELAY] = sConfigMgr->GetOption("ChatFlood.MessageDelay", 1); - _int_configs[CONFIG_CHATFLOOD_ADDON_MESSAGE_COUNT] = sConfigMgr->GetOption("ChatFlood.AddonMessageCount", 100); - _int_configs[CONFIG_CHATFLOOD_ADDON_MESSAGE_DELAY] = sConfigMgr->GetOption("ChatFlood.AddonMessageDelay", 1); - _int_configs[CONFIG_CHATFLOOD_MUTE_TIME] = sConfigMgr->GetOption("ChatFlood.MuteTime", 10); - _bool_configs[CONFIG_CHAT_MUTE_FIRST_LOGIN] = sConfigMgr->GetOption("Chat.MuteFirstLogin", false); - _int_configs[CONFIG_CHAT_TIME_MUTE_FIRST_LOGIN] = sConfigMgr->GetOption("Chat.MuteTimeFirstLogin", 120); - - _int_configs[CONFIG_EVENT_ANNOUNCE] = sConfigMgr->GetOption("Event.Announce", 0); - - _float_configs[CONFIG_CREATURE_LEASH_RADIUS] = sConfigMgr->GetOption("CreatureLeashRadius", 30.0f); - _float_configs[CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS] = sConfigMgr->GetOption("CreatureFamilyFleeAssistanceRadius", 30.0f); - _float_configs[CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS] = sConfigMgr->GetOption("CreatureFamilyAssistanceRadius", 10.0f); - _int_configs[CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY] = sConfigMgr->GetOption("CreatureFamilyAssistanceDelay", 2000); - _int_configs[CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD] = sConfigMgr->GetOption("CreatureFamilyAssistancePeriod", 3000); - _int_configs[CONFIG_CREATURE_FAMILY_FLEE_DELAY] = sConfigMgr->GetOption("CreatureFamilyFleeDelay", 7000); - - _int_configs[CONFIG_WORLD_BOSS_LEVEL_DIFF] = sConfigMgr->GetOption("WorldBossLevelDiff", 3); - - _bool_configs[CONFIG_QUEST_ENABLE_QUEST_TRACKER] = sConfigMgr->GetOption("Quests.EnableQuestTracker", false); - - // note: disable value (-1) will assigned as 0xFFFFFFF, to prevent overflow at calculations limit it to max possible player level MAX_LEVEL(100) - _int_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = sConfigMgr->GetOption("Quests.LowLevelHideDiff", 4); - if (_int_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] > MAX_LEVEL) - _int_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = MAX_LEVEL; - _int_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = sConfigMgr->GetOption("Quests.HighLevelHideDiff", 7); - if (_int_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] > MAX_LEVEL) - _int_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = MAX_LEVEL; - _bool_configs[CONFIG_QUEST_IGNORE_RAID] = sConfigMgr->GetOption("Quests.IgnoreRaid", false); - _bool_configs[CONFIG_QUEST_IGNORE_AUTO_ACCEPT] = sConfigMgr->GetOption("Quests.IgnoreAutoAccept", false); - _bool_configs[CONFIG_QUEST_IGNORE_AUTO_COMPLETE] = sConfigMgr->GetOption("Quests.IgnoreAutoComplete", false); - - _int_configs[CONFIG_RANDOM_BG_RESET_HOUR] = sConfigMgr->GetOption("Battleground.Random.ResetHour", 6); - if (_int_configs[CONFIG_RANDOM_BG_RESET_HOUR] > 23) - { - LOG_ERROR("server.loading", "Battleground.Random.ResetHour ({}) can't be load. Set to 6.", _int_configs[CONFIG_RANDOM_BG_RESET_HOUR]); - _int_configs[CONFIG_RANDOM_BG_RESET_HOUR] = 6; - } - - _int_configs[CONFIG_CALENDAR_DELETE_OLD_EVENTS_HOUR] = sConfigMgr->GetOption("Calendar.DeleteOldEventsHour", 6); - if (_int_configs[CONFIG_CALENDAR_DELETE_OLD_EVENTS_HOUR] > 23 || int32(_int_configs[CONFIG_CALENDAR_DELETE_OLD_EVENTS_HOUR]) < 0) - { - LOG_ERROR("server.loading", "Calendar.DeleteOldEventsHour ({}) can't be load. Set to 6.", _int_configs[CONFIG_CALENDAR_DELETE_OLD_EVENTS_HOUR]); - _int_configs[CONFIG_CALENDAR_DELETE_OLD_EVENTS_HOUR] = 6; - } - - _int_configs[CONFIG_GUILD_RESET_HOUR] = sConfigMgr->GetOption("Guild.ResetHour", 6); - if (_int_configs[CONFIG_GUILD_RESET_HOUR] > 23) - { - LOG_ERROR("server.loading", "Guild.ResetHour ({}) can't be load. Set to 6.", _int_configs[CONFIG_GUILD_RESET_HOUR]); - _int_configs[CONFIG_GUILD_RESET_HOUR] = 6; - } - - _int_configs[CONFIG_GUILD_BANK_INITIAL_TABS] = sConfigMgr->GetOption("Guild.BankInitialTabs", 0); - _int_configs[CONFIG_GUILD_BANK_TAB_COST_0] = sConfigMgr->GetOption("Guild.BankTabCost0", 1000000); - _int_configs[CONFIG_GUILD_BANK_TAB_COST_1] = sConfigMgr->GetOption("Guild.BankTabCost1", 2500000); - _int_configs[CONFIG_GUILD_BANK_TAB_COST_2] = sConfigMgr->GetOption("Guild.BankTabCost2", 5000000); - _int_configs[CONFIG_GUILD_BANK_TAB_COST_3] = sConfigMgr->GetOption("Guild.BankTabCost3", 10000000); - _int_configs[CONFIG_GUILD_BANK_TAB_COST_4] = sConfigMgr->GetOption("Guild.BankTabCost4", 25000000); - _int_configs[CONFIG_GUILD_BANK_TAB_COST_5] = sConfigMgr->GetOption("Guild.BankTabCost5", 50000000); - - _int_configs[CONFIG_GUILD_MEMBER_LIMIT] = sConfigMgr->GetOption("Guild.MemberLimit", 0); - - _bool_configs[CONFIG_DETECT_POS_COLLISION] = sConfigMgr->GetOption("DetectPosCollision", true); - - _bool_configs[CONFIG_RESTRICTED_LFG_CHANNEL] = sConfigMgr->GetOption("Channel.RestrictedLfg", true); - _bool_configs[CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL] = sConfigMgr->GetOption("Channel.SilentlyGMJoin", false); - - _bool_configs[CONFIG_TALENTS_INSPECTING] = sConfigMgr->GetOption("TalentsInspecting", true); - _bool_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfigMgr->GetOption("ChatFakeMessagePreventing", true); - _int_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY] = sConfigMgr->GetOption("ChatStrictLinkChecking.Severity", 0); - _int_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_KICK] = sConfigMgr->GetOption("ChatStrictLinkChecking.Kick", 0); - - _int_configs[CONFIG_CORPSE_DECAY_NORMAL] = sConfigMgr->GetOption("Corpse.Decay.NORMAL", 60); - _int_configs[CONFIG_CORPSE_DECAY_RARE] = sConfigMgr->GetOption("Corpse.Decay.RARE", 300); - _int_configs[CONFIG_CORPSE_DECAY_ELITE] = sConfigMgr->GetOption("Corpse.Decay.ELITE", 300); - _int_configs[CONFIG_CORPSE_DECAY_RAREELITE] = sConfigMgr->GetOption("Corpse.Decay.RAREELITE", 300); - _int_configs[CONFIG_CORPSE_DECAY_WORLDBOSS] = sConfigMgr->GetOption("Corpse.Decay.WORLDBOSS", 3600); - - _int_configs[CONFIG_DEATH_SICKNESS_LEVEL] = sConfigMgr->GetOption ("Death.SicknessLevel", 11); - _bool_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP] = sConfigMgr->GetOption("Death.CorpseReclaimDelay.PvP", true); - _bool_configs[CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE] = sConfigMgr->GetOption("Death.CorpseReclaimDelay.PvE", true); - _bool_configs[CONFIG_DEATH_BONES_WORLD] = sConfigMgr->GetOption("Death.Bones.World", true); - _bool_configs[CONFIG_DEATH_BONES_BG_OR_ARENA] = sConfigMgr->GetOption("Death.Bones.BattlegroundOrArena", true); - - _bool_configs[CONFIG_DIE_COMMAND_MODE] = sConfigMgr->GetOption("Die.Command.Mode", true); - - // always use declined names in the russian client - _bool_configs[CONFIG_DECLINED_NAMES_USED] = - (_int_configs[CONFIG_REALM_ZONE] == REALM_ZONE_RUSSIAN) ? true : sConfigMgr->GetOption("DeclinedNames", false); - - _float_configs[CONFIG_LISTEN_RANGE_SAY] = sConfigMgr->GetOption("ListenRange.Say", 25.0f); - _float_configs[CONFIG_LISTEN_RANGE_TEXTEMOTE] = sConfigMgr->GetOption("ListenRange.TextEmote", 25.0f); - _float_configs[CONFIG_LISTEN_RANGE_YELL] = sConfigMgr->GetOption("ListenRange.Yell", 300.0f); - - _int_configs[CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS] = sConfigMgr->GetOption("Battleground.Override.LowLevels.MinPlayers", 0); - _bool_configs[CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG] = sConfigMgr->GetOption("Battleground.DisableQuestShareInBG", false); - _bool_configs[CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG] = sConfigMgr->GetOption("Battleground.DisableReadyCheckInBG", false); - _bool_configs[CONFIG_BATTLEGROUND_CAST_DESERTER] = sConfigMgr->GetOption("Battleground.CastDeserter", true); - _bool_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE] = sConfigMgr->GetOption("Battleground.QueueAnnouncer.Enable", false); - _int_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_LEVEL] = sConfigMgr->GetOption("Battleground.QueueAnnouncer.Limit.MinLevel", 0); - _int_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS] = sConfigMgr->GetOption("Battleground.QueueAnnouncer.Limit.MinPlayers", 3); - _int_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY] = sConfigMgr->GetOption("Battleground.QueueAnnouncer.SpamProtection.Delay", 30); - _bool_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY] = sConfigMgr->GetOption("Battleground.QueueAnnouncer.PlayerOnly", false); - _bool_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED] = sConfigMgr->GetOption("Battleground.QueueAnnouncer.Timed", false); - _int_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER] = sConfigMgr->GetOption("Battleground.QueueAnnouncer.Timer", 30000); - _bool_configs[CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE] = sConfigMgr->GetOption("Battleground.StoreStatistics.Enable", false); - _bool_configs[CONFIG_BATTLEGROUND_TRACK_DESERTERS] = sConfigMgr->GetOption("Battleground.TrackDeserters.Enable", false); - _int_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfigMgr->GetOption ("Battleground.PrematureFinishTimer", 5 * MINUTE * IN_MILLISECONDS); - _int_configs[CONFIG_BATTLEGROUND_INVITATION_TYPE] = sConfigMgr->GetOption("Battleground.InvitationType", 0); - _int_configs[CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH] = sConfigMgr->GetOption ("Battleground.PremadeGroupWaitForMatch", 30 * MINUTE * IN_MILLISECONDS); - _bool_configs[CONFIG_BG_XP_FOR_KILL] = sConfigMgr->GetOption("Battleground.GiveXPForKills", false); - _int_configs[CONFIG_BATTLEGROUND_REPORT_AFK_TIMER] = sConfigMgr->GetOption("Battleground.ReportAFK.Timer", 4); - _int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = sConfigMgr->GetOption("Battleground.ReportAFK", 3); - if (_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] < 1) - { - LOG_ERROR("server.loading", "Battleground.ReportAFK ({}) must be >0. Using 3 instead.", _int_configs[CONFIG_BATTLEGROUND_REPORT_AFK]); - _int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = 3; - } - else if (_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] > 9) - { - LOG_ERROR("server.loading", "Battleground.ReportAFK ({}) must be <10. Using 3 instead.", _int_configs[CONFIG_BATTLEGROUND_REPORT_AFK]); - _int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = 3; - } - _int_configs[CONFIG_BATTLEGROUND_PLAYER_RESPAWN] = sConfigMgr->GetOption("Battleground.PlayerRespawn", 30); - if (_int_configs[CONFIG_BATTLEGROUND_PLAYER_RESPAWN] < 3) - { - LOG_ERROR("server.loading", "Battleground.PlayerRespawn ({}) must be >2. Using 30 instead.", _int_configs[CONFIG_BATTLEGROUND_PLAYER_RESPAWN]); - _int_configs[CONFIG_BATTLEGROUND_PLAYER_RESPAWN] = 30; - } - _int_configs[CONFIG_BATTLEGROUND_RESTORATION_BUFF_RESPAWN] = sConfigMgr->GetOption("Battleground.RestorationBuffRespawn", 20); - if (_int_configs[CONFIG_BATTLEGROUND_RESTORATION_BUFF_RESPAWN] < 1) - { - LOG_ERROR("server.loading", "Battleground.RestorationBuffRespawn ({}) must be > 0. Using 20 instead.", _int_configs[CONFIG_BATTLEGROUND_RESTORATION_BUFF_RESPAWN]); - _int_configs[CONFIG_BATTLEGROUND_RESTORATION_BUFF_RESPAWN] = 20; - } - _int_configs[CONFIG_BATTLEGROUND_BERSERKING_BUFF_RESPAWN] = sConfigMgr->GetOption("Battleground.BerserkingBuffRespawn", 120); - if (_int_configs[CONFIG_BATTLEGROUND_BERSERKING_BUFF_RESPAWN] < 1) - { - LOG_ERROR("server.loading", "Battleground.BerserkingBuffRespawn ({}) must be > 0. Using 120 instead.", _int_configs[CONFIG_BATTLEGROUND_BERSERKING_BUFF_RESPAWN]); - _int_configs[CONFIG_BATTLEGROUND_BERSERKING_BUFF_RESPAWN] = 120; - } - _int_configs[CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN] = sConfigMgr->GetOption("Battleground.SpeedBuffRespawn", 150); - if (_int_configs[CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN] < 1) - { - LOG_ERROR("server.loading", "Battleground.SpeedBuffRespawn ({}) must be > 0. Using 150 instead.", _int_configs[CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN]); - _int_configs[CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN] = 150; - } - - _int_configs[CONFIG_BATTLEGROUND_WARSONG_FLAGS] = sConfigMgr->GetOption("Battleground.Warsong.Flags", 3); - _int_configs[CONFIG_BATTLEGROUND_ARATHI_CAPTUREPOINTS] = sConfigMgr->GetOption("Battleground.Arathi.CapturePoints", 1600); - _int_configs[CONFIG_BATTLEGROUND_ALTERAC_REINFORCEMENTS] = sConfigMgr->GetOption("Battleground.Alterac.Reinforcements", 600); - _int_configs[CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH] = sConfigMgr->GetOption("Battleground.Alterac.ReputationOnBossDeath", 350); - _int_configs[CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS] = sConfigMgr->GetOption("Battleground.EyeOfTheStorm.CapturePoints", 1600); - - _int_configs[CONFIG_ARENA_MAX_RATING_DIFFERENCE] = sConfigMgr->GetOption("Arena.MaxRatingDifference", 150); - _int_configs[CONFIG_ARENA_RATING_DISCARD_TIMER] = sConfigMgr->GetOption("Arena.RatingDiscardTimer", 10 * MINUTE * IN_MILLISECONDS); - _int_configs[CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER] = sConfigMgr->GetOption("Arena.PreviousOpponentsDiscardTimer", 2 * MINUTE * IN_MILLISECONDS); - _bool_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS] = sConfigMgr->GetOption("Arena.AutoDistributePoints", false); - _int_configs[CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS] = sConfigMgr->GetOption("Arena.AutoDistributeInterval", 7); // pussywizard: spoiled by implementing constant day and hour, always 7 now - _int_configs[CONFIG_ARENA_GAMES_REQUIRED] = sConfigMgr->GetOption("Arena.GamesRequired", 10); - _int_configs[CONFIG_ARENA_START_RATING] = sConfigMgr->GetOption("Arena.ArenaStartRating", 0); - _int_configs[CONFIG_LEGACY_ARENA_POINTS_CALC] = sConfigMgr->GetOption("Arena.LegacyArenaPoints", 0); - _int_configs[CONFIG_ARENA_START_PERSONAL_RATING] = sConfigMgr->GetOption("Arena.ArenaStartPersonalRating", 0); - _int_configs[CONFIG_ARENA_START_MATCHMAKER_RATING] = sConfigMgr->GetOption("Arena.ArenaStartMatchmakerRating", 1500); - _float_configs[CONFIG_ARENA_WIN_RATING_MODIFIER_1] = sConfigMgr->GetOption("Arena.ArenaWinRatingModifier1", 48.0f); - _float_configs[CONFIG_ARENA_WIN_RATING_MODIFIER_2] = sConfigMgr->GetOption("Arena.ArenaWinRatingModifier2", 24.0f); - _float_configs[CONFIG_ARENA_LOSE_RATING_MODIFIER] = sConfigMgr->GetOption("Arena.ArenaLoseRatingModifier", 24.0f); - _float_configs[CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER] = sConfigMgr->GetOption("Arena.ArenaMatchmakerRatingModifier", 24.0f); - _bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE] = sConfigMgr->GetOption("Arena.QueueAnnouncer.Enable", false); - _bool_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY] = sConfigMgr->GetOption("Arena.QueueAnnouncer.PlayerOnly", false); - _int_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_DETAIL] = sConfigMgr->GetOption("Arena.QueueAnnouncer.Detail", 3); - - _bool_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfigMgr->GetOption("OffhandCheckAtSpellUnlearn", true); - _int_configs[CONFIG_CREATURE_STOP_FOR_PLAYER] = sConfigMgr->GetOption("Creature.MovingStopTimeForPlayer", 3 * MINUTE * IN_MILLISECONDS); - - _int_configs[CONFIG_WATER_BREATH_TIMER] = sConfigMgr->GetOption("WaterBreath.Timer", 180000); - if (_int_configs[CONFIG_WATER_BREATH_TIMER] <= 0) - { - LOG_ERROR("server.loading", "WaterBreath.Timer ({}) must be > 0. Using 180000 instead.", _int_configs[CONFIG_WATER_BREATH_TIMER]); - _int_configs[CONFIG_WATER_BREATH_TIMER] = 180000; - } - - if (int32 clientCacheId = sConfigMgr->GetOption("ClientCacheVersion", 0)) - { - // overwrite DB/old value - if (clientCacheId > 0) - { - _int_configs[CONFIG_CLIENTCACHE_VERSION] = clientCacheId; - LOG_INFO("server.loading", "Client cache version set to: {}", clientCacheId); - } - else - LOG_ERROR("server.loading", "ClientCacheVersion can't be negative {}, ignored.", clientCacheId); - } - - _int_configs[CONFIG_INSTANT_LOGOUT] = sConfigMgr->GetOption("InstantLogout", SEC_MODERATOR); - - _int_configs[CONFIG_GUILD_EVENT_LOG_COUNT] = sConfigMgr->GetOption("Guild.EventLogRecordsCount", GUILD_EVENTLOG_MAX_RECORDS); - if (_int_configs[CONFIG_GUILD_EVENT_LOG_COUNT] > GUILD_EVENTLOG_MAX_RECORDS) - _int_configs[CONFIG_GUILD_EVENT_LOG_COUNT] = GUILD_EVENTLOG_MAX_RECORDS; - _int_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] = sConfigMgr->GetOption("Guild.BankEventLogRecordsCount", GUILD_BANKLOG_MAX_RECORDS); - if (_int_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] > GUILD_BANKLOG_MAX_RECORDS) - _int_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] = GUILD_BANKLOG_MAX_RECORDS; - //visibility on continents _maxVisibleDistanceOnContinents = sConfigMgr->GetOption("Visibility.Distance.Continents", DEFAULT_VISIBILITY_DISTANCE); if (_maxVisibleDistanceOnContinents < 45 * sWorld->getRate(RATE_CREATURE_AGGRO)) @@ -1026,46 +254,8 @@ void World::LoadConfigSettings(bool reload) _maxVisibleDistanceInBGArenas = MAX_VISIBILITY_DISTANCE; } - ///- Load the CharDelete related config options - _int_configs[CONFIG_CHARDELETE_METHOD] = sConfigMgr->GetOption("CharDelete.Method", 0); - _int_configs[CONFIG_CHARDELETE_MIN_LEVEL] = sConfigMgr->GetOption("CharDelete.MinLevel", 0); - _int_configs[CONFIG_CHARDELETE_KEEP_DAYS] = sConfigMgr->GetOption("CharDelete.KeepDays", 30); - - ///- Load the ItemDelete related config options - _bool_configs[CONFIG_ITEMDELETE_METHOD] = sConfigMgr->GetOption("ItemDelete.Method", 0); - _bool_configs[CONFIG_ITEMDELETE_VENDOR] = sConfigMgr->GetOption("ItemDelete.Vendor", 0); - _int_configs[CONFIG_ITEMDELETE_QUALITY] = sConfigMgr->GetOption("ItemDelete.Quality", 3); - _int_configs[CONFIG_ITEMDELETE_ITEM_LEVEL] = sConfigMgr->GetOption("ItemDelete.ItemLevel", 80); - _int_configs[CONFIG_ITEMDELETE_KEEP_DAYS] = sConfigMgr->GetOption("ItemDelete.KeepDays", 0); - - _int_configs[CONFIG_FFA_PVP_TIMER] = sConfigMgr->GetOption("FFAPvPTimer", 30); - - _int_configs[CONFIG_LOOT_NEED_BEFORE_GREED_ILVL_RESTRICTION] = sConfigMgr->GetOption("LootNeedBeforeGreedILvlRestriction", 70); - - _bool_configs[CONFIG_PLAYER_SETTINGS_ENABLED] = sConfigMgr->GetOption("EnablePlayerSettings", 0); - - _bool_configs[CONFIG_ALLOW_JOIN_BG_AND_LFG] = sConfigMgr->GetOption("JoinBGAndLFG.Enable", false); - - _bool_configs[CONFIG_LEAVE_GROUP_ON_LOGOUT] = sConfigMgr->GetOption("LeaveGroupOnLogout.Enabled", false); - - _bool_configs[CONFIG_QUEST_POI_ENABLED] = sConfigMgr->GetOption("QuestPOI.Enabled", true); - - _int_configs[CONFIG_CHANGE_FACTION_MAX_MONEY] = sConfigMgr->GetOption("ChangeFaction.MaxMoney", 0); - - _bool_configs[CONFIG_ALLOWS_RANK_MOD_FOR_PET_HEALTH] = sConfigMgr->GetOption("Pet.RankMod.Health", true); - - _bool_configs[CONFIG_MUNCHING_BLIZZLIKE] = sConfigMgr->GetOption("MunchingBlizzlike.Enabled", true); - - _bool_configs[CONFIG_ENABLE_DAZE] = sConfigMgr->GetOption("Daze.Enabled", true); - - _int_configs[CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD] = sConfigMgr->GetOption("DailyRBGArenaPoints.MinLevel", 71); - - // Respawn - _float_configs[CONFIG_RESPAWN_DYNAMICRATE_CREATURE] = sConfigMgr->GetOption("Respawn.DynamicRateCreature", 1.0f); - _int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE] = sConfigMgr->GetOption("Respawn.DynamicMinimumCreature", 10); - - _float_configs[CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT] = sConfigMgr->GetOption("Respawn.DynamicRateGameObject", 1.0f); - _int_configs[CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT] = sConfigMgr->GetOption("Respawn.DynamicMinimumGameObject", 10); + LOG_INFO("server.loading", "Will clear `logs` table of entries older than {} seconds every {} minutes.", + getIntConfig(CONFIG_LOGDB_CLEARTIME), getIntConfig(CONFIG_LOGDB_CLEARINTERVAL)); ///- Read the "Data" directory from the config file std::string dataPath = sConfigMgr->GetOption("DataDir", "./"); @@ -1092,14 +282,10 @@ void World::LoadConfigSettings(bool reload) LOG_INFO("server.loading", "Using DataDir {}", _dataPath); } - bool enableIndoor = sConfigMgr->GetOption("vmap.enableIndoorCheck", true); - _bool_configs[CONFIG_VMAP_INDOOR_CHECK] = enableIndoor; - bool enableLOS = sConfigMgr->GetOption("vmap.enableLOS", true); - bool enableHeight = sConfigMgr->GetOption("vmap.enableHeight", true); - bool enablePetLOS = sConfigMgr->GetOption("vmap.petLOS", true); - _bool_configs[CONFIG_VMAP_BLIZZLIKE_PVP_LOS] = sConfigMgr->GetOption("vmap.BlizzlikePvPLOS", true); - _bool_configs[CONFIG_VMAP_BLIZZLIKE_LOS_OPEN_WORLD] = sConfigMgr->GetOption("vmap.BlizzlikeLOSInOpenWorld", true); - + bool const enableIndoor = getBoolConfig(CONFIG_VMAP_INDOOR_CHECK); + bool const enableLOS = sConfigMgr->GetOption("vmap.enableLOS", true); + bool const enablePetLOS = getBoolConfig(CONFIG_PET_LOS); + bool const enableHeight = sConfigMgr->GetOption("vmap.enableHeight", true); if (!enableHeight) LOG_ERROR("server.loading", "VMap height checking disabled! Creatures movements and other various things WILL be broken! Expect no support."); @@ -1107,169 +293,8 @@ void World::LoadConfigSettings(bool reload) VMAP::VMapFactory::createOrGetVMapMgr()->setEnableHeightCalc(enableHeight); LOG_INFO("server.loading", "WORLD: VMap support included. LineOfSight:{}, getHeight:{}, indoorCheck:{} PetLOS:{}", enableLOS, enableHeight, enableIndoor, enablePetLOS); - _bool_configs[CONFIG_PET_LOS] = enablePetLOS; - _bool_configs[CONFIG_START_CUSTOM_SPELLS] = sConfigMgr->GetOption("PlayerStart.CustomSpells", false); - _int_configs[CONFIG_HONOR_AFTER_DUEL] = sConfigMgr->GetOption("HonorPointsAfterDuel", 0); - _bool_configs[CONFIG_START_ALL_EXPLORED] = sConfigMgr->GetOption("PlayerStart.MapsExplored", false); - _bool_configs[CONFIG_START_ALL_REP] = sConfigMgr->GetOption("PlayerStart.AllReputation", false); - _bool_configs[CONFIG_ALWAYS_MAXSKILL] = sConfigMgr->GetOption("AlwaysMaxWeaponSkill", false); - _bool_configs[CONFIG_PVP_TOKEN_ENABLE] = sConfigMgr->GetOption("PvPToken.Enable", false); - _int_configs[CONFIG_PVP_TOKEN_MAP_TYPE] = sConfigMgr->GetOption("PvPToken.MapAllowType", 4); - _int_configs[CONFIG_PVP_TOKEN_ID] = sConfigMgr->GetOption("PvPToken.ItemID", 29434); - _int_configs[CONFIG_PVP_TOKEN_COUNT] = sConfigMgr->GetOption("PvPToken.ItemCount", 1); - if (_int_configs[CONFIG_PVP_TOKEN_COUNT] < 1) - _int_configs[CONFIG_PVP_TOKEN_COUNT] = 1; - - _bool_configs[CONFIG_NO_RESET_TALENT_COST] = sConfigMgr->GetOption("NoResetTalentsCost", false); - _int_configs[CONFIG_TOGGLE_XP_COST] = sConfigMgr->GetOption("ToggleXP.Cost", 100000); - _bool_configs[CONFIG_SHOW_KICK_IN_WORLD] = sConfigMgr->GetOption("ShowKickInWorld", false); - _bool_configs[CONFIG_SHOW_MUTE_IN_WORLD] = sConfigMgr->GetOption("ShowMuteInWorld", false); - _bool_configs[CONFIG_SHOW_BAN_IN_WORLD] = sConfigMgr->GetOption("ShowBanInWorld", false); - _int_configs[CONFIG_NUMTHREADS] = sConfigMgr->GetOption("MapUpdate.Threads", 1); - _int_configs[CONFIG_MAX_RESULTS_LOOKUP_COMMANDS] = sConfigMgr->GetOption("Command.LookupMaxResults", 0); - - // Warden - _bool_configs[CONFIG_WARDEN_ENABLED] = sConfigMgr->GetOption("Warden.Enabled", true); - _int_configs[CONFIG_WARDEN_NUM_MEM_CHECKS] = sConfigMgr->GetOption("Warden.NumMemChecks", 3); - _int_configs[CONFIG_WARDEN_NUM_LUA_CHECKS] = sConfigMgr->GetOption("Warden.NumLuaChecks", 1); - _int_configs[CONFIG_WARDEN_NUM_OTHER_CHECKS] = sConfigMgr->GetOption("Warden.NumOtherChecks", 7); - _int_configs[CONFIG_WARDEN_CLIENT_BAN_DURATION] = sConfigMgr->GetOption("Warden.BanDuration", 86400); - _int_configs[CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF] = sConfigMgr->GetOption("Warden.ClientCheckHoldOff", 30); - _int_configs[CONFIG_WARDEN_CLIENT_FAIL_ACTION] = sConfigMgr->GetOption("Warden.ClientCheckFailAction", 0); - _int_configs[CONFIG_WARDEN_CLIENT_RESPONSE_DELAY] = sConfigMgr->GetOption("Warden.ClientResponseDelay", 600); - - // Dungeon finder - _int_configs[CONFIG_LFG_OPTIONSMASK] = sConfigMgr->GetOption("DungeonFinder.OptionsMask", 5); - - _bool_configs[CONFIG_LFG_CAST_DESERTER] = sConfigMgr->GetOption("DungeonFinder.CastDeserter", true); - - // DBC_ItemAttributes - _bool_configs[CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES] = sConfigMgr->GetOption("DBC.EnforceItemAttributes", true); - - // Max instances per hour - _int_configs[CONFIG_MAX_INSTANCES_PER_HOUR] = sConfigMgr->GetOption("AccountInstancesPerHour", 5); - - // AutoBroadcast - _bool_configs[CONFIG_AUTOBROADCAST] = sConfigMgr->GetOption("AutoBroadcast.On", false); - _int_configs[CONFIG_AUTOBROADCAST_CENTER] = sConfigMgr->GetOption("AutoBroadcast.Center", 0); - _int_configs[CONFIG_AUTOBROADCAST_INTERVAL] = sConfigMgr->GetOption("AutoBroadcast.Timer", 60000); - _int_configs[CONFIG_AUTOBROADCAST_MIN_LEVEL_DISABLE] = sConfigMgr->GetOption("AutoBroadcast.MinDisableLevel", 0); - if (reload) - { - _timers[WUPDATE_AUTOBROADCAST].SetInterval(_int_configs[CONFIG_AUTOBROADCAST_INTERVAL]); - _timers[WUPDATE_AUTOBROADCAST].Reset(); - } - - // MySQL ping time interval - _int_configs[CONFIG_DB_PING_INTERVAL] = sConfigMgr->GetOption("MaxPingTime", 30); - - // misc - _bool_configs[CONFIG_PDUMP_NO_PATHS] = sConfigMgr->GetOption("PlayerDump.DisallowPaths", true); - _bool_configs[CONFIG_PDUMP_NO_OVERWRITE] = sConfigMgr->GetOption("PlayerDump.DisallowOverwrite", true); - _bool_configs[CONFIG_ENABLE_MMAPS] = sConfigMgr->GetOption("MoveMaps.Enable", true); MMAP::MMapFactory::InitializeDisabledMaps(); - // Wintergrasp - _int_configs[CONFIG_WINTERGRASP_ENABLE] = sConfigMgr->GetOption("Wintergrasp.Enable", 1); - _int_configs[CONFIG_WINTERGRASP_PLR_MAX] = sConfigMgr->GetOption("Wintergrasp.PlayerMax", 120); - _int_configs[CONFIG_WINTERGRASP_PLR_MIN] = sConfigMgr->GetOption("Wintergrasp.PlayerMin", 0); - _int_configs[CONFIG_WINTERGRASP_PLR_MIN_LVL] = sConfigMgr->GetOption("Wintergrasp.PlayerMinLvl", 75); - _int_configs[CONFIG_WINTERGRASP_BATTLETIME] = sConfigMgr->GetOption("Wintergrasp.BattleTimer", 30); - _int_configs[CONFIG_WINTERGRASP_NOBATTLETIME] = sConfigMgr->GetOption("Wintergrasp.NoBattleTimer", 150); - _int_configs[CONFIG_WINTERGRASP_RESTART_AFTER_CRASH] = sConfigMgr->GetOption("Wintergrasp.CrashRestartTimer", 10); - - _int_configs[CONFIG_BIRTHDAY_TIME] = sConfigMgr->GetOption("BirthdayTime", 1222964635); - _bool_configs[CONFIG_MINIGOB_MANABONK] = sConfigMgr->GetOption("Minigob.Manabonk.Enable", true); - - _bool_configs[CONFIG_ENABLE_CONTINENT_TRANSPORT] = sConfigMgr->GetOption("IsContinentTransport.Enabled", true); - _bool_configs[CONFIG_ENABLE_CONTINENT_TRANSPORT_PRELOADING] = sConfigMgr->GetOption("IsPreloadedContinentTransport.Enabled", false); - - _bool_configs[CONFIG_IP_BASED_ACTION_LOGGING] = sConfigMgr->GetOption("Allow.IP.Based.Action.Logging", false); - - // Whether to use LoS from game objects - _bool_configs[CONFIG_CHECK_GOBJECT_LOS] = sConfigMgr->GetOption("CheckGameObjectLoS", true); - - _bool_configs[CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA] = sConfigMgr->GetOption("Calculate.Creature.Zone.Area.Data", false); - _bool_configs[CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA] = sConfigMgr->GetOption("Calculate.Gameoject.Zone.Area.Data", false); - - // Player can join LFG anywhere - _bool_configs[CONFIG_LFG_LOCATION_ALL] = sConfigMgr->GetOption("LFG.Location.All", false); - - // Prevent players AFK from being logged out - _int_configs[CONFIG_AFK_PREVENT_LOGOUT] = sConfigMgr->GetOption("PreventAFKLogout", 0); - - // Preload all grids of all non-instanced maps - _bool_configs[CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS] = sConfigMgr->GetOption("PreloadAllNonInstancedMapGrids", false); - - // ICC buff override - _int_configs[CONFIG_ICC_BUFF_HORDE] = sConfigMgr->GetOption("ICC.Buff.Horde", 73822); - _int_configs[CONFIG_ICC_BUFF_ALLIANCE] = sConfigMgr->GetOption("ICC.Buff.Alliance", 73828); - - _bool_configs[CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE] = sConfigMgr->GetOption("SetAllCreaturesWithWaypointMovementActive", false); - - // packet spoof punishment - _int_configs[CONFIG_PACKET_SPOOF_BANMODE] = sConfigMgr->GetOption("PacketSpoof.BanMode", (uint32)0); - if (_int_configs[CONFIG_PACKET_SPOOF_BANMODE] > 1) - _int_configs[CONFIG_PACKET_SPOOF_BANMODE] = (uint32)0; - - _int_configs[CONFIG_PACKET_SPOOF_BANDURATION] = sConfigMgr->GetOption("PacketSpoof.BanDuration", 86400); - - // Random Battleground Rewards - _int_configs[CONFIG_BG_REWARD_WINNER_HONOR_FIRST] = sConfigMgr->GetOption("Battleground.RewardWinnerHonorFirst", 30); - _int_configs[CONFIG_BG_REWARD_WINNER_ARENA_FIRST] = sConfigMgr->GetOption("Battleground.RewardWinnerArenaFirst", 25); - _int_configs[CONFIG_BG_REWARD_WINNER_HONOR_LAST] = sConfigMgr->GetOption("Battleground.RewardWinnerHonorLast", 15); - _int_configs[CONFIG_BG_REWARD_WINNER_ARENA_LAST] = sConfigMgr->GetOption("Battleground.RewardWinnerArenaLast", 0); - _int_configs[CONFIG_BG_REWARD_LOSER_HONOR_FIRST] = sConfigMgr->GetOption("Battleground.RewardLoserHonorFirst", 5); - _int_configs[CONFIG_BG_REWARD_LOSER_HONOR_LAST] = sConfigMgr->GetOption("Battleground.RewardLoserHonorLast", 5); - - _int_configs[CONFIG_WAYPOINT_MOVEMENT_STOP_TIME_FOR_PLAYER] = sConfigMgr->GetOption("WaypointMovementStopTimeForPlayer", 120); - - _int_configs[CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PRINT_MODE] = sConfigMgr->GetOption("DungeonAccessRequirements.PrintMode", 1); - _bool_configs[CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PORTAL_CHECK_ILVL] = sConfigMgr->GetOption("DungeonAccessRequirements.PortalAvgIlevelCheck", false); - _bool_configs[CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE] = sConfigMgr->GetOption("DungeonAccessRequirements.LFGLevelDBCOverride", false); - _int_configs[CONFIG_DUNGEON_ACCESS_REQUIREMENTS_OPTIONAL_STRING_ID] = sConfigMgr->GetOption("DungeonAccessRequirements.OptionalStringID", 0); - _int_configs[CONFIG_NPC_EVADE_IF_NOT_REACHABLE] = sConfigMgr->GetOption("NpcEvadeIfTargetIsUnreachable", 5); - _int_configs[CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID] = sConfigMgr->GetOption("NpcRegenHPTimeIfTargetIsUnreachable", 10); - _bool_configs[CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID] = sConfigMgr->GetOption("NpcRegenHPIfTargetIsUnreachable", true); - - //Debug - _bool_configs[CONFIG_DEBUG_BATTLEGROUND] = sConfigMgr->GetOption("Debug.Battleground", false); - _bool_configs[CONFIG_DEBUG_ARENA] = sConfigMgr->GetOption("Debug.Arena", false); - - _int_configs[CONFIG_GM_LEVEL_CHANNEL_MODERATION] = sConfigMgr->GetOption("Channel.ModerationGMLevel", 1); - - _bool_configs[CONFIG_SET_BOP_ITEM_TRADEABLE] = sConfigMgr->GetOption("Item.SetItemTradeable", true); - - // Specifies if IP addresses can be logged to the database - _bool_configs[CONFIG_ALLOW_LOGGING_IP_ADDRESSES_IN_DATABASE] = sConfigMgr->GetOption("AllowLoggingIPAddressesInDatabase", true, true); - - // LFG group mechanics. - _int_configs[CONFIG_LFG_MAX_KICK_COUNT] = sConfigMgr->GetOption("LFG.MaxKickCount", 2); - if (_int_configs[CONFIG_LFG_MAX_KICK_COUNT] > 3) - { - _int_configs[CONFIG_LFG_MAX_KICK_COUNT] = 3; - LOG_ERROR("server.loading", "LFG.MaxKickCount can't be higher than 3."); - } - - _int_configs[CONFIG_LFG_KICK_PREVENTION_TIMER] = sConfigMgr->GetOption("LFG.KickPreventionTimer", 15 * MINUTE * IN_MILLISECONDS) * IN_MILLISECONDS; - if (_int_configs[CONFIG_LFG_KICK_PREVENTION_TIMER] > 15 * MINUTE * IN_MILLISECONDS) - { - _int_configs[CONFIG_LFG_KICK_PREVENTION_TIMER] = 15 * MINUTE * IN_MILLISECONDS; - LOG_ERROR("server.loading", "LFG.KickPreventionTimer can't be higher than 15 minutes."); - } - - // Realm Availability - _bool_configs[CONFIG_REALM_LOGIN_ENABLED] = sConfigMgr->GetOption("World.RealmAvailability", true); - - // AH Worker threads - _int_configs[CONFIG_AUCTIONHOUSE_WORKERTHREADS] = sConfigMgr->GetOption("AuctionHouse.WorkerThreads", 1); - - // SpellQueue - _bool_configs[CONFIG_SPELL_QUEUE_ENABLED] = sConfigMgr->GetOption("SpellQueue.Enabled", true); - _int_configs[CONFIG_SPELL_QUEUE_WINDOW] = sConfigMgr->GetOption("SpellQueue.Window", 400); - - _int_configs[CONFIG_SUNSREACH_COUNTER_MAX] = sConfigMgr->GetOption("Sunsreach.CounterMax", 10000); - // call ScriptMgr if we're reloading the configuration sScriptMgr->OnAfterConfigLoad(reload); } @@ -1309,7 +334,7 @@ void World::SetInitialWorldSettings() || !MapMgr::ExistMapAndVMap(MAP_EASTERN_KINGDOMS, 1676.35f, 1677.45f) || !MapMgr::ExistMapAndVMap(MAP_KALIMDOR, 10311.3f, 832.463f) || !MapMgr::ExistMapAndVMap(MAP_KALIMDOR, -2917.58f, -257.98f) - || (_int_configs[CONFIG_EXPANSION] && ( + || (getIntConfig(CONFIG_EXPANSION) && ( !MapMgr::ExistMapAndVMap(MAP_OUTLAND, 10349.6f, -6357.29f) || !MapMgr::ExistMapAndVMap(MAP_OUTLAND, -3961.64f, -13931.2f)))) { @@ -1860,12 +885,12 @@ void World::SetInitialWorldSettings() LoginDatabase.Execute(stmt); _timers[WUPDATE_WEATHERS].SetInterval(1 * IN_MILLISECONDS); - _timers[WUPDATE_UPTIME].SetInterval(_int_configs[CONFIG_UPTIME_UPDATE]*MINUTE * IN_MILLISECONDS); + _timers[WUPDATE_UPTIME].SetInterval(getIntConfig(CONFIG_UPTIME_UPDATE)*MINUTE * IN_MILLISECONDS); //Update "uptime" table based on configuration entry in minutes. _timers[WUPDATE_CORPSES].SetInterval(20 * MINUTE * IN_MILLISECONDS); //erase corpses every 20 minutes - _timers[WUPDATE_CLEANDB].SetInterval(_int_configs[CONFIG_LOGDB_CLEARINTERVAL]*MINUTE * IN_MILLISECONDS); + _timers[WUPDATE_CLEANDB].SetInterval(getIntConfig(CONFIG_LOGDB_CLEARINTERVAL)*MINUTE * IN_MILLISECONDS); // clean logs table every 14 days by default _timers[WUPDATE_AUTOBROADCAST].SetInterval(getIntConfig(CONFIG_AUTOBROADCAST_INTERVAL)); @@ -2303,6 +1328,58 @@ void World::Update(uint32 diff) } } +// Internally uses setFloatConfig. Retained for backwards compatibility +void World::setRate(ServerConfigs index, float value) +{ + setFloatConfig(index, value); +} + +// Internally uses getFloatConfig. Retained for backwards compatibility +float World::getRate(ServerConfigs index) const +{ + return getFloatConfig(index); +} + +void World::setBoolConfig(ServerConfigs index, bool value) +{ + _worldConfig.OverwriteConfigValue(index, value); +} + +bool World::getBoolConfig(ServerConfigs index) const +{ + return _worldConfig.GetConfigValue(index); +} + +void World::setFloatConfig(ServerConfigs index, float value) +{ + _worldConfig.OverwriteConfigValue(index, value); +} + +float World::getFloatConfig(ServerConfigs index) const +{ + return _worldConfig.GetConfigValue(index); +} + +void World::setIntConfig(ServerConfigs index, uint32 value) +{ + _worldConfig.OverwriteConfigValue(index, value); +} + +uint32 World::getIntConfig(ServerConfigs index) const +{ + return _worldConfig.GetConfigValue(index); +} + +void World::setStringConfig(ServerConfigs index, std::string const& value) +{ + _worldConfig.OverwriteConfigValue(index, value); +} + +std::string_view World::getStringConfig(ServerConfigs index) const +{ + return _worldConfig.GetConfigValue(index); +} + void World::ForceGameEventUpdate() { _timers[WUPDATE_EVENTS].Reset(); // to give time for Update() to be processed @@ -2716,7 +1793,7 @@ void World::LoadDBVersion() _dbVersion = fields[0].Get(); // will be overwrite by config values if different and non-0 - _int_configs[CONFIG_CLIENTCACHE_VERSION] = fields[1].Get(); + _dbClientCacheVersion = fields[1].Get(); } if (_dbVersion.empty()) diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 8fdcd815e..2b8310efe 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -160,11 +160,6 @@ public: /// Allow/Disallow object movements void SetAllowMovement(bool allow) override { _allowMovement = allow; } - /// Set the string for new characters (first login) - void SetNewCharString(std::string const& str) override { _newCharString = str; } - /// Get the string for new characters (first login) - [[nodiscard]] std::string const& GetNewCharString() const override { return _newCharString; } - [[nodiscard]] LocaleConstant GetDefaultDbcLocale() const override { return _defaultDbcLocale; } /// Get the path where data (dbc, maps) are stored on disk @@ -197,49 +192,20 @@ public: void Update(uint32 diff) override; - /// Set a server rate (see #Rates) - void setRate(Rates rate, float value) override { _rate_values[rate] = value; } - /// Get a server rate (see #Rates) - [[nodiscard]] float getRate(Rates rate) const override { return _rate_values[rate]; } + void setRate(ServerConfigs index, float value) override; + float getRate(ServerConfigs index) const override; - /// Set a server configuration element (see #WorldConfigs) - void setBoolConfig(WorldBoolConfigs index, bool value) override - { - if (index < BOOL_CONFIG_VALUE_COUNT) - _bool_configs[index] = value; - } + void setBoolConfig(ServerConfigs index, bool value) override; + bool getBoolConfig(ServerConfigs index) const override; - /// Get a server configuration element (see #WorldConfigs) - [[nodiscard]] bool getBoolConfig(WorldBoolConfigs index) const override - { - return index < BOOL_CONFIG_VALUE_COUNT ? _bool_configs[index] : false; - } + void setFloatConfig(ServerConfigs index, float value) override; + float getFloatConfig(ServerConfigs index) const override; - /// Set a server configuration element (see #WorldConfigs) - void setFloatConfig(WorldFloatConfigs index, float value) override - { - if (index < FLOAT_CONFIG_VALUE_COUNT) - _float_configs[index] = value; - } + void setIntConfig(ServerConfigs index, uint32 value) override; + uint32 getIntConfig(ServerConfigs index) const override; - /// Get a server configuration element (see #WorldConfigs) - [[nodiscard]] float getFloatConfig(WorldFloatConfigs index) const override - { - return index < FLOAT_CONFIG_VALUE_COUNT ? _float_configs[index] : 0; - } - - /// Set a server configuration element (see #WorldConfigs) - void setIntConfig(WorldIntConfigs index, uint32 value) override - { - if (index < INT_CONFIG_VALUE_COUNT) - _int_configs[index] = value; - } - - /// Get a server configuration element (see #WorldConfigs) - [[nodiscard]] uint32 getIntConfig(WorldIntConfigs index) const override - { - return index < INT_CONFIG_VALUE_COUNT ? _int_configs[index] : 0; - } + void setStringConfig(ServerConfigs index, std::string const& value) override; + std::string_view getStringConfig(ServerConfigs index) const override; /// Are we on a "Player versus Player" server? [[nodiscard]] bool IsPvPRealm() const override; @@ -295,6 +261,8 @@ protected: void CalendarDeleteOldEvents(); void ResetGuildCap(); private: + WorldConfig _worldConfig; + static std::atomic_long _stopEvent; static uint8 _exitCode; uint32 _shutdownTimer; @@ -308,12 +276,6 @@ private: IntervalTimer _timers[WUPDATE_COUNT]; Seconds _mail_expire_check_timer; - std::string _newCharString; - - float _rate_values[MAX_RATES]; - uint32 _int_configs[INT_CONFIG_VALUE_COUNT]; - bool _bool_configs[BOOL_CONFIG_VALUE_COUNT]; - float _float_configs[FLOAT_CONFIG_VALUE_COUNT]; AccountTypes _allowedSecurityLevel; LocaleConstant _defaultDbcLocale; // from config for one from loaded DBC locales uint32 _availableDbcLocaleMask; // by loaded DBC @@ -341,6 +303,7 @@ private: // used versions std::string _dbVersion; + uint32 _dbClientCacheVersion; void ProcessQueryCallbacks(); QueryCallbackProcessor _queryProcessor; diff --git a/src/server/game/World/WorldConfig.cpp b/src/server/game/World/WorldConfig.cpp new file mode 100644 index 000000000..1af0672c3 --- /dev/null +++ b/src/server/game/World/WorldConfig.cpp @@ -0,0 +1,658 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "Player.h" +#include "WorldConfig.h" + +void WorldConfig::BuildConfigCache() +{ + SetConfigValue(CONFIG_ALLOW_TICKETS, "AllowTickets", true); + SetConfigValue(CONFIG_DELETE_CHARACTER_TICKET_TRACE, "DeletedCharacterTicketTrace", false); + + ///- Send server info on login? + SetConfigValue(CONFIG_ENABLE_SINFO_LOGIN, "Server.LoginInfo", 0); + + ///- Read all rates from the config file + SetConfigValue(RATE_HEALTH, "Rate.Health", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0; }, "> 0"); + SetConfigValue(RATE_POWER_MANA, "Rate.Mana", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0; }, "> 0"); + SetConfigValue(RATE_POWER_RAGE_INCOME, "Rate.Rage.Income", 1); + SetConfigValue(RATE_POWER_RAGE_LOSS, "Rate.Rage.Loss", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0; }, "> 0"); + SetConfigValue(RATE_POWER_RUNICPOWER_INCOME, "Rate.RunicPower.Income", 1); + SetConfigValue(RATE_POWER_RUNICPOWER_LOSS, "Rate.RunicPower.Loss", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value > 0; }, "> 0"); + SetConfigValue(RATE_POWER_FOCUS, "Rate.Focus", 1); + SetConfigValue(RATE_POWER_ENERGY, "Rate.Energy", 1); + + SetConfigValue(RATE_SKILL_DISCOVERY, "Rate.Skill.Discovery", 1); + + SetConfigValue(RATE_DROP_ITEM_POOR, "Rate.Drop.Item.Poor", 1); + SetConfigValue(RATE_DROP_ITEM_NORMAL, "Rate.Drop.Item.Normal", 1); + SetConfigValue(RATE_DROP_ITEM_UNCOMMON, "Rate.Drop.Item.Uncommon", 1); + SetConfigValue(RATE_DROP_ITEM_RARE, "Rate.Drop.Item.Rare", 1); + SetConfigValue(RATE_DROP_ITEM_EPIC, "Rate.Drop.Item.Epic", 1); + SetConfigValue(RATE_DROP_ITEM_LEGENDARY, "Rate.Drop.Item.Legendary", 1); + SetConfigValue(RATE_DROP_ITEM_ARTIFACT, "Rate.Drop.Item.Artifact", 1); + SetConfigValue(RATE_DROP_ITEM_REFERENCED, "Rate.Drop.Item.Referenced", 1); + SetConfigValue(RATE_DROP_ITEM_REFERENCED_AMOUNT, "Rate.Drop.Item.ReferencedAmount", 1); + SetConfigValue(RATE_DROP_ITEM_GROUP_AMOUNT, "Rate.Drop.Item.GroupAmount", 1); + SetConfigValue(RATE_DROP_MONEY, "Rate.Drop.Money", 1); + + SetConfigValue(RATE_REWARD_QUEST_MONEY, "Rate.RewardQuestMoney", 1); + SetConfigValue(RATE_REWARD_BONUS_MONEY, "Rate.RewardBonusMoney", 1); + SetConfigValue(RATE_XP_KILL, "Rate.XP.Kill", 1); + SetConfigValue(RATE_XP_BG_KILL_AV, "Rate.XP.BattlegroundKillAV", 1); + SetConfigValue(RATE_XP_BG_KILL_WSG, "Rate.XP.BattlegroundKillWSG", 1); + SetConfigValue(RATE_XP_BG_KILL_AB, "Rate.XP.BattlegroundKillAB", 1); + SetConfigValue(RATE_XP_BG_KILL_EOTS, "Rate.XP.BattlegroundKillEOTS", 1); + SetConfigValue(RATE_XP_BG_KILL_SOTA, "Rate.XP.BattlegroundKillSOTA", 1); + SetConfigValue(RATE_XP_BG_KILL_IC, "Rate.XP.BattlegroundKillIC", 1); + SetConfigValue(RATE_XP_QUEST, "Rate.XP.Quest", 1); + SetConfigValue(RATE_XP_QUEST_DF, "Rate.XP.Quest.DF", 1); + SetConfigValue(RATE_XP_EXPLORE, "Rate.XP.Explore", 1); + SetConfigValue(RATE_XP_PET, "Rate.XP.Pet", 1); + SetConfigValue(RATE_XP_PET_NEXT_LEVEL, "Rate.Pet.LevelXP", 0); + SetConfigValue(RATE_REPAIRCOST, "Rate.RepairCost", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + + SetConfigValue(RATE_SELLVALUE_ITEM_POOR, "Rate.SellValue.Item.Poor", 1); + SetConfigValue(RATE_SELLVALUE_ITEM_NORMAL, "Rate.SellValue.Item.Normal", 1); + SetConfigValue(RATE_SELLVALUE_ITEM_UNCOMMON, "Rate.SellValue.Item.Uncommon", 1); + SetConfigValue(RATE_SELLVALUE_ITEM_RARE, "Rate.SellValue.Item.Rare", 1); + SetConfigValue(RATE_SELLVALUE_ITEM_EPIC, "Rate.SellValue.Item.Epic", 1); + SetConfigValue(RATE_SELLVALUE_ITEM_LEGENDARY, "Rate.SellValue.Item.Legendary", 1); + SetConfigValue(RATE_SELLVALUE_ITEM_ARTIFACT, "Rate.SellValue.Item.Artifact", 1); + SetConfigValue(RATE_SELLVALUE_ITEM_HEIRLOOM, "Rate.SellValue.Item.Heirloom", 1); + + SetConfigValue(RATE_BUYVALUE_ITEM_POOR, "Rate.BuyValue.Item.Poor", 1); + SetConfigValue(RATE_BUYVALUE_ITEM_NORMAL, "Rate.BuyValue.Item.Normal", 1); + SetConfigValue(RATE_BUYVALUE_ITEM_UNCOMMON, "Rate.BuyValue.Item.Uncommon", 1); + SetConfigValue(RATE_BUYVALUE_ITEM_RARE, "Rate.BuyValue.Item.Rare", 1); + SetConfigValue(RATE_BUYVALUE_ITEM_EPIC, "Rate.BuyValue.Item.Epic", 1); + SetConfigValue(RATE_BUYVALUE_ITEM_LEGENDARY, "Rate.BuyValue.Item.Legendary", 1); + SetConfigValue(RATE_BUYVALUE_ITEM_ARTIFACT, "Rate.BuyValue.Item.Artifact", 1); + SetConfigValue(RATE_BUYVALUE_ITEM_HEIRLOOM, "Rate.BuyValue.Item.Heirloom", 1); + + SetConfigValue(RATE_REPUTATION_GAIN, "Rate.Reputation.Gain", 1); + SetConfigValue(RATE_REPUTATION_LOWLEVEL_KILL, "Rate.Reputation.LowLevel.Kill", 1); + SetConfigValue(RATE_REPUTATION_LOWLEVEL_QUEST, "Rate.Reputation.LowLevel.Quest", 1); + SetConfigValue(RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS, "Rate.Reputation.RecruitAFriendBonus", 0); + SetConfigValue(RATE_CREATURE_NORMAL_DAMAGE, "Rate.Creature.Normal.Damage", 1); + SetConfigValue(RATE_CREATURE_ELITE_ELITE_DAMAGE, "Rate.Creature.Elite.Elite.Damage", 1); + SetConfigValue(RATE_CREATURE_ELITE_RAREELITE_DAMAGE, "Rate.Creature.Elite.RAREELITE.Damage", 1); + SetConfigValue(RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE, "Rate.Creature.Elite.WORLDBOSS.Damage", 1); + SetConfigValue(RATE_CREATURE_ELITE_RARE_DAMAGE, "Rate.Creature.Elite.RARE.Damage", 1); + SetConfigValue(RATE_CREATURE_NORMAL_HP, "Rate.Creature.Normal.HP", 1); + SetConfigValue(RATE_CREATURE_ELITE_ELITE_HP, "Rate.Creature.Elite.Elite.HP", 1); + SetConfigValue(RATE_CREATURE_ELITE_RAREELITE_HP, "Rate.Creature.Elite.RAREELITE.HP", 1); + SetConfigValue(RATE_CREATURE_ELITE_WORLDBOSS_HP, "Rate.Creature.Elite.WORLDBOSS.HP", 1); + SetConfigValue(RATE_CREATURE_ELITE_RARE_HP, "Rate.Creature.Elite.RARE.HP", 1); + SetConfigValue(RATE_CREATURE_NORMAL_SPELLDAMAGE, "Rate.Creature.Normal.SpellDamage", 1); + SetConfigValue(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE, "Rate.Creature.Elite.Elite.SpellDamage", 1); + SetConfigValue(RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE, "Rate.Creature.Elite.RAREELITE.SpellDamage", 1); + SetConfigValue(RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE, "Rate.Creature.Elite.WORLDBOSS.SpellDamage", 1); + SetConfigValue(RATE_CREATURE_ELITE_RARE_SPELLDAMAGE, "Rate.Creature.Elite.RARE.SpellDamage", 1); + SetConfigValue(RATE_CREATURE_AGGRO, "Rate.Creature.Aggro", 1); + SetConfigValue(RATE_REST_INGAME, "Rate.Rest.InGame", 1); + SetConfigValue(RATE_REST_OFFLINE_IN_TAVERN_OR_CITY, "Rate.Rest.Offline.InTavernOrCity", 1); + SetConfigValue(RATE_REST_OFFLINE_IN_WILDERNESS, "Rate.Rest.Offline.InWilderness", 1); + SetConfigValue(RATE_REST_MAX_BONUS, "Rate.Rest.MaxBonus", 1); + SetConfigValue(RATE_DAMAGE_FALL, "Rate.Damage.Fall", 1); + SetConfigValue(RATE_AUCTION_TIME, "Rate.Auction.Time", 1); + SetConfigValue(RATE_AUCTION_DEPOSIT, "Rate.Auction.Deposit", 1); + SetConfigValue(RATE_AUCTION_CUT, "Rate.Auction.Cut", 1); + SetConfigValue(RATE_HONOR, "Rate.Honor", 1); + SetConfigValue(RATE_ARENA_POINTS, "Rate.ArenaPoints", 1); + SetConfigValue(RATE_INSTANCE_RESET_TIME, "Rate.InstanceResetTime", 1); + + SetConfigValue(RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE, "Rate.MissChanceMultiplier.TargetCreature", 11); + SetConfigValue(RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER, "Rate.MissChanceMultiplier.TargetPlayer", 7); + SetConfigValue(CONFIG_MISS_CHANCE_MULTIPLIER_ONLY_FOR_PLAYERS, "Rate.MissChanceMultiplier.OnlyAffectsPlayer", false); + + SetConfigValue(RATE_TALENT, "Rate.Talent", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + SetConfigValue(RATE_TALENT_PET, "Rate.Talent.Pet", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + // Controls Player movespeed rate. + SetConfigValue(RATE_MOVESPEED_PLAYER, "Rate.MoveSpeed.Player", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + + // Controls all npc movespeed rate. + SetConfigValue(RATE_MOVESPEED_NPC, "Rate.MoveSpeed.NPC", 1, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + + SetConfigValue(RATE_CORPSE_DECAY_LOOTED, "Rate.Corpse.Decay.Looted", 0); + + SetConfigValue(RATE_DURABILITY_LOSS_ON_DEATH, "DurabilityLoss.OnDeath", 10, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0 && value <= 100; }, ">= 0 && <= 100"); + + SetConfigValue(RATE_DURABILITY_LOSS_DAMAGE, "DurabilityLossChance.Damage", 0, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + SetConfigValue(RATE_DURABILITY_LOSS_ABSORB, "DurabilityLossChance.Absorb", 0, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + SetConfigValue(RATE_DURABILITY_LOSS_PARRY, "DurabilityLossChance.Parry", 0, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + SetConfigValue(RATE_DURABILITY_LOSS_BLOCK, "DurabilityLossChance.Block", 0, ConfigValueCache::Reloadable::Yes, [](float const& value) { return value >= 0; }, ">= 0"); + + ///- Read other configuration items from the config file + + SetConfigValue(CONFIG_DURABILITY_LOSS_IN_PVP, "DurabilityLoss.InPvP", false); + + SetConfigValue(CONFIG_COMPRESSION, "Compression", 1, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value < 10; }, "> 0 && < 10"); + + SetConfigValue(CONFIG_ADDON_CHANNEL, "AddonChannel", true); + SetConfigValue(CONFIG_CLEAN_CHARACTER_DB, "CleanCharacterDB", false); + SetConfigValue(CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS, "PersistentCharacterCleanFlags", 0); + SetConfigValue(CONFIG_CHAT_CHANNEL_LEVEL_REQ, "ChatLevelReq.Channel", 1); + SetConfigValue(CONFIG_CHAT_WHISPER_LEVEL_REQ, "ChatLevelReq.Whisper", 1); + SetConfigValue(CONFIG_CHAT_SAY_LEVEL_REQ, "ChatLevelReq.Say", 1); + SetConfigValue(CONFIG_PARTY_LEVEL_REQ, "PartyLevelReq", 1); + SetConfigValue(CONFIG_TRADE_LEVEL_REQ, "LevelReq.Trade", 1); + SetConfigValue(CONFIG_TICKET_LEVEL_REQ, "LevelReq.Ticket", 1); + SetConfigValue(CONFIG_AUCTION_LEVEL_REQ, "LevelReq.Auction", 1); + SetConfigValue(CONFIG_MAIL_LEVEL_REQ, "LevelReq.Mail", 1); + SetConfigValue(CONFIG_ALLOW_PLAYER_COMMANDS, "AllowPlayerCommands", 1); + SetConfigValue(CONFIG_PRESERVE_CUSTOM_CHANNELS, "PreserveCustomChannels", false); + SetConfigValue(CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION, "PreserveCustomChannelDuration", 14); + SetConfigValue(CONFIG_INTERVAL_SAVE, "PlayerSaveInterval", 15); + SetConfigValue(CONFIG_INTERVAL_DISCONNECT_TOLERANCE, "DisconnectToleranceInterval", 0); + SetConfigValue(CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, "PlayerSave.Stats.SaveOnlyOnLogout", true); + + SetConfigValue(CONFIG_MIN_LEVEL_STAT_SAVE, "PlayerSave.Stats.MinLevel", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value < MAX_LEVEL; }, "< MAX_LEVEL"); + + SetConfigValue(CONFIG_INTERVAL_MAPUPDATE, "MapUpdateInterval", 10, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value >= MIN_MAP_UPDATE_DELAY; }, ">= MIN_MAP_UPDATE_DELAY"); + + SetConfigValue(CONFIG_INTERVAL_CHANGEWEATHER, "ChangeWeatherInterval", 10); + + SetConfigValue(CONFIG_PORT_WORLD, "WorldServerPort", 8085, ConfigValueCache::Reloadable::No); + + SetConfigValue(CONFIG_CLOSE_IDLE_CONNECTIONS, "CloseIdleConnections", true); + SetConfigValue(CONFIG_SOCKET_TIMEOUTTIME, "SocketTimeOutTime", 900000); + SetConfigValue(CONFIG_SOCKET_TIMEOUTTIME_ACTIVE, "SocketTimeOutTimeActive", 60000); + SetConfigValue(CONFIG_SESSION_ADD_DELAY, "SessionAddDelay", 10000); + + SetConfigValue(CONFIG_GROUP_XP_DISTANCE, "MaxGroupXPDistance", 74); + SetConfigValue(CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE, "MaxRecruitAFriendBonusDistance", 100); + + SetConfigValue(CONFIG_SIGHT_MONSTER, "MonsterSight", 50); + + SetConfigValue(CONFIG_GAME_TYPE, "GameType", 0, ConfigValueCache::Reloadable::No); + SetConfigValue(CONFIG_REALM_ZONE, "RealmZone", REALM_ZONE_DEVELOPMENT, ConfigValueCache::Reloadable::No); + + SetConfigValue(CONFIG_STRICT_NAMES_RESERVED, "StrictNames.Reserved", true); + SetConfigValue(CONFIG_STRICT_NAMES_PROFANITY, "StrictNames.Profanity", true); + SetConfigValue(CONFIG_STRICT_PLAYER_NAMES, "StrictPlayerNames", 0); + SetConfigValue(CONFIG_STRICT_CHARTER_NAMES, "StrictCharterNames", 0); + SetConfigValue(CONFIG_STRICT_CHANNEL_NAMES, "StrictChannelNames", 0); + SetConfigValue(CONFIG_STRICT_PET_NAMES, "StrictPetNames", 0); + + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_ACCOUNTS, "AllowTwoSide.Accounts", true); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR, "AllowTwoSide.Interaction.Calendar", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT, "AllowTwoSide.Interaction.Chat", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL, "AllowTwoSide.Interaction.Channel", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP, "AllowTwoSide.Interaction.Group", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, "AllowTwoSide.Interaction.Guild", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA, "AllowTwoSide.Interaction.Arena", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, "AllowTwoSide.Interaction.Auction", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, "AllowTwoSide.Interaction.Mail", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_WHO_LIST, "AllowTwoSide.WhoList", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND, "AllowTwoSide.AddFriend", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_TRADE, "AllowTwoSide.Trade", false); + SetConfigValue(CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE, "AllowTwoSide.Interaction.Emote", false); + + SetConfigValue(CONFIG_MIN_PLAYER_NAME, "MinPlayerName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_PLAYER_NAME; }, "> 0 && <= MAX_PLAYER_NAME"); + SetConfigValue(CONFIG_MIN_CHARTER_NAME, "MinCharterName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_CHARTER_NAME; }, "> 0 && <= MAX_CHARTER_NAME"); + SetConfigValue(CONFIG_MIN_PET_NAME, "MinPetName", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= MAX_PET_NAME; }, "> 0 && <= MAX_PET_NAME"); + + SetConfigValue(CONFIG_CHARTER_COST_GUILD, "Guild.CharterCost", 1000); + SetConfigValue(CONFIG_CHARTER_COST_ARENA_2v2, "ArenaTeam.CharterCost.2v2", 800000); + SetConfigValue(CONFIG_CHARTER_COST_ARENA_3v3, "ArenaTeam.CharterCost.3v3", 1200000); + SetConfigValue(CONFIG_CHARTER_COST_ARENA_5v5, "ArenaTeam.CharterCost.5v5", 2000000); + + SetConfigValue(CONFIG_MAX_WHO_LIST_RETURN, "MaxWhoListReturns", 49); + + SetConfigValue(CONFIG_CHARACTER_CREATING_DISABLED, "CharacterCreating.Disabled", 0); + SetConfigValue(CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK, "CharacterCreating.Disabled.RaceMask", 0); + + SetConfigValue(CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK, "CharacterCreating.Disabled.ClassMask", 0); + + SetConfigValue(CONFIG_CHARACTERS_PER_REALM, "CharactersPerRealm", 10, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= 10; }, "> 0 && <= 10"); + + // must be after CONFIG_CHARACTERS_PER_REALM + SetConfigValue(CONFIG_CHARACTERS_PER_ACCOUNT, "CharactersPerAccount", 50, ConfigValueCache::Reloadable::Yes, [this](uint32 const& value) { return value >= GetConfigValue(CONFIG_CHARACTERS_PER_REALM); }, ">= CONFIG_CHARACTERS_PER_REALM"); + SetConfigValue(CONFIG_HEROIC_CHARACTERS_PER_REALM, "HeroicCharactersPerRealm", 1, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 10; }, "<= 10"); + + SetConfigValue(CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER, "CharacterCreating.MinLevelForHeroicCharacter", 55); + + SetConfigValue(CONFIG_SKIP_CINEMATICS, "SkipCinematics", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 2; }, "<= 2"); + + SetConfigValue(CONFIG_MAX_PLAYER_LEVEL, "MaxPlayerLevel", DEFAULT_MAX_LEVEL, ConfigValueCache::Reloadable::No, [](uint32 const& value) { return value > 0 && value <= MAX_LEVEL; }, "> 0 && <= MAX_LEVEL"); + + SetConfigValue(CONFIG_MIN_DUALSPEC_LEVEL, "MinDualSpecLevel", 40); + + SetConfigValue(CONFIG_START_PLAYER_LEVEL, "StartPlayerLevel", 1, ConfigValueCache::Reloadable::Yes, [this](uint32 const& value) { return value > 0 && value <= GetConfigValue(CONFIG_MAX_PLAYER_LEVEL); }, "> 0 && <= CONFIG_MAX_PLAYER_LEVEL"); + SetConfigValue(CONFIG_START_HEROIC_PLAYER_LEVEL, "StartHeroicPlayerLevel", 55, ConfigValueCache::Reloadable::Yes, [this](uint32 const& value) { return value > 0 && value <= GetConfigValue(CONFIG_MAX_PLAYER_LEVEL); }, "> 0 && <= CONFIG_MAX_PLAYER_LEVEL"); + + SetConfigValue(CONFIG_START_PLAYER_MONEY, "StartPlayerMoney", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= MAX_MONEY_AMOUNT; }, "<= MAX_MONEY_AMOUNT"); + SetConfigValue(CONFIG_START_HEROIC_PLAYER_MONEY, "StartHeroicPlayerMoney", 2000, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= MAX_MONEY_AMOUNT; }, "<= MAX_MONEY_AMOUNT"); + + SetConfigValue(CONFIG_MAX_HONOR_POINTS, "MaxHonorPoints", 75000); + + SetConfigValue(CONFIG_MAX_HONOR_POINTS_MONEY_PER_POINT, "MaxHonorPointsMoneyPerPoint", 0); + + SetConfigValue(CONFIG_START_HONOR_POINTS, "StartHonorPoints", 0, ConfigValueCache::Reloadable::Yes, [this](uint32 const& value) { return value <= GetConfigValue(CONFIG_MAX_HONOR_POINTS); }, "<= CONFIG_MAX_HONOR_POINTS"); + + SetConfigValue(CONFIG_MAX_ARENA_POINTS, "MaxArenaPoints", 10000); + + SetConfigValue(CONFIG_START_ARENA_POINTS, "StartArenaPoints", 0, ConfigValueCache::Reloadable::Yes, [this](uint32 const& value) { return value <= GetConfigValue(CONFIG_MAX_ARENA_POINTS); }, "<= CONFIG_MAX_ARENA_POINTS"); + + SetConfigValue(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL, "RecruitAFriend.MaxLevel", 60, ConfigValueCache::Reloadable::Yes, [this](uint32 const& value) { return value <= GetConfigValue(CONFIG_MAX_PLAYER_LEVEL); }, "<= CONFIG_MAX_PLAYER_LEVEL"); + + SetConfigValue(CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL_DIFFERENCE, "RecruitAFriend.MaxDifference", 4); + SetConfigValue(CONFIG_ALL_TAXI_PATHS, "AllFlightPaths", false); + SetConfigValue(CONFIG_INSTANT_TAXI, "InstantFlightPaths", 0); + + SetConfigValue(CONFIG_INSTANCE_IGNORE_LEVEL, "Instance.IgnoreLevel", false); + SetConfigValue(CONFIG_INSTANCE_IGNORE_RAID, "Instance.IgnoreRaid", false); + SetConfigValue(CONFIG_INSTANCE_GMSUMMON_PLAYER, "Instance.GMSummonPlayer", false); + SetConfigValue(CONFIG_INSTANCE_SHARED_ID, "Instance.SharedNormalHeroicId", false); + + SetConfigValue(CONFIG_INSTANCE_RESET_TIME_HOUR, "Instance.ResetTimeHour", 4); + SetConfigValue(CONFIG_INSTANCE_RESET_TIME_RELATIVE_TIMESTAMP, "Instance.ResetTimeRelativeTimestamp", 1135814400); + SetConfigValue(CONFIG_INSTANCE_UNLOAD_DELAY, "Instance.UnloadDelay", 30); + + SetConfigValue(CONFIG_MAX_PRIMARY_TRADE_SKILL, "MaxPrimaryTradeSkill", 2); + SetConfigValue(CONFIG_MIN_PETITION_SIGNS, "MinPetitionSigns", 9, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 9; }, "<= 9"); + + SetConfigValue(CONFIG_GM_LOGIN_STATE, "GM.LoginState", 2); + SetConfigValue(CONFIG_GM_VISIBLE_STATE, "GM.Visible", 2); + SetConfigValue(CONFIG_GM_CHAT, "GM.Chat", 2); + SetConfigValue(CONFIG_GM_WHISPERING_TO, "GM.WhisperingTo", 2); + + SetConfigValue(CONFIG_GM_LEVEL_IN_GM_LIST, "GM.InGMList.Level", SEC_ADMINISTRATOR); + SetConfigValue(CONFIG_GM_LEVEL_IN_WHO_LIST, "GM.InWhoList.Level", SEC_ADMINISTRATOR); + SetConfigValue(CONFIG_START_GM_LEVEL, "GM.StartLevel", 1, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= MAX_LEVEL; }, "<= MAX_LEVEL"); + + SetConfigValue(CONFIG_ALLOW_GM_GROUP, "GM.AllowInvite", false); + SetConfigValue(CONFIG_ALLOW_GM_FRIEND, "GM.AllowFriend", false); + SetConfigValue(CONFIG_GM_LOWER_SECURITY, "GM.LowerSecurity", false); + SetConfigValue(CONFIG_CHANCE_OF_GM_SURVEY, "GM.TicketSystem.ChanceOfGMSurvey", 50); + + SetConfigValue(CONFIG_GROUP_VISIBILITY, "Visibility.GroupMode", 1); + + SetConfigValue(CONFIG_OBJECT_SPARKLES, "Visibility.ObjectSparkles", true); + + SetConfigValue(CONFIG_LOW_LEVEL_REGEN_BOOST, "EnableLowLevelRegenBoost", true); + + SetConfigValue(CONFIG_OBJECT_QUEST_MARKERS, "Visibility.ObjectQuestMarkers", true); + + SetConfigValue(CONFIG_MAIL_DELIVERY_DELAY, "MailDeliveryDelay", HOUR); + + SetConfigValue(CONFIG_UPTIME_UPDATE, "UpdateUptimeInterval", 10, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0"); + + // log db cleanup interval + SetConfigValue(CONFIG_LOGDB_CLEARINTERVAL, "LogDB.Opt.ClearInterval", 10, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0"); + SetConfigValue(CONFIG_LOGDB_CLEARTIME, "LogDB.Opt.ClearTime", 1209600); + + SetConfigValue(CONFIG_TELEPORT_TIMEOUT_NEAR, "TeleportTimeoutNear", 25); + SetConfigValue(CONFIG_TELEPORT_TIMEOUT_FAR, "TeleportTimeoutFar", 45); + SetConfigValue(CONFIG_MAX_ALLOWED_MMR_DROP, "MaxAllowedMMRDrop", 500); + SetConfigValue(CONFIG_ENABLE_LOGIN_AFTER_DC, "EnableLoginAfterDC", true); + SetConfigValue(CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS, "DontCacheRandomMovementPaths", false); + + SetConfigValue(CONFIG_SKILL_CHANCE_ORANGE, "SkillChance.Orange", 100); + SetConfigValue(CONFIG_SKILL_CHANCE_YELLOW, "SkillChance.Yellow", 75); + SetConfigValue(CONFIG_SKILL_CHANCE_GREEN, "SkillChance.Green", 25); + SetConfigValue(CONFIG_SKILL_CHANCE_GREY, "SkillChance.Grey", 0); + + SetConfigValue(CONFIG_SKILL_CHANCE_MINING_STEPS, "SkillChance.MiningSteps", 0); + SetConfigValue(CONFIG_SKILL_CHANCE_SKINNING_STEPS, "SkillChance.SkinningSteps", 0); + + SetConfigValue(CONFIG_SKILL_PROSPECTING, "SkillChance.Prospecting", false); + SetConfigValue(CONFIG_SKILL_MILLING, "SkillChance.Milling", false); + + SetConfigValue(CONFIG_SKILL_GAIN_CRAFTING, "SkillGain.Crafting", 1); + + SetConfigValue(CONFIG_SKILL_GAIN_DEFENSE, "SkillGain.Defense", 1); + + SetConfigValue(CONFIG_SKILL_GAIN_GATHERING, "SkillGain.Gathering", 1); + + SetConfigValue(CONFIG_SKILL_GAIN_WEAPON, "SkillGain.Weapon", 1); + + SetConfigValue(CONFIG_MAX_OVERSPEED_PINGS, "MaxOverspeedPings", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value != 1; }, "!= 1"); + + SetConfigValue(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY, "SaveRespawnTimeImmediately", true); + SetConfigValue(CONFIG_WEATHER, "ActivateWeather", true); + + SetConfigValue(CONFIG_DISABLE_BREATHING, "DisableWaterBreath", SEC_CONSOLE); + + SetConfigValue(CONFIG_ALWAYS_MAX_SKILL_FOR_LEVEL, "AlwaysMaxSkillForLevel", false); + + SetConfigValue(CONFIG_EXPANSION, "Expansion", 2, ConfigValueCache::Reloadable::No); + + SetConfigValue(CONFIG_CHATFLOOD_MESSAGE_COUNT, "ChatFlood.MessageCount", 10); + SetConfigValue(CONFIG_CHATFLOOD_MESSAGE_DELAY, "ChatFlood.MessageDelay", 1); + SetConfigValue(CONFIG_CHATFLOOD_ADDON_MESSAGE_COUNT, "ChatFlood.AddonMessageCount", 100); + SetConfigValue(CONFIG_CHATFLOOD_ADDON_MESSAGE_DELAY, "ChatFlood.AddonMessageDelay", 1); + SetConfigValue(CONFIG_CHATFLOOD_MUTE_TIME, "ChatFlood.MuteTime", 10); + SetConfigValue(CONFIG_CHAT_MUTE_FIRST_LOGIN, "Chat.MuteFirstLogin", false); + SetConfigValue(CONFIG_CHAT_TIME_MUTE_FIRST_LOGIN, "Chat.MuteTimeFirstLogin", 120); + + SetConfigValue(CONFIG_EVENT_ANNOUNCE, "Event.Announce", 0); + + SetConfigValue(CONFIG_CREATURE_LEASH_RADIUS, "CreatureLeashRadius", 30.0f); + SetConfigValue(CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS, "CreatureFamilyFleeAssistanceRadius", 30); + SetConfigValue(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS, "CreatureFamilyAssistanceRadius", 10); + SetConfigValue(CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY, "CreatureFamilyAssistanceDelay", 2000); + SetConfigValue(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD, "CreatureFamilyAssistancePeriod", 3000); + SetConfigValue(CONFIG_CREATURE_FAMILY_FLEE_DELAY, "CreatureFamilyFleeDelay", 7000); + + SetConfigValue(CONFIG_WORLD_BOSS_LEVEL_DIFF, "WorldBossLevelDiff", 3); + + SetConfigValue(CONFIG_QUEST_ENABLE_QUEST_TRACKER, "Quests.EnableQuestTracker", false); + + // note: disable value (-1) will assigned as 0xFFFFFFF, to prevent overflow at calculations limit it to max possible player level MAX_LEVEL(100) + SetConfigValue(CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF, "Quests.LowLevelHideDiff", 4, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= MAX_LEVEL; }, "<= MAX_LEVEL"); + SetConfigValue(CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF, "Quests.HighLevelHideDiff", 7, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= MAX_LEVEL; }, "<= MAX_LEVEL"); + SetConfigValue(CONFIG_QUEST_IGNORE_RAID, "Quests.IgnoreRaid", false); + SetConfigValue(CONFIG_QUEST_IGNORE_AUTO_ACCEPT, "Quests.IgnoreAutoAccept", false); + SetConfigValue(CONFIG_QUEST_IGNORE_AUTO_COMPLETE, "Quests.IgnoreAutoComplete", false); + + SetConfigValue(CONFIG_RANDOM_BG_RESET_HOUR, "Battleground.Random.ResetHour", 6, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 23; }, "<= 23"); + SetConfigValue(CONFIG_CALENDAR_DELETE_OLD_EVENTS_HOUR, "Calendar.DeleteOldEventsHour", 6, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 23; }, "<= 23"); + SetConfigValue(CONFIG_GUILD_RESET_HOUR, "Guild.ResetHour", 6, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 23; }, "<= 23"); + + SetConfigValue(CONFIG_GUILD_BANK_INITIAL_TABS, "Guild.BankInitialTabs", 0); + SetConfigValue(CONFIG_GUILD_BANK_TAB_COST_0, "Guild.BankTabCost0", 1000000); + SetConfigValue(CONFIG_GUILD_BANK_TAB_COST_1, "Guild.BankTabCost1", 2500000); + SetConfigValue(CONFIG_GUILD_BANK_TAB_COST_2, "Guild.BankTabCost2", 5000000); + SetConfigValue(CONFIG_GUILD_BANK_TAB_COST_3, "Guild.BankTabCost3", 10000000); + SetConfigValue(CONFIG_GUILD_BANK_TAB_COST_4, "Guild.BankTabCost4", 25000000); + SetConfigValue(CONFIG_GUILD_BANK_TAB_COST_5, "Guild.BankTabCost5", 50000000); + + SetConfigValue(CONFIG_GUILD_MEMBER_LIMIT, "Guild.MemberLimit", 0); + + SetConfigValue(CONFIG_DETECT_POS_COLLISION, "DetectPosCollision", true); + + SetConfigValue(CONFIG_RESTRICTED_LFG_CHANNEL, "Channel.RestrictedLfg", true); + SetConfigValue(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL, "Channel.SilentlyGMJoin", false); + + SetConfigValue(CONFIG_TALENTS_INSPECTING, "TalentsInspecting", true); + SetConfigValue(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, "ChatFakeMessagePreventing", true); + SetConfigValue(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY, "ChatStrictLinkChecking.Severity", 0); + SetConfigValue(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK, "ChatStrictLinkChecking.Kick", 0); + + SetConfigValue(CONFIG_CORPSE_DECAY_NORMAL, "Corpse.Decay.NORMAL", 60); + SetConfigValue(CONFIG_CORPSE_DECAY_RARE, "Corpse.Decay.RARE", 300); + SetConfigValue(CONFIG_CORPSE_DECAY_ELITE, "Corpse.Decay.ELITE", 300); + SetConfigValue(CONFIG_CORPSE_DECAY_RAREELITE, "Corpse.Decay.RAREELITE", 300); + SetConfigValue(CONFIG_CORPSE_DECAY_WORLDBOSS, "Corpse.Decay.WORLDBOSS", 3600); + + SetConfigValue(CONFIG_DEATH_SICKNESS_LEVEL, "Death.SicknessLevel", 11); + SetConfigValue(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP, "Death.CorpseReclaimDelay.PvP", true); + SetConfigValue(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE, "Death.CorpseReclaimDelay.PvE", true); + SetConfigValue(CONFIG_DEATH_BONES_WORLD, "Death.Bones.World", true); + SetConfigValue(CONFIG_DEATH_BONES_BG_OR_ARENA, "Death.Bones.BattlegroundOrArena", true); + + SetConfigValue(CONFIG_DIE_COMMAND_MODE, "Die.Command.Mode", true); + + // always use declined names in the russian client + SetConfigValue(CONFIG_DECLINED_NAMES_USED, "DeclinedNames", GetConfigValue(CONFIG_REALM_ZONE) == REALM_ZONE_RUSSIAN); + + SetConfigValue(CONFIG_LISTEN_RANGE_SAY, "ListenRange.Say", 25); + SetConfigValue(CONFIG_LISTEN_RANGE_TEXTEMOTE, "ListenRange.TextEmote", 25); + SetConfigValue(CONFIG_LISTEN_RANGE_YELL, "ListenRange.Yell", 300); + + SetConfigValue(CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, "Battleground.Override.LowLevels.MinPlayers", 0); + SetConfigValue(CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG, "Battleground.DisableQuestShareInBG", false); + SetConfigValue(CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG, "Battleground.DisableReadyCheckInBG", false); + SetConfigValue(CONFIG_BATTLEGROUND_CAST_DESERTER, "Battleground.CastDeserter", true); + SetConfigValue(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE, "Battleground.QueueAnnouncer.Enable", false); + SetConfigValue(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_LEVEL, "Battleground.QueueAnnouncer.Limit.MinLevel", 0); + SetConfigValue(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS, "Battleground.QueueAnnouncer.Limit.MinPlayers", 3); + SetConfigValue(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY, "Battleground.QueueAnnouncer.SpamProtection.Delay", 30); + SetConfigValue(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY, "Battleground.QueueAnnouncer.PlayerOnly", false); + SetConfigValue(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED, "Battleground.QueueAnnouncer.Timed", false); + SetConfigValue(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER, "Battleground.QueueAnnouncer.Timer", 30000); + SetConfigValue(CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE, "Battleground.StoreStatistics.Enable", false); + SetConfigValue(CONFIG_BATTLEGROUND_TRACK_DESERTERS, "Battleground.TrackDeserters.Enable", false); + SetConfigValue(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, "Battleground.PrematureFinishTimer", 5); + SetConfigValue(CONFIG_BATTLEGROUND_INVITATION_TYPE, "Battleground.InvitationType", 0); + SetConfigValue(CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, "Battleground.PremadeGroupWaitForMatch", 30); + SetConfigValue(CONFIG_BG_XP_FOR_KILL, "Battleground.GiveXPForKills", false); + SetConfigValue(CONFIG_BATTLEGROUND_REPORT_AFK_TIMER, "Battleground.ReportAFK.Timer", 4); + SetConfigValue(CONFIG_BATTLEGROUND_REPORT_AFK, "Battleground.ReportAFK", 3, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0 && value <= 9; }, "> 0 && value <= 9"); + SetConfigValue(CONFIG_BATTLEGROUND_PLAYER_RESPAWN, "Battleground.PlayerRespawn", 30, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value >= 3; }, ">= 3"); + SetConfigValue(CONFIG_BATTLEGROUND_RESTORATION_BUFF_RESPAWN, "Battleground.RestorationBuffRespawn", 20, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0"); + SetConfigValue(CONFIG_BATTLEGROUND_BERSERKING_BUFF_RESPAWN, "Battleground.BerserkingBuffRespawn", 120, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0"); + SetConfigValue(CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN, "Battleground.SpeedBuffRespawn", 150, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0"); + + SetConfigValue(CONFIG_BATTLEGROUND_WARSONG_FLAGS, "Battleground.Warsong.Flags", 3); + SetConfigValue(CONFIG_BATTLEGROUND_ARATHI_CAPTUREPOINTS, "Battleground.Arathi.CapturePoints", 1600); + SetConfigValue(CONFIG_BATTLEGROUND_ALTERAC_REINFORCEMENTS, "Battleground.Alterac.Reinforcements", 600); + SetConfigValue(CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH, "Battleground.Alterac.ReputationOnBossDeath", 350); + SetConfigValue(CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS, "Battleground.EyeOfTheStorm.CapturePoints", 1600); + + SetConfigValue(CONFIG_ARENA_MAX_RATING_DIFFERENCE, "Arena.MaxRatingDifference", 150); + SetConfigValue(CONFIG_ARENA_RATING_DISCARD_TIMER, "Arena.RatingDiscardTimer", 10); + SetConfigValue(CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, "Arena.PreviousOpponentsDiscardTimer", 2); + SetConfigValue(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, "Arena.AutoDistributePoints", false); + SetConfigValue(CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, "Arena.AutoDistributeInterval", 7); + SetConfigValue(CONFIG_ARENA_GAMES_REQUIRED, "Arena.GamesRequired", 10); + SetConfigValue(CONFIG_ARENA_START_RATING, "Arena.ArenaStartRating", 0); + SetConfigValue(CONFIG_LEGACY_ARENA_POINTS_CALC, "Arena.LegacyArenaPoints", 0); + SetConfigValue(CONFIG_ARENA_START_PERSONAL_RATING, "Arena.ArenaStartPersonalRating", 0); + SetConfigValue(CONFIG_ARENA_START_MATCHMAKER_RATING, "Arena.ArenaStartMatchmakerRating", 1500); + SetConfigValue(CONFIG_ARENA_WIN_RATING_MODIFIER_1, "Arena.ArenaWinRatingModifier1", 48); + SetConfigValue(CONFIG_ARENA_WIN_RATING_MODIFIER_2, "Arena.ArenaWinRatingModifier2", 24); + SetConfigValue(CONFIG_ARENA_LOSE_RATING_MODIFIER, "Arena.ArenaLoseRatingModifier", 24); + SetConfigValue(CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER, "Arena.ArenaMatchmakerRatingModifier", 24); + SetConfigValue(CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE, "Arena.QueueAnnouncer.Enable", false); + SetConfigValue(CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY, "Arena.QueueAnnouncer.PlayerOnly", false); + SetConfigValue(CONFIG_ARENA_QUEUE_ANNOUNCER_DETAIL, "Arena.QueueAnnouncer.Detail", 3); + + SetConfigValue(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN, "OffhandCheckAtSpellUnlearn", true); + SetConfigValue(CONFIG_CREATURE_STOP_FOR_PLAYER, "Creature.MovingStopTimeForPlayer", 3); + + SetConfigValue(CONFIG_WATER_BREATH_TIMER, "WaterBreath.Timer", 180000, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0"); + + SetConfigValue(CONFIG_CLIENTCACHE_VERSION, "ClientCacheVersion", 0); + + SetConfigValue(CONFIG_INSTANT_LOGOUT, "InstantLogout", SEC_MODERATOR); + + SetConfigValue(CONFIG_GUILD_EVENT_LOG_COUNT, "Guild.EventLogRecordsCount", GUILD_EVENTLOG_MAX_RECORDS, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= GUILD_EVENTLOG_MAX_RECORDS; }, "<= GUILD_EVENTLOG_MAX_RECORDS"); + SetConfigValue(CONFIG_GUILD_BANK_EVENT_LOG_COUNT, "Guild.BankEventLogRecordsCount", GUILD_BANKLOG_MAX_RECORDS, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= GUILD_BANKLOG_MAX_RECORDS; }, "<= GUILD_BANKLOG_MAX_RECORDS"); + + ///- Load the CharDelete related config options + SetConfigValue(CONFIG_CHARDELETE_METHOD, "CharDelete.Method", 0); + SetConfigValue(CONFIG_CHARDELETE_MIN_LEVEL, "CharDelete.MinLevel", 0); + SetConfigValue(CONFIG_CHARDELETE_KEEP_DAYS, "CharDelete.KeepDays", 30); + + ///- Load the ItemDelete related config options + SetConfigValue(CONFIG_ITEMDELETE_METHOD, "ItemDelete.Method", 0); + SetConfigValue(CONFIG_ITEMDELETE_VENDOR, "ItemDelete.Vendor", 0); + SetConfigValue(CONFIG_ITEMDELETE_QUALITY, "ItemDelete.Quality", 3); + SetConfigValue(CONFIG_ITEMDELETE_ITEM_LEVEL, "ItemDelete.ItemLevel", 80); + SetConfigValue(CONFIG_ITEMDELETE_KEEP_DAYS, "ItemDelete.KeepDays", 0); + + SetConfigValue(CONFIG_FFA_PVP_TIMER, "FFAPvPTimer", 30); + + SetConfigValue(CONFIG_LOOT_NEED_BEFORE_GREED_ILVL_RESTRICTION, "LootNeedBeforeGreedILvlRestriction", 70); + + SetConfigValue(CONFIG_PLAYER_SETTINGS_ENABLED, "EnablePlayerSettings", 0); + + SetConfigValue(CONFIG_ALLOW_JOIN_BG_AND_LFG, "JoinBGAndLFG.Enable", false); + + SetConfigValue(CONFIG_LEAVE_GROUP_ON_LOGOUT, "LeaveGroupOnLogout.Enabled", false); + + SetConfigValue(CONFIG_QUEST_POI_ENABLED, "QuestPOI.Enabled", true); + + SetConfigValue(CONFIG_CHANGE_FACTION_MAX_MONEY, "ChangeFaction.MaxMoney", 0); + + SetConfigValue(CONFIG_ALLOWS_RANK_MOD_FOR_PET_HEALTH, "Pet.RankMod.Health", true); + + SetConfigValue(CONFIG_MUNCHING_BLIZZLIKE, "MunchingBlizzlike.Enabled", true); + + SetConfigValue(CONFIG_ENABLE_DAZE, "Daze.Enabled", true); + + SetConfigValue(CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD, "DailyRBGArenaPoints.MinLevel", 71); + + // Respawn + SetConfigValue(CONFIG_RESPAWN_DYNAMICRATE_CREATURE, "Respawn.DynamicRateCreature", 1); + SetConfigValue(CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE, "Respawn.DynamicMinimumCreature", 10); + + SetConfigValue(CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT, "Respawn.DynamicRateGameObject", 1); + SetConfigValue(CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT, "Respawn.DynamicMinimumGameObject", 10); + + SetConfigValue(CONFIG_VMAP_INDOOR_CHECK, "vmap.enableIndoorCheck", true); + SetConfigValue(CONFIG_PET_LOS, "vmap.petLOS", true); + + SetConfigValue(CONFIG_VMAP_BLIZZLIKE_PVP_LOS, "vmap.BlizzlikePvPLOS", true); + SetConfigValue(CONFIG_VMAP_BLIZZLIKE_LOS_OPEN_WORLD, "vmap.BlizzlikeLOSInOpenWorld", true); + + SetConfigValue(CONFIG_START_CUSTOM_SPELLS, "PlayerStart.CustomSpells", false); + SetConfigValue(CONFIG_HONOR_AFTER_DUEL, "HonorPointsAfterDuel", 0); + SetConfigValue(CONFIG_START_ALL_EXPLORED, "PlayerStart.MapsExplored", false); + SetConfigValue(CONFIG_START_ALL_REP, "PlayerStart.AllReputation", false); + SetConfigValue(CONFIG_ALWAYS_MAXSKILL, "AlwaysMaxWeaponSkill", false); + SetConfigValue(CONFIG_PVP_TOKEN_ENABLE, "PvPToken.Enable", false); + SetConfigValue(CONFIG_PVP_TOKEN_MAP_TYPE, "PvPToken.MapAllowType", 4); + SetConfigValue(CONFIG_PVP_TOKEN_ID, "PvPToken.ItemID", 29434); + SetConfigValue(CONFIG_PVP_TOKEN_COUNT, "PvPToken.ItemCount", 1, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value > 0; }, "> 0"); + + SetConfigValue(CONFIG_NO_RESET_TALENT_COST, "NoResetTalentsCost", false); + SetConfigValue(CONFIG_TOGGLE_XP_COST, "ToggleXP.Cost", 100000); + SetConfigValue(CONFIG_SHOW_KICK_IN_WORLD, "ShowKickInWorld", false); + SetConfigValue(CONFIG_SHOW_MUTE_IN_WORLD, "ShowMuteInWorld", false); + SetConfigValue(CONFIG_SHOW_BAN_IN_WORLD, "ShowBanInWorld", false); + SetConfigValue(CONFIG_NUMTHREADS, "MapUpdate.Threads", 1); + SetConfigValue(CONFIG_MAX_RESULTS_LOOKUP_COMMANDS, "Command.LookupMaxResults", 0); + + // Warden + SetConfigValue(CONFIG_WARDEN_ENABLED, "Warden.Enabled", true); + SetConfigValue(CONFIG_WARDEN_NUM_MEM_CHECKS, "Warden.NumMemChecks", 3); + SetConfigValue(CONFIG_WARDEN_NUM_LUA_CHECKS, "Warden.NumLuaChecks", 1); + SetConfigValue(CONFIG_WARDEN_NUM_OTHER_CHECKS, "Warden.NumOtherChecks", 7); + SetConfigValue(CONFIG_WARDEN_CLIENT_BAN_DURATION, "Warden.BanDuration", 86400); + SetConfigValue(CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF, "Warden.ClientCheckHoldOff", 30); + SetConfigValue(CONFIG_WARDEN_CLIENT_FAIL_ACTION, "Warden.ClientCheckFailAction", 0); + SetConfigValue(CONFIG_WARDEN_CLIENT_RESPONSE_DELAY, "Warden.ClientResponseDelay", 600); + + // Dungeon finder + SetConfigValue(CONFIG_LFG_OPTIONSMASK, "DungeonFinder.OptionsMask", 5); + + SetConfigValue(CONFIG_LFG_CAST_DESERTER, "DungeonFinder.CastDeserter", true); + + // DBC_ItemAttributes + SetConfigValue(CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, "DBC.EnforceItemAttributes", true); + + // Max instances per hour + SetConfigValue(CONFIG_MAX_INSTANCES_PER_HOUR, "AccountInstancesPerHour", 5); + + // AutoBroadcast + SetConfigValue(CONFIG_AUTOBROADCAST, "AutoBroadcast.On", false); + SetConfigValue(CONFIG_AUTOBROADCAST_CENTER, "AutoBroadcast.Center", 0); + SetConfigValue(CONFIG_AUTOBROADCAST_INTERVAL, "AutoBroadcast.Timer", 60000); + SetConfigValue(CONFIG_AUTOBROADCAST_MIN_LEVEL_DISABLE, "AutoBroadcast.MinDisableLevel", 0); + + // MySQL ping time interval + SetConfigValue(CONFIG_DB_PING_INTERVAL, "MaxPingTime", 30); + + // misc + SetConfigValue(CONFIG_PDUMP_NO_PATHS, "PlayerDump.DisallowPaths", true); + SetConfigValue(CONFIG_PDUMP_NO_OVERWRITE, "PlayerDump.DisallowOverwrite", true); + SetConfigValue(CONFIG_ENABLE_MMAPS, "MoveMaps.Enable", true); + + // Wintergrasp + SetConfigValue(CONFIG_WINTERGRASP_ENABLE, "Wintergrasp.Enable", 1); + SetConfigValue(CONFIG_WINTERGRASP_PLR_MAX, "Wintergrasp.PlayerMax", 120); + SetConfigValue(CONFIG_WINTERGRASP_PLR_MIN, "Wintergrasp.PlayerMin", 0); + SetConfigValue(CONFIG_WINTERGRASP_PLR_MIN_LVL, "Wintergrasp.PlayerMinLvl", 75); + SetConfigValue(CONFIG_WINTERGRASP_BATTLETIME, "Wintergrasp.BattleTimer", 30); + SetConfigValue(CONFIG_WINTERGRASP_NOBATTLETIME, "Wintergrasp.NoBattleTimer", 150); + SetConfigValue(CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, "Wintergrasp.CrashRestartTimer", 10); + + SetConfigValue(CONFIG_BIRTHDAY_TIME, "BirthdayTime", 1222964635); + SetConfigValue(CONFIG_MINIGOB_MANABONK, "Minigob.Manabonk.Enable", true); + + SetConfigValue(CONFIG_ENABLE_CONTINENT_TRANSPORT, "IsContinentTransport.Enabled", true); + SetConfigValue(CONFIG_ENABLE_CONTINENT_TRANSPORT_PRELOADING, "IsPreloadedContinentTransport.Enabled", false); + + SetConfigValue(CONFIG_IP_BASED_ACTION_LOGGING, "Allow.IP.Based.Action.Logging", false); + + // Whether to use LoS from game objects + SetConfigValue(CONFIG_CHECK_GOBJECT_LOS, "CheckGameObjectLoS", true); + + SetConfigValue(CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA, "Calculate.Creature.Zone.Area.Data", false); + SetConfigValue(CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA, "Calculate.Gameoject.Zone.Area.Data", false); + + // Player can join LFG anywhere + SetConfigValue(CONFIG_LFG_LOCATION_ALL, "LFG.Location.All", false); + + // Prevent players AFK from being logged out + SetConfigValue(CONFIG_AFK_PREVENT_LOGOUT, "PreventAFKLogout", 0); + + // Preload all grids of all non-instanced maps + SetConfigValue(CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS, "PreloadAllNonInstancedMapGrids", false); + + // ICC buff override + SetConfigValue(CONFIG_ICC_BUFF_HORDE, "ICC.Buff.Horde", 73822); + SetConfigValue(CONFIG_ICC_BUFF_ALLIANCE, "ICC.Buff.Alliance", 73828); + + SetConfigValue(CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE, "SetAllCreaturesWithWaypointMovementActive", false); + + // packet spoof punishment + SetConfigValue(CONFIG_PACKET_SPOOF_BANMODE, "PacketSpoof.BanMode", 0, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value == 0; }, "== 0"); + SetConfigValue(CONFIG_PACKET_SPOOF_BANDURATION, "PacketSpoof.BanDuration", 86400); + + // Random Battleground Rewards + SetConfigValue(CONFIG_BG_REWARD_WINNER_HONOR_FIRST, "Battleground.RewardWinnerHonorFirst", 30); + SetConfigValue(CONFIG_BG_REWARD_WINNER_ARENA_FIRST, "Battleground.RewardWinnerArenaFirst", 25); + SetConfigValue(CONFIG_BG_REWARD_WINNER_HONOR_LAST, "Battleground.RewardWinnerHonorLast", 15); + SetConfigValue(CONFIG_BG_REWARD_WINNER_ARENA_LAST, "Battleground.RewardWinnerArenaLast", 0); + SetConfigValue(CONFIG_BG_REWARD_LOSER_HONOR_FIRST, "Battleground.RewardLoserHonorFirst", 5); + SetConfigValue(CONFIG_BG_REWARD_LOSER_HONOR_LAST, "Battleground.RewardLoserHonorLast", 5); + + SetConfigValue(CONFIG_WAYPOINT_MOVEMENT_STOP_TIME_FOR_PLAYER, "WaypointMovementStopTimeForPlayer", 120); + + SetConfigValue(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PRINT_MODE, "DungeonAccessRequirements.PrintMode", 1); + SetConfigValue(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PORTAL_CHECK_ILVL, "DungeonAccessRequirements.PortalAvgIlevelCheck", false); + SetConfigValue(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE, "DungeonAccessRequirements.LFGLevelDBCOverride", false); + SetConfigValue(CONFIG_DUNGEON_ACCESS_REQUIREMENTS_OPTIONAL_STRING_ID, "DungeonAccessRequirements.OptionalStringID", 0); + SetConfigValue(CONFIG_NPC_EVADE_IF_NOT_REACHABLE, "NpcEvadeIfTargetIsUnreachable", 5); + SetConfigValue(CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID, "NpcRegenHPTimeIfTargetIsUnreachable", 10); + SetConfigValue(CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID, "NpcRegenHPIfTargetIsUnreachable", true); + + //Debug + SetConfigValue(CONFIG_DEBUG_BATTLEGROUND, "Debug.Battleground", false); + SetConfigValue(CONFIG_DEBUG_ARENA, "Debug.Arena", false); + + SetConfigValue(CONFIG_GM_LEVEL_CHANNEL_MODERATION, "Channel.ModerationGMLevel", 1); + + SetConfigValue(CONFIG_SET_BOP_ITEM_TRADEABLE, "Item.SetItemTradeable", true); + + // Specifies if IP addresses can be logged to the database + SetConfigValue(CONFIG_ALLOW_LOGGING_IP_ADDRESSES_IN_DATABASE, "AllowLoggingIPAddressesInDatabase", true); + + // LFG group mechanics. + SetConfigValue(CONFIG_LFG_MAX_KICK_COUNT, "LFG.MaxKickCount", 2, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 3; }, "<= 3"); + + SetConfigValue(CONFIG_LFG_KICK_PREVENTION_TIMER, "LFG.KickPreventionTimer", 15, ConfigValueCache::Reloadable::Yes, [](uint32 const& value) { return value <= 15 * MINUTE * IN_MILLISECONDS; }, "<= 15 * MINUTE * IN_MILLISECONDS"); + + // Realm Availability + SetConfigValue(CONFIG_REALM_LOGIN_ENABLED, "World.RealmAvailability", true); + + // AH Worker threads + SetConfigValue(CONFIG_AUCTIONHOUSE_WORKERTHREADS, "AuctionHouse.WorkerThreads", 1, ConfigValueCache::Reloadable::No, [](uint32 const& value) { return value >= 1; }, ">= 1"); + + // SpellQueue + SetConfigValue(CONFIG_SPELL_QUEUE_ENABLED, "SpellQueue.Enabled", true); + SetConfigValue(CONFIG_SPELL_QUEUE_WINDOW, "SpellQueue.Window", 400); + + SetConfigValue(CONFIG_SUNSREACH_COUNTER_MAX, "Sunsreach.CounterMax", 10000); + + SetConfigValue(CONFIG_NEW_CHAR_STRING, "PlayerStart.String", ""); +} diff --git a/src/server/game/World/WorldConfig.h b/src/server/game/World/WorldConfig.h new file mode 100644 index 000000000..7474df38e --- /dev/null +++ b/src/server/game/World/WorldConfig.h @@ -0,0 +1,487 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by the + * Free Software Foundation; either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef WORLDCONFIG_H +#define WORLDCONFIG_H + +#include "ConfigValueCache.h" + +enum ServerConfigs +{ + CONFIG_DURABILITY_LOSS_IN_PVP = 0, + CONFIG_ADDON_CHANNEL, + CONFIG_ALLOW_PLAYER_COMMANDS, + CONFIG_CLEAN_CHARACTER_DB, + CONFIG_STATS_SAVE_ONLY_ON_LOGOUT, + CONFIG_ALLOW_TWO_SIDE_ACCOUNTS, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_CALENDAR, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_ARENA, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_MAIL, + CONFIG_ALLOW_TWO_SIDE_WHO_LIST, + CONFIG_ALLOW_TWO_SIDE_ADD_FRIEND, + CONFIG_ALLOW_TWO_SIDE_TRADE, + CONFIG_ALL_TAXI_PATHS, + CONFIG_INSTANCE_IGNORE_LEVEL, + CONFIG_INSTANCE_IGNORE_RAID, + CONFIG_INSTANCE_GMSUMMON_PLAYER, + CONFIG_INSTANCE_SHARED_ID, + CONFIG_ALLOW_GM_GROUP, + CONFIG_ALLOW_GM_FRIEND, + CONFIG_GM_LOWER_SECURITY, + CONFIG_SKILL_PROSPECTING, + CONFIG_SKILL_MILLING, + CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY, + CONFIG_WEATHER, + CONFIG_ALWAYS_MAX_SKILL_FOR_LEVEL, + CONFIG_QUEST_IGNORE_RAID, + CONFIG_DETECT_POS_COLLISION, + CONFIG_RESTRICTED_LFG_CHANNEL, + CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL, + CONFIG_TALENTS_INSPECTING, + CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, + CONFIG_CHAT_MUTE_FIRST_LOGIN, + CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP, + CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE, + CONFIG_DEATH_BONES_WORLD, + CONFIG_DEATH_BONES_BG_OR_ARENA, + CONFIG_DIE_COMMAND_MODE, + CONFIG_DECLINED_NAMES_USED, + CONFIG_BATTLEGROUND_DISABLE_QUEST_SHARE_IN_BG, + CONFIG_BATTLEGROUND_DISABLE_READY_CHECK_IN_BG, + CONFIG_BATTLEGROUND_CAST_DESERTER, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMED, + CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE, + CONFIG_BATTLEGROUND_TRACK_DESERTERS, + CONFIG_BG_XP_FOR_KILL, + CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS, + CONFIG_ARENA_QUEUE_ANNOUNCER_ENABLE, + CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY, + CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN, + CONFIG_VMAP_INDOOR_CHECK, + CONFIG_PET_LOS, + CONFIG_START_CUSTOM_SPELLS, + CONFIG_START_ALL_EXPLORED, + CONFIG_START_ALL_REP, + CONFIG_ALWAYS_MAXSKILL, + CONFIG_PVP_TOKEN_ENABLE, + CONFIG_NO_RESET_TALENT_COST, + CONFIG_SHOW_KICK_IN_WORLD, + CONFIG_SHOW_MUTE_IN_WORLD, + CONFIG_SHOW_BAN_IN_WORLD, + CONFIG_AUTOBROADCAST, + CONFIG_ALLOW_TICKETS, + CONFIG_DELETE_CHARACTER_TICKET_TRACE, + CONFIG_LFG_CAST_DESERTER, + CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, + CONFIG_PRESERVE_CUSTOM_CHANNELS, + CONFIG_PDUMP_NO_PATHS, + CONFIG_PDUMP_NO_OVERWRITE, + CONFIG_ENABLE_MMAPS, + CONFIG_ENABLE_LOGIN_AFTER_DC, + CONFIG_DONT_CACHE_RANDOM_MOVEMENT_PATHS, + CONFIG_QUEST_IGNORE_AUTO_ACCEPT, + CONFIG_QUEST_IGNORE_AUTO_COMPLETE, + CONFIG_QUEST_ENABLE_QUEST_TRACKER, + CONFIG_WARDEN_ENABLED, + CONFIG_ENABLE_CONTINENT_TRANSPORT, + CONFIG_ENABLE_CONTINENT_TRANSPORT_PRELOADING, + CONFIG_MINIGOB_MANABONK, + CONFIG_IP_BASED_ACTION_LOGGING, + CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA, + CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA, + CONFIG_CHECK_GOBJECT_LOS, + CONFIG_CLOSE_IDLE_CONNECTIONS, + CONFIG_LFG_LOCATION_ALL, + CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS, + CONFIG_ALLOW_TWO_SIDE_INTERACTION_EMOTE, + CONFIG_ITEMDELETE_METHOD, + CONFIG_ITEMDELETE_VENDOR, + CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE, + CONFIG_DEBUG_BATTLEGROUND, + CONFIG_DEBUG_ARENA, + CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PORTAL_CHECK_ILVL, + CONFIG_DUNGEON_ACCESS_REQUIREMENTS_LFG_DBC_LEVEL_OVERRIDE, + CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID, + CONFIG_SET_BOP_ITEM_TRADEABLE, + CONFIG_ALLOW_LOGGING_IP_ADDRESSES_IN_DATABASE, + CONFIG_REALM_LOGIN_ENABLED, + CONFIG_PLAYER_SETTINGS_ENABLED, + CONFIG_ALLOW_JOIN_BG_AND_LFG, + CONFIG_MISS_CHANCE_MULTIPLIER_ONLY_FOR_PLAYERS, + CONFIG_LEAVE_GROUP_ON_LOGOUT, + CONFIG_QUEST_POI_ENABLED, + CONFIG_VMAP_BLIZZLIKE_PVP_LOS, + CONFIG_VMAP_BLIZZLIKE_LOS_OPEN_WORLD, + CONFIG_OBJECT_SPARKLES, + CONFIG_LOW_LEVEL_REGEN_BOOST, + CONFIG_OBJECT_QUEST_MARKERS, + CONFIG_STRICT_NAMES_RESERVED, + CONFIG_STRICT_NAMES_PROFANITY, + CONFIG_ALLOWS_RANK_MOD_FOR_PET_HEALTH, + CONFIG_MUNCHING_BLIZZLIKE, + CONFIG_ENABLE_DAZE, + CONFIG_SPELL_QUEUE_ENABLED, + CONFIG_GROUP_XP_DISTANCE, + CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE, + CONFIG_SIGHT_MONSTER, + CONFIG_LISTEN_RANGE_SAY, + CONFIG_LISTEN_RANGE_TEXTEMOTE, + CONFIG_LISTEN_RANGE_YELL, + CONFIG_CREATURE_LEASH_RADIUS, + CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS, + CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS, + CONFIG_CHANCE_OF_GM_SURVEY, + CONFIG_ARENA_WIN_RATING_MODIFIER_1, + CONFIG_ARENA_WIN_RATING_MODIFIER_2, + CONFIG_ARENA_LOSE_RATING_MODIFIER, + CONFIG_ARENA_MATCHMAKER_RATING_MODIFIER, + CONFIG_RESPAWN_DYNAMICRATE_GAMEOBJECT, + CONFIG_RESPAWN_DYNAMICRATE_CREATURE, + CONFIG_COMPRESSION, + CONFIG_INTERVAL_MAPUPDATE, + CONFIG_INTERVAL_CHANGEWEATHER, + CONFIG_INTERVAL_DISCONNECT_TOLERANCE, + CONFIG_INTERVAL_SAVE, + CONFIG_PORT_WORLD, + CONFIG_SOCKET_TIMEOUTTIME, + CONFIG_SESSION_ADD_DELAY, + CONFIG_GAME_TYPE, + CONFIG_REALM_ZONE, + CONFIG_STRICT_PLAYER_NAMES, + CONFIG_STRICT_CHARTER_NAMES, + CONFIG_STRICT_CHANNEL_NAMES, + CONFIG_STRICT_PET_NAMES, + CONFIG_MIN_PLAYER_NAME, + CONFIG_MIN_CHARTER_NAME, + CONFIG_MIN_PET_NAME, + CONFIG_CHARACTER_CREATING_DISABLED, + CONFIG_CHARACTER_CREATING_DISABLED_RACEMASK, + CONFIG_CHARACTER_CREATING_DISABLED_CLASSMASK, + CONFIG_CHARACTERS_PER_ACCOUNT, + CONFIG_CHARACTERS_PER_REALM, + CONFIG_CREATURE_STOP_FOR_PLAYER, + CONFIG_HEROIC_CHARACTERS_PER_REALM, + CONFIG_CHARACTER_CREATING_MIN_LEVEL_FOR_HEROIC_CHARACTER, + CONFIG_SKIP_CINEMATICS, + CONFIG_MAX_PLAYER_LEVEL, + CONFIG_MIN_DUALSPEC_LEVEL, + CONFIG_START_PLAYER_LEVEL, + CONFIG_START_HEROIC_PLAYER_LEVEL, + CONFIG_START_PLAYER_MONEY, + CONFIG_START_HEROIC_PLAYER_MONEY, + CONFIG_MAX_HONOR_POINTS, + CONFIG_MAX_HONOR_POINTS_MONEY_PER_POINT, + CONFIG_START_HONOR_POINTS, + CONFIG_MAX_ARENA_POINTS, + CONFIG_START_ARENA_POINTS, + CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL, + CONFIG_MAX_RECRUIT_A_FRIEND_BONUS_PLAYER_LEVEL_DIFFERENCE, + CONFIG_INSTANCE_RESET_TIME_HOUR, + CONFIG_INSTANCE_RESET_TIME_RELATIVE_TIMESTAMP, + CONFIG_INSTANCE_UNLOAD_DELAY, + CONFIG_MAX_PRIMARY_TRADE_SKILL, + CONFIG_MIN_PETITION_SIGNS, + CONFIG_GM_LOGIN_STATE, + CONFIG_GM_VISIBLE_STATE, + CONFIG_GM_CHAT, + CONFIG_GM_WHISPERING_TO, + CONFIG_GM_LEVEL_IN_GM_LIST, + CONFIG_GM_LEVEL_IN_WHO_LIST, + CONFIG_START_GM_LEVEL, + CONFIG_GROUP_VISIBILITY, + CONFIG_MAIL_DELIVERY_DELAY, + CONFIG_UPTIME_UPDATE, + CONFIG_SKILL_CHANCE_ORANGE, + CONFIG_SKILL_CHANCE_YELLOW, + CONFIG_SKILL_CHANCE_GREEN, + CONFIG_SKILL_CHANCE_GREY, + CONFIG_SKILL_CHANCE_MINING_STEPS, + CONFIG_SKILL_CHANCE_SKINNING_STEPS, + CONFIG_SKILL_GAIN_CRAFTING, + CONFIG_SKILL_GAIN_DEFENSE, + CONFIG_SKILL_GAIN_GATHERING, + CONFIG_SKILL_GAIN_WEAPON, + CONFIG_MAX_OVERSPEED_PINGS, + CONFIG_EXPANSION, + CONFIG_CHATFLOOD_MESSAGE_COUNT, + CONFIG_CHATFLOOD_MESSAGE_DELAY, + CONFIG_CHATFLOOD_ADDON_MESSAGE_COUNT, + CONFIG_CHATFLOOD_ADDON_MESSAGE_DELAY, + CONFIG_CHATFLOOD_MUTE_TIME, + CONFIG_EVENT_ANNOUNCE, + CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY, + CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD, + CONFIG_CREATURE_FAMILY_FLEE_DELAY, + CONFIG_WORLD_BOSS_LEVEL_DIFF, + CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF, + CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF, + CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY, + CONFIG_CHAT_STRICT_LINK_CHECKING_KICK, + CONFIG_CHAT_CHANNEL_LEVEL_REQ, + CONFIG_CHAT_WHISPER_LEVEL_REQ, + CONFIG_CHAT_SAY_LEVEL_REQ, + CONFIG_PARTY_LEVEL_REQ, + CONFIG_CHAT_TIME_MUTE_FIRST_LOGIN, + CONFIG_TRADE_LEVEL_REQ, + CONFIG_TICKET_LEVEL_REQ, + CONFIG_AUCTION_LEVEL_REQ, + CONFIG_MAIL_LEVEL_REQ, + CONFIG_CORPSE_DECAY_NORMAL, + CONFIG_CORPSE_DECAY_RARE, + CONFIG_CORPSE_DECAY_ELITE, + CONFIG_CORPSE_DECAY_RAREELITE, + CONFIG_CORPSE_DECAY_WORLDBOSS, + CONFIG_DEATH_SICKNESS_LEVEL, + CONFIG_INSTANT_LOGOUT, + CONFIG_DISABLE_BREATHING, + CONFIG_BATTLEGROUND_OVERRIDE_LOWLEVELS_MINPLAYERS, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_SPAM_DELAY, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_TIMER, + CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, + CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, + CONFIG_BATTLEGROUND_REPORT_AFK_TIMER, + CONFIG_BATTLEGROUND_REPORT_AFK, + CONFIG_BATTLEGROUND_INVITATION_TYPE, + CONFIG_BATTLEGROUND_PLAYER_RESPAWN, + CONFIG_BATTLEGROUND_RESTORATION_BUFF_RESPAWN, + CONFIG_BATTLEGROUND_BERSERKING_BUFF_RESPAWN, + CONFIG_BATTLEGROUND_SPEED_BUFF_RESPAWN, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_LEVEL, + CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_LIMIT_MIN_PLAYERS, + CONFIG_BATTLEGROUND_WARSONG_FLAGS, + CONFIG_BATTLEGROUND_ARATHI_CAPTUREPOINTS, + CONFIG_BATTLEGROUND_ALTERAC_REINFORCEMENTS, + CONFIG_BATTLEGROUND_ALTERAC_REP_ONBOSSDEATH, + CONFIG_BATTLEGROUND_EYEOFTHESTORM_CAPTUREPOINTS, + CONFIG_WINTERGRASP_ENABLE, + CONFIG_ARENA_MAX_RATING_DIFFERENCE, + CONFIG_ARENA_RATING_DISCARD_TIMER, + CONFIG_ARENA_PREV_OPPONENTS_DISCARD_TIMER, + CONFIG_ARENA_AUTO_DISTRIBUTE_INTERVAL_DAYS, + CONFIG_ARENA_GAMES_REQUIRED, + CONFIG_ARENA_START_RATING, + CONFIG_LEGACY_ARENA_POINTS_CALC, + CONFIG_ARENA_START_PERSONAL_RATING, + CONFIG_ARENA_START_MATCHMAKER_RATING, + CONFIG_ARENA_QUEUE_ANNOUNCER_DETAIL, + CONFIG_HONOR_AFTER_DUEL, + CONFIG_PVP_TOKEN_MAP_TYPE, + CONFIG_PVP_TOKEN_ID, + CONFIG_PVP_TOKEN_COUNT, + CONFIG_ENABLE_SINFO_LOGIN, + CONFIG_NUMTHREADS, + CONFIG_LOGDB_CLEARINTERVAL, + CONFIG_LOGDB_CLEARTIME, + CONFIG_TELEPORT_TIMEOUT_NEAR, + CONFIG_TELEPORT_TIMEOUT_FAR, + CONFIG_MAX_ALLOWED_MMR_DROP, + CONFIG_CLIENTCACHE_VERSION, + CONFIG_GUILD_EVENT_LOG_COUNT, + CONFIG_GUILD_BANK_EVENT_LOG_COUNT, + CONFIG_MIN_LEVEL_STAT_SAVE, + CONFIG_RANDOM_BG_RESET_HOUR, + CONFIG_CALENDAR_DELETE_OLD_EVENTS_HOUR, + CONFIG_GUILD_RESET_HOUR, + CONFIG_CHARDELETE_KEEP_DAYS, + CONFIG_CHARDELETE_METHOD, + CONFIG_CHARDELETE_MIN_LEVEL, + CONFIG_AUTOBROADCAST_CENTER, + CONFIG_AUTOBROADCAST_INTERVAL, + CONFIG_AUTOBROADCAST_MIN_LEVEL_DISABLE, + CONFIG_MAX_RESULTS_LOOKUP_COMMANDS, + CONFIG_DB_PING_INTERVAL, + CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION, + CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS, + CONFIG_LFG_OPTIONSMASK, + CONFIG_MAX_INSTANCES_PER_HOUR, + CONFIG_WINTERGRASP_PLR_MAX, + CONFIG_WINTERGRASP_PLR_MIN, + CONFIG_WINTERGRASP_PLR_MIN_LVL, + CONFIG_WINTERGRASP_BATTLETIME, + CONFIG_WINTERGRASP_NOBATTLETIME, + CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, + CONFIG_PACKET_SPOOF_BANMODE, + CONFIG_PACKET_SPOOF_BANDURATION, + CONFIG_WARDEN_CLIENT_RESPONSE_DELAY, + CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF, + CONFIG_WARDEN_CLIENT_FAIL_ACTION, + CONFIG_WARDEN_CLIENT_BAN_DURATION, + CONFIG_WARDEN_NUM_MEM_CHECKS, + CONFIG_WARDEN_NUM_LUA_CHECKS, + CONFIG_WARDEN_NUM_OTHER_CHECKS, + CONFIG_BIRTHDAY_TIME, + CONFIG_SOCKET_TIMEOUTTIME_ACTIVE, + CONFIG_INSTANT_TAXI, + CONFIG_AFK_PREVENT_LOGOUT, + CONFIG_ICC_BUFF_HORDE, + CONFIG_ICC_BUFF_ALLIANCE, + CONFIG_ITEMDELETE_QUALITY, + CONFIG_ITEMDELETE_ITEM_LEVEL, + CONFIG_ITEMDELETE_KEEP_DAYS, + CONFIG_BG_REWARD_WINNER_HONOR_FIRST, + CONFIG_BG_REWARD_WINNER_ARENA_FIRST, + CONFIG_BG_REWARD_WINNER_HONOR_LAST, + CONFIG_BG_REWARD_WINNER_ARENA_LAST, + CONFIG_BG_REWARD_LOSER_HONOR_FIRST, + CONFIG_BG_REWARD_LOSER_HONOR_LAST, + CONFIG_CHARTER_COST_GUILD, + CONFIG_CHARTER_COST_ARENA_2v2, + CONFIG_CHARTER_COST_ARENA_3v3, + CONFIG_CHARTER_COST_ARENA_5v5, + CONFIG_MAX_WHO_LIST_RETURN, + CONFIG_WAYPOINT_MOVEMENT_STOP_TIME_FOR_PLAYER, + CONFIG_DUNGEON_ACCESS_REQUIREMENTS_PRINT_MODE, + CONFIG_DUNGEON_ACCESS_REQUIREMENTS_OPTIONAL_STRING_ID, + CONFIG_GUILD_BANK_INITIAL_TABS, + CONFIG_GUILD_BANK_TAB_COST_0, + CONFIG_GUILD_BANK_TAB_COST_1, + CONFIG_GUILD_BANK_TAB_COST_2, + CONFIG_GUILD_BANK_TAB_COST_3, + CONFIG_GUILD_BANK_TAB_COST_4, + CONFIG_GUILD_BANK_TAB_COST_5, + CONFIG_GUILD_MEMBER_LIMIT, + CONFIG_GM_LEVEL_CHANNEL_MODERATION, + CONFIG_TOGGLE_XP_COST, + CONFIG_NPC_EVADE_IF_NOT_REACHABLE, + CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID, + CONFIG_FFA_PVP_TIMER, + CONFIG_LOOT_NEED_BEFORE_GREED_ILVL_RESTRICTION, + CONFIG_LFG_MAX_KICK_COUNT, + CONFIG_LFG_KICK_PREVENTION_TIMER, + CONFIG_CHANGE_FACTION_MAX_MONEY, + CONFIG_WATER_BREATH_TIMER, + CONFIG_DAILY_RBG_MIN_LEVEL_AP_REWARD, + CONFIG_AUCTIONHOUSE_WORKERTHREADS, + CONFIG_SPELL_QUEUE_WINDOW, + CONFIG_SUNSREACH_COUNTER_MAX, + CONFIG_RESPAWN_DYNAMICMINIMUM_GAMEOBJECT, + CONFIG_RESPAWN_DYNAMICMINIMUM_CREATURE, + RATE_HEALTH, + RATE_POWER_MANA, + RATE_POWER_RAGE_INCOME, + RATE_POWER_RAGE_LOSS, + RATE_POWER_RUNICPOWER_INCOME, + RATE_POWER_RUNICPOWER_LOSS, + RATE_POWER_FOCUS, + RATE_POWER_ENERGY, + RATE_SKILL_DISCOVERY, + RATE_DROP_ITEM_POOR, + RATE_DROP_ITEM_NORMAL, + RATE_DROP_ITEM_UNCOMMON, + RATE_DROP_ITEM_RARE, + RATE_DROP_ITEM_EPIC, + RATE_DROP_ITEM_LEGENDARY, + RATE_DROP_ITEM_ARTIFACT, + RATE_DROP_ITEM_REFERENCED, + RATE_DROP_ITEM_REFERENCED_AMOUNT, + RATE_DROP_ITEM_GROUP_AMOUNT, + RATE_SELLVALUE_ITEM_POOR, + RATE_SELLVALUE_ITEM_NORMAL, + RATE_SELLVALUE_ITEM_UNCOMMON, + RATE_SELLVALUE_ITEM_RARE, + RATE_SELLVALUE_ITEM_EPIC, + RATE_SELLVALUE_ITEM_LEGENDARY, + RATE_SELLVALUE_ITEM_ARTIFACT, + RATE_SELLVALUE_ITEM_HEIRLOOM, + RATE_BUYVALUE_ITEM_POOR, + RATE_BUYVALUE_ITEM_NORMAL, + RATE_BUYVALUE_ITEM_UNCOMMON, + RATE_BUYVALUE_ITEM_RARE, + RATE_BUYVALUE_ITEM_EPIC, + RATE_BUYVALUE_ITEM_LEGENDARY, + RATE_BUYVALUE_ITEM_ARTIFACT, + RATE_BUYVALUE_ITEM_HEIRLOOM, + RATE_DROP_MONEY, + RATE_REWARD_QUEST_MONEY, + RATE_REWARD_BONUS_MONEY, + RATE_XP_KILL, + RATE_XP_BG_KILL_AV, + RATE_XP_BG_KILL_WSG, + RATE_XP_BG_KILL_AB, + RATE_XP_BG_KILL_EOTS, + RATE_XP_BG_KILL_SOTA, + RATE_XP_BG_KILL_IC, + RATE_XP_QUEST, + RATE_XP_QUEST_DF, + RATE_XP_EXPLORE, + RATE_XP_PET, + RATE_XP_PET_NEXT_LEVEL, + RATE_REPAIRCOST, + RATE_REPUTATION_GAIN, + RATE_REPUTATION_LOWLEVEL_KILL, + RATE_REPUTATION_LOWLEVEL_QUEST, + RATE_REPUTATION_RECRUIT_A_FRIEND_BONUS, + RATE_CREATURE_NORMAL_HP, + RATE_CREATURE_ELITE_ELITE_HP, + RATE_CREATURE_ELITE_RAREELITE_HP, + RATE_CREATURE_ELITE_WORLDBOSS_HP, + RATE_CREATURE_ELITE_RARE_HP, + RATE_CREATURE_NORMAL_DAMAGE, + RATE_CREATURE_ELITE_ELITE_DAMAGE, + RATE_CREATURE_ELITE_RAREELITE_DAMAGE, + RATE_CREATURE_ELITE_WORLDBOSS_DAMAGE, + RATE_CREATURE_ELITE_RARE_DAMAGE, + RATE_CREATURE_NORMAL_SPELLDAMAGE, + RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE, + RATE_CREATURE_ELITE_RAREELITE_SPELLDAMAGE, + RATE_CREATURE_ELITE_WORLDBOSS_SPELLDAMAGE, + RATE_CREATURE_ELITE_RARE_SPELLDAMAGE, + RATE_CREATURE_AGGRO, + RATE_REST_INGAME, + RATE_REST_OFFLINE_IN_TAVERN_OR_CITY, + RATE_REST_OFFLINE_IN_WILDERNESS, + RATE_REST_MAX_BONUS, + RATE_DAMAGE_FALL, + RATE_AUCTION_TIME, + RATE_AUCTION_DEPOSIT, + RATE_AUCTION_CUT, + RATE_HONOR, + RATE_ARENA_POINTS, + RATE_TALENT, + RATE_TALENT_PET, + RATE_CORPSE_DECAY_LOOTED, + RATE_INSTANCE_RESET_TIME, + RATE_DURABILITY_LOSS_ON_DEATH, + RATE_DURABILITY_LOSS_DAMAGE, + RATE_DURABILITY_LOSS_PARRY, + RATE_DURABILITY_LOSS_ABSORB, + RATE_DURABILITY_LOSS_BLOCK, + RATE_MOVESPEED_PLAYER, + RATE_MOVESPEED_NPC, + RATE_MISS_CHANCE_MULTIPLIER_TARGET_CREATURE, + RATE_MISS_CHANCE_MULTIPLIER_TARGET_PLAYER, + CONFIG_NEW_CHAR_STRING, + + MAX_NUM_SERVER_CONFIGS +}; + +class WorldConfig : public ConfigValueCache +{ +public: + WorldConfig() : ConfigValueCache(MAX_NUM_SERVER_CONFIGS) { } + + void BuildConfigCache() override; +}; + +#endif diff --git a/src/test/mocks/WorldMock.h b/src/test/mocks/WorldMock.h index fc095b4a6..75e8438e4 100644 --- a/src/test/mocks/WorldMock.h +++ b/src/test/mocks/WorldMock.h @@ -39,8 +39,6 @@ public: MOCK_METHOD(void, LoadDBAllowedSecurityLevel, ()); MOCK_METHOD(bool, getAllowMovement, (), (const)); MOCK_METHOD(void, SetAllowMovement, (bool allow), ()); - MOCK_METHOD(void, SetNewCharString, (std::string const& str), ()); - MOCK_METHOD(std::string const&, GetNewCharString, (), (const)); MOCK_METHOD(LocaleConstant, GetDefaultDbcLocale, (), (const)); MOCK_METHOD(std::string const&, GetDataPath, (), (const)); MOCK_METHOD(Seconds, GetNextDailyQuestsResetTime, (), (const)); @@ -55,14 +53,16 @@ public: MOCK_METHOD(void, ShutdownCancel, ()); MOCK_METHOD(void, ShutdownMsg, (bool show, Player* player, const std::string& reason), ()); MOCK_METHOD(void, Update, (uint32 diff), ()); - MOCK_METHOD(void, setRate, (Rates rate, float value), ()); - MOCK_METHOD(float, getRate, (Rates rate), (const)); - MOCK_METHOD(void, setBoolConfig, (WorldBoolConfigs index, bool value), ()); - MOCK_METHOD(bool, getBoolConfig, (WorldBoolConfigs index), (const)); - MOCK_METHOD(void, setFloatConfig, (WorldFloatConfigs index, float value), ()); - MOCK_METHOD(float, getFloatConfig, (WorldFloatConfigs index), (const)); - MOCK_METHOD(void, setIntConfig, (WorldIntConfigs index, uint32 value), ()); - MOCK_METHOD(uint32, getIntConfig, (WorldIntConfigs index), (const)); + MOCK_METHOD(void, setRate, (ServerConfigs index, float value), ()); + MOCK_METHOD(float, getRate, (ServerConfigs index), (const)); + MOCK_METHOD(void, setBoolConfig, (ServerConfigs index, bool value), ()); + MOCK_METHOD(bool, getBoolConfig, (ServerConfigs index), (const)); + MOCK_METHOD(void, setFloatConfig, (ServerConfigs index, float value), ()); + MOCK_METHOD(float, getFloatConfig, (ServerConfigs index), (const)); + MOCK_METHOD(void, setIntConfig, (ServerConfigs index, uint32 value), ()); + MOCK_METHOD(uint32, getIntConfig, (ServerConfigs index), (const)); + MOCK_METHOD(void, setStringConfig, (ServerConfigs index, std::string const& value), ()); + MOCK_METHOD(std::string_view, getStringConfig, (ServerConfigs index), (const)); MOCK_METHOD(void, setWorldState, (uint32 index, uint64 value), ()); MOCK_METHOD(uint64, getWorldState, (uint32 index), (const)); MOCK_METHOD(void, LoadWorldStates, ()); From dbb995cace6e00b93e8a0475debc2463d78be7a1 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Mon, 30 Jun 2025 17:55:18 +0200 Subject: [PATCH 13/43] refactor(Scripts/Pet): Death Knight use creature register macros (#22399) --- src/server/scripts/Pet/pet_dk.cpp | 440 +++++++++++++----------------- 1 file changed, 186 insertions(+), 254 deletions(-) diff --git a/src/server/scripts/Pet/pet_dk.cpp b/src/server/scripts/Pet/pet_dk.cpp index a0a79b949..c5dd44fe2 100644 --- a/src/server/scripts/Pet/pet_dk.cpp +++ b/src/server/scripts/Pet/pet_dk.cpp @@ -49,292 +49,228 @@ enum DeathKnightSpells SPELL_GHOUL_FRENZY = 62218, }; -class npc_pet_dk_ebon_gargoyle : public CreatureScript +struct npc_pet_dk_ebon_gargoyle : ScriptedAI { -public: - npc_pet_dk_ebon_gargoyle() : CreatureScript("npc_pet_dk_ebon_gargoyle") { } - - struct npc_pet_dk_ebon_gargoyleAI : ScriptedAI + npc_pet_dk_ebon_gargoyle(Creature* creature) : ScriptedAI(creature) { - npc_pet_dk_ebon_gargoyleAI(Creature* creature) : ScriptedAI(creature) - { - _despawnTimer = 36000; // 30 secs + 4 fly out + 2 initial attack timer - _despawning = false; - _initialSelection = true; - _targetGUID.Clear(); - } + _despawnTimer = 36000; // 30 secs + 4 fly out + 2 initial attack timer + _despawning = false; + _initialSelection = true; + _targetGUID.Clear(); + } - void MovementInform(uint32 type, uint32 point) override + void MovementInform(uint32 type, uint32 point) override + { + if (type == POINT_MOTION_TYPE && point == 1) { - if (type == POINT_MOTION_TYPE && point == 1) + me->SetCanFly(false); + me->SetDisableGravity(false); + } + } + + void InitializeAI() override + { + ScriptedAI::InitializeAI(); + Unit* owner = me->GetOwner(); + if (!owner) + return; + + // Xinef: Night of the Dead avoidance + if (Aura* aur = me->GetAura(SPELL_DK_NIGHT_OF_THE_DEAD)) + if (AuraEffect* aurEff = owner->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_DEATHKNIGHT, 2718, 0)) + if (aur->GetEffect(0)) + aur->GetEffect(0)->SetAmount(-aurEff->GetSpellInfo()->Effects[EFFECT_2].CalcValue()); + + me->SetCanFly(true); + me->SetDisableGravity(true); + + float tz = me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), true, MAX_FALL_DISTANCE); + me->GetMotionMaster()->MoveCharge(me->GetPositionX(), me->GetPositionY(), tz, 7.0f, 1); + me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD); + _selectionTimer = 2000; + _initialCastTimer = 0; + } + + void MySelectNextTarget() + { + Unit* owner = me->GetOwner(); + if (owner && owner->IsPlayer() && (!me->GetVictim() || me->GetVictim()->IsImmunedToSpell(sSpellMgr->GetSpellInfo(51963)) || !me->IsValidAttackTarget(me->GetVictim()) || !owner->CanSeeOrDetect(me->GetVictim()))) + { + Unit* selection = owner->ToPlayer()->GetSelectedUnit(); + if (selection && selection != me->GetVictim() && me->IsValidAttackTarget(selection)) { - me->SetCanFly(false); - me->SetDisableGravity(false); + me->GetMotionMaster()->Clear(false); + SetGazeOn(selection); + } + + else if (!me->GetVictim() || !owner->CanSeeOrDetect(me->GetVictim())) + { + me->CombatStop(true); + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, 0.0f); + RemoveTargetAura(); } } + } - void InitializeAI() override + void AttackStart(Unit* who) override + { + RemoveTargetAura(); + _targetGUID = who->GetGUID(); + me->AddAura(SPELL_DK_SUMMON_GARGOYLE_1, who); + ScriptedAI::AttackStart(who); + } + + void RemoveTargetAura() + { + if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) + target->RemoveAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetGUID()); + } + + void Reset() override + { + _selectionTimer = 0; + me->SetReactState(REACT_PASSIVE); + MySelectNextTarget(); + } + + // Fly away when dismissed + void FlyAway() + { + RemoveTargetAura(); + + // Stop Fighting + me->CombatStop(true); + me->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE, true); + + // Sanctuary + me->CastSpell(me, SPELL_DK_SANCTUARY, true); + me->SetReactState(REACT_PASSIVE); + + me->SetSpeed(MOVE_FLIGHT, 1.0f, true); + me->SetSpeed(MOVE_RUN, 1.0f, true); + float x = me->GetPositionX() + 20 * cos(me->GetOrientation()); + float y = me->GetPositionY() + 20 * std::sin(me->GetOrientation()); + float z = me->GetPositionZ() + 40; + me->DisableSpline(); + me->GetMotionMaster()->Clear(false); + + me->GetMotionMaster()->MoveCharge(x, y, z, 7.0f, 1); + me->SetCanFly(true); + me->SetDisableGravity(true); + + _despawning = true; + } + + void UpdateAI(uint32 diff) override + { + if (_initialSelection) { - ScriptedAI::InitializeAI(); - Unit* owner = me->GetOwner(); - if (!owner) + _initialSelection = false; + // Find victim of Summon Gargoyle spell + std::list targets; + Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 50.0f); + Acore::UnitListSearcher searcher(me, targets, u_check); + Cell::VisitAllObjects(me, searcher, 50.0f); + for (auto const& target : targets) + if (target->GetAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetOwnerGUID())) + { + target->RemoveAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetOwnerGUID()); + SetGazeOn(target); + _targetGUID = target->GetGUID(); + break; + } + } + if (_despawnTimer > 4000) + { + _despawnTimer -= diff; + if (!UpdateVictimWithGaze()) + { + MySelectNextTarget(); return; - - // Xinef: Night of the Dead avoidance - if (Aura* aur = me->GetAura(SPELL_DK_NIGHT_OF_THE_DEAD)) - if (AuraEffect* aurEff = owner->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_DEATHKNIGHT, 2718, 0)) - { - if (aur->GetEffect(0)) - { - aur->GetEffect(0)->SetAmount(-aurEff->GetSpellInfo()->Effects[EFFECT_2].CalcValue()); - } - } - - me->SetCanFly(true); - me->SetDisableGravity(true); - - float tz = me->GetMapHeight(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), true, MAX_FALL_DISTANCE); - me->GetMotionMaster()->MoveCharge(me->GetPositionX(), me->GetPositionY(), tz, 7.0f, 1); - me->AddUnitState(UNIT_STATE_NO_ENVIRONMENT_UPD); - _selectionTimer = 2000; - _initialCastTimer = 0; - } - - void MySelectNextTarget() - { - Unit* owner = me->GetOwner(); - if (owner && owner->IsPlayer() && (!me->GetVictim() || me->GetVictim()->IsImmunedToSpell(sSpellMgr->GetSpellInfo(51963)) || !me->IsValidAttackTarget(me->GetVictim()) || !owner->CanSeeOrDetect(me->GetVictim()))) - { - Unit* selection = owner->ToPlayer()->GetSelectedUnit(); - if (selection && selection != me->GetVictim() && me->IsValidAttackTarget(selection)) - { - me->GetMotionMaster()->Clear(false); - SetGazeOn(selection); - } - - else if (!me->GetVictim() || !owner->CanSeeOrDetect(me->GetVictim())) - { - me->CombatStop(true); - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, 0.0f); - RemoveTargetAura(); - } } - } - void AttackStart(Unit* who) override - { - RemoveTargetAura(); - _targetGUID = who->GetGUID(); - me->AddAura(SPELL_DK_SUMMON_GARGOYLE_1, who); - ScriptedAI::AttackStart(who); - } - - void RemoveTargetAura() - { - if (Unit* target = ObjectAccessor::GetUnit(*me, _targetGUID)) - target->RemoveAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetGUID()); - } - - void Reset() override - { - _selectionTimer = 0; - me->SetReactState(REACT_PASSIVE); - MySelectNextTarget(); - } - - // Fly away when dismissed - void FlyAway() - { - RemoveTargetAura(); - - // Stop Fighting - me->CombatStop(true); - me->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE, true); - - // Sanctuary - me->CastSpell(me, SPELL_DK_SANCTUARY, true); - me->SetReactState(REACT_PASSIVE); - - me->SetSpeed(MOVE_FLIGHT, 1.0f, true); - me->SetSpeed(MOVE_RUN, 1.0f, true); - float x = me->GetPositionX() + 20 * cos(me->GetOrientation()); - float y = me->GetPositionY() + 20 * std::sin(me->GetOrientation()); - float z = me->GetPositionZ() + 40; - me->DisableSpline(); - me->GetMotionMaster()->Clear(false); - - me->GetMotionMaster()->MoveCharge(x, y, z, 7.0f, 1); - me->SetCanFly(true); - me->SetDisableGravity(true); - - _despawning = true; - } - - void UpdateAI(uint32 diff) override - { - if (_initialSelection) + _initialCastTimer += diff; + _selectionTimer += diff; + if (_selectionTimer >= 1000) { - _initialSelection = false; - // Find victim of Summon Gargoyle spell - std::list targets; - Acore::AnyUnfriendlyUnitInObjectRangeCheck u_check(me, me, 50.0f); - Acore::UnitListSearcher searcher(me, targets, u_check); - Cell::VisitAllObjects(me, searcher, 50.0f); - for (std::list::const_iterator iter = targets.begin(); iter != targets.end(); ++iter) - if ((*iter)->GetAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetOwnerGUID())) - { - (*iter)->RemoveAura(SPELL_DK_SUMMON_GARGOYLE_1, me->GetOwnerGUID()); - SetGazeOn(*iter); - _targetGUID = (*iter)->GetGUID(); - break; - } + MySelectNextTarget(); + _selectionTimer = 0; } - if (_despawnTimer > 4000) - { + if (_initialCastTimer >= 2000 && !me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_LOST_CONTROL) && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE) + me->CastSpell(me->GetVictim(), 51963, false); + } + else + { + if (!_despawning) + FlyAway(); + + if (_despawnTimer > diff) _despawnTimer -= diff; - if (!UpdateVictimWithGaze()) - { - MySelectNextTarget(); - return; - } - - _initialCastTimer += diff; - _selectionTimer += diff; - if (_selectionTimer >= 1000) - { - MySelectNextTarget(); - _selectionTimer = 0; - } - if (_initialCastTimer >= 2000 && !me->HasUnitState(UNIT_STATE_CASTING | UNIT_STATE_LOST_CONTROL) && me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_CONTROLLED) == NULL_MOTION_TYPE) - me->CastSpell(me->GetVictim(), 51963, false); - } else - { - if (!_despawning) - FlyAway(); - - if (_despawnTimer > diff) - _despawnTimer -= diff; - else - me->DespawnOrUnsummon(); - } + me->DespawnOrUnsummon(); } + } - private: - ObjectGuid _targetGUID; - uint32 _despawnTimer; - uint32 _selectionTimer; - uint32 _initialCastTimer; - bool _despawning; - bool _initialSelection; - }; +private: + ObjectGuid _targetGUID; + uint32 _despawnTimer; + uint32 _selectionTimer; + uint32 _initialCastTimer; + bool _despawning; + bool _initialSelection; +}; - CreatureAI* GetAI(Creature* creature) const override +struct npc_pet_dk_ghoul : public CombatAI +{ + npc_pet_dk_ghoul(Creature* c) : CombatAI(c) { } + + void JustDied(Unit* /*who*/) override { - return new npc_pet_dk_ebon_gargoyleAI(creature); + if (me->IsGuardian() || me->IsSummon()) + me->ToTempSummon()->UnSummon(); } }; -class npc_pet_dk_ghoul : public CreatureScript +struct npc_pet_dk_risen_ally : public PossessedAI { -public: - npc_pet_dk_ghoul() : CreatureScript("npc_pet_dk_ghoul") { } + npc_pet_dk_risen_ally(Creature* c) : PossessedAI(c) { } - struct npc_pet_dk_ghoulAI : public CombatAI + void OnCharmed(bool apply) override { - npc_pet_dk_ghoulAI(Creature* c) : CombatAI(c) { } - - void JustDied(Unit* /*who*/) override - { - if (me->IsGuardian() || me->IsSummon()) - me->ToTempSummon()->UnSummon(); - } - }; - - CreatureAI* GetAI(Creature* pCreature) const override - { - return new npc_pet_dk_ghoulAI (pCreature); - } -}; - -class npc_pet_dk_risen_ally : public CreatureScript -{ -public: - npc_pet_dk_risen_ally() : CreatureScript("npc_pet_dk_risen_ally") { } - - struct npc_pet_dk_risen_allyAI : public PossessedAI - { - npc_pet_dk_risen_allyAI(Creature* c) : PossessedAI(c) { } - - void OnCharmed(bool apply) override - { - if (!apply) - { - if (Unit* owner = me->GetCharmerOrOwner()) + if (!apply) + if (Unit* owner = me->GetCharmerOrOwner()) + if (Player* player = owner->ToPlayer()) { - if (Player* player = owner->ToPlayer()) - { - player->RemoveAurasDueToSpell(SPELL_DK_RAISE_ALLY); // Remove Raise Ally aura - player->RemoveAurasDueToSpell(SPELL_GHOUL_FRENZY); // Remove Frenzy aura - //player->ClearResurrectRequestData(); - } + player->RemoveAurasDueToSpell(SPELL_DK_RAISE_ALLY); // Remove Raise Ally aura + player->RemoveAurasDueToSpell(SPELL_GHOUL_FRENZY); // Remove Frenzy aura + //player->ClearResurrectRequestData(); } - } - } - }; - - CreatureAI* GetAI(Creature* pCreature) const override - { - return new npc_pet_dk_risen_allyAI (pCreature); } }; -class npc_pet_dk_army_of_the_dead : public CreatureScript +struct npc_pet_dk_army_of_the_dead : public CombatAI { -public: - npc_pet_dk_army_of_the_dead() : CreatureScript("npc_pet_dk_army_of_the_dead") { } + npc_pet_dk_army_of_the_dead(Creature* creature) : CombatAI(creature) { } - struct npc_pet_dk_army_of_the_deadAI : public CombatAI + void InitializeAI() override { - npc_pet_dk_army_of_the_deadAI(Creature* creature) : CombatAI(creature) { } - - void InitializeAI() override - { - CombatAI::InitializeAI(); - ((Minion*)me)->SetFollowAngle(rand_norm() * 2 * M_PI); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_pet_dk_army_of_the_deadAI (creature); + CombatAI::InitializeAI(); + ((Minion*)me)->SetFollowAngle(rand_norm() * 2 * M_PI); } }; -class npc_pet_dk_dancing_rune_weapon : public CreatureScript +struct npc_pet_dk_dancing_rune_weapon : public NullCreatureAI { -public: - npc_pet_dk_dancing_rune_weapon() : CreatureScript("npc_pet_dk_dancing_rune_weapon") { } + npc_pet_dk_dancing_rune_weapon(Creature* creature) : NullCreatureAI(creature) { } - struct npc_pet_dk_dancing_rune_weaponAI : public NullCreatureAI + void InitializeAI() override { - npc_pet_dk_dancing_rune_weaponAI(Creature* creature) : NullCreatureAI(creature) { } + me->AddAura(SPELL_HUNTER_PET_SCALING_04, me); + if (Unit* owner = me->GetOwner()) + me->GetMotionMaster()->MoveFollow(owner, 0.01f, me->GetFollowAngle(), MOTION_SLOT_CONTROLLED); - void InitializeAI() override - { - // Xinef: Hit / Expertise scaling - me->AddAura(61017, me); - if (Unit* owner = me->GetOwner()) - me->GetMotionMaster()->MoveFollow(owner, 0.01f, me->GetFollowAngle(), MOTION_SLOT_CONTROLLED); - - NullCreatureAI::InitializeAI(); - } - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_pet_dk_dancing_rune_weaponAI (creature); + NullCreatureAI::InitializeAI(); } }; @@ -346,12 +282,8 @@ class spell_pet_dk_gargoyle_strike : public SpellScript { int32 damage = 60; if (Unit* caster = GetCaster()) - { if (caster->GetLevel() >= 60) - { damage += (caster->GetLevel() - 60) * 4; - } - } SetEffectValue(damage); } @@ -364,10 +296,10 @@ class spell_pet_dk_gargoyle_strike : public SpellScript void AddSC_deathknight_pet_scripts() { - new npc_pet_dk_ebon_gargoyle(); - new npc_pet_dk_ghoul(); - new npc_pet_dk_risen_ally(); - new npc_pet_dk_army_of_the_dead(); - new npc_pet_dk_dancing_rune_weapon(); + RegisterCreatureAI(npc_pet_dk_ebon_gargoyle); + RegisterCreatureAI(npc_pet_dk_ghoul); + RegisterCreatureAI(npc_pet_dk_risen_ally); + RegisterCreatureAI(npc_pet_dk_army_of_the_dead); + RegisterCreatureAI(npc_pet_dk_dancing_rune_weapon); RegisterSpellScript(spell_pet_dk_gargoyle_strike); } From 681e8439fff17025148ddb0febc494fa131ea7d6 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Tue, 1 Jul 2025 04:17:00 -0300 Subject: [PATCH 14/43] =?UTF-8?q?fix(Scripts/ScarletEnclave):=20Correct=20?= =?UTF-8?q?Tirion=20spawn=20position=20in=20Light=20o=E2=80=A6=20(#22394)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/sql/updates/pending_db_world/tirion.sql | 19 ++++++++++++++++++ .../ScarletEnclave/chapter5.cpp | 20 ++++++++++++++----- 2 files changed, 34 insertions(+), 5 deletions(-) create mode 100644 data/sql/updates/pending_db_world/tirion.sql diff --git a/data/sql/updates/pending_db_world/tirion.sql b/data/sql/updates/pending_db_world/tirion.sql new file mode 100644 index 000000000..a1df78031 --- /dev/null +++ b/data/sql/updates/pending_db_world/tirion.sql @@ -0,0 +1,19 @@ +-- DB update 2025_06_29_02 -> 2025_06_29_03 +-- +SET @NPC := 29175; +SET @PATH := @NPC * 10; +DELETE FROM `waypoint_data` WHERE `id`=@PATH; +INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES +(@PATH,1,2199.3313,-5271.9775,88.125336,0,0,1,0,100,0), +(@PATH,2,2235.2202,-5278.9307,79.06934,0,0,1,0,100,0), +(@PATH,3,2253.4414,-5284.3496,82.44837,0,0,1,0,100,0), +(@PATH,4,2281.2556,-5299.5728,84.998795,1.692969322204589843,0,1,0,100,0); + +UPDATE `creature_template` SET `AIName` = 'SmartAI', `ScriptName` = '' WHERE `entry` = 29175; + +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 29175) AND (`source_type` = 0) AND (`id` IN (0, 1)); +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 +(29175, 0, 0, 1, 109, 0, 100, 0, 0, 0, 0, 0, 0, 0, 206, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Highlord Tirion Fordring - On Path 0 Finished - Dismount'), +(29175, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1.692969322, 'Highlord Tirion Fordring - On Path 0 Finished - Set Orientation 1.692969322'); + +UPDATE `creature_text` SET `Type` = 41 WHERE `CreatureId` = 29173 AND `GroupId` = 72 AND `ID` = 0; diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp index 30ffb72b4..1e68ae383 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp @@ -280,14 +280,14 @@ const Position LightOfDawnPos[] = {2289.259f, -5280.355f, 86.112f, 4.41f}, // 3 Koltira Loc1 {2273.289f, -5273.675f, 86.701f, 5.01f}, // 4 Thassarian Loc1 {2280.81f, -5284.09f, 86.608f, 4.76f}, // 5 Morgraine Loc1 - {2281.335f, -5300.409f, 85.170f, 1.528f}, // 6 Tirion Summon loc + {2165.711f, -5266.1235f, 95.5025f, 0.13962634f }, // 6 Tirion Summon loc {2281.198f, -5257.397f, 80.224f, 4.66f}, // 7 Alexandros loc1 {2281.156f, -5259.934f, 80.647f, 0}, // 8 Alexandros loc2 {2281.294f, -5281.895f, 82.445f, 1.35f}, // 9 Darion loc1 {2281.093f, -5263.013f, 81.125f, 0}, // 10 Darion loc2 {2283.896f, -5287.914f, 83.066f, 1.55f}, // 11 Tirion Fordring loc2 - {2281.313f, -5250.282f, 79.322f, 4.69f}, // 12 Lich King spawns - {2281.523f, -5261.058f, 80.877f, 0}, // 13 Lich king moves forward + {2280.304f, -5257.205f, 80.09781f, 4.6251f },// 12 Lich King spawns + {2280.687f, -5262.276f, 81.082634f, 0.0f },// 13 Lich king moves forward {2264.27f, -5267.29f, 80.16f, 0}, // 14 Tirion Fordring loc3 {2270.99f, -5278.00f, 81.89f, 0} // 15 Tirion Fordring loc4 }; @@ -501,7 +501,12 @@ public: if (Creature* tirion = me->SummonCreature(NPC_HIGHLORD_TIRION_FORDRING, LightOfDawnPos[6], TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 600000)) { tirion->LoadEquipment(0, true); - tirion->AI()->Talk(SAY_LIGHT_OF_DAWN25); + tirion->AI()->Talk(SAY_LIGHT_OF_DAWN25, 4s); + + tirion->m_Events.AddEventAtOffset([&, tirion] { + tirion->GetMotionMaster()->MovePath(NPC_HIGHLORD_TIRION_FORDRING * 10, false); + }, 14s); + events.Reset(); events.ScheduleEvent(EVENT_FINISH_FIGHT_1, 10s); events.ScheduleEvent(EVENT_FINISH_FIGHT_2, 20s); @@ -898,7 +903,12 @@ public: if (Creature* lk = GetEntryFromSummons(NPC_THE_LICH_KING)) { lk->AI()->Talk(SAY_LIGHT_OF_DAWN46); - lk->CastSpell(me, SPELL_REBUKE, false); + // Mograine's charge puts him inside the Lich King's collision, so we need to teleport him out + // Otherwise, Rebuke will kick him the wrong direction + me->NearTeleportTo(2279.7493f, -5258.1f, 80.065f, 4.3419204f); + lk->m_Events.AddEventAtOffset([&, lk] { + lk->CastSpell(me, SPELL_REBUKE, false); + }, 1s); } break; case EVENT_OUTRO_SCENE_28: From d3130f0d39064d03bed969c7bc135ebb6066442f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Jul 2025 07:18:01 +0000 Subject: [PATCH 15/43] chore(DB): import pending files Referenced commit(s): 681e8439fff17025148ddb0febc494fa131ea7d6 --- .../{pending_db_world/tirion.sql => db_world/2025_07_01_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/tirion.sql => db_world/2025_07_01_00.sql} (97%) diff --git a/data/sql/updates/pending_db_world/tirion.sql b/data/sql/updates/db_world/2025_07_01_00.sql similarity index 97% rename from data/sql/updates/pending_db_world/tirion.sql rename to data/sql/updates/db_world/2025_07_01_00.sql index a1df78031..d073dceb6 100644 --- a/data/sql/updates/pending_db_world/tirion.sql +++ b/data/sql/updates/db_world/2025_07_01_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_06_30_00 -> 2025_07_01_00 -- DB update 2025_06_29_02 -> 2025_06_29_03 -- SET @NPC := 29175; From e1b2689c3a2b1395323d2fc58588b1e1b3c07c53 Mon Sep 17 00:00:00 2001 From: Yehonal Date: Tue, 1 Jul 2025 15:35:54 +0200 Subject: [PATCH 16/43] feat(bash): startup-scripts reworked + bash scripts workflow integration (#22401) --- .devcontainer/devcontainer.json | 37 +- .github/workflows/dashboard-ci.yml | 82 ++ .vscode/extensions.json | 5 +- .vscode/settings.json | 9 +- CMakeLists.txt | 3 + apps/compiler/includes/functions.sh | 24 +- apps/compiler/includes/includes.sh | 2 +- apps/installer/includes/functions.sh | 46 +- apps/installer/includes/os_configs/debian.sh | 17 +- apps/installer/includes/os_configs/ubuntu.sh | 40 +- apps/installer/main.sh | 10 +- apps/startup-scripts/README.md | 497 +++++++ apps/startup-scripts/conf.sh.dist | 50 - .../examples/restarter-auth.sh | 14 - .../examples/restarter-world.sh | 14 - apps/startup-scripts/examples/starter-auth.sh | 13 - .../startup-scripts/examples/starter-world.sh | 14 - apps/startup-scripts/run-engine | 115 -- apps/startup-scripts/simple-restarter | 70 - apps/startup-scripts/src/.gitignore | 1 + apps/startup-scripts/src/conf.sh.dist | 57 + .../src/examples/restarter-auth.sh | 48 + .../src/examples/restarter-world.sh | 47 + .../src/examples/starter-auth.sh | 46 + .../src/examples/starter-world.sh | 47 + apps/startup-scripts/{ => src}/gdb.conf | 0 apps/startup-scripts/src/run-engine | 467 ++++++ apps/startup-scripts/src/service-manager.sh | 1261 +++++++++++++++++ apps/startup-scripts/src/simple-restarter | 89 ++ apps/startup-scripts/src/starter | 117 ++ apps/startup-scripts/starter | 32 - apps/startup-scripts/test/bats.conf | 14 + .../test/test_startup_scripts.bats | 147 ++ apps/test-framework/README.md | 335 +++++ .../bats_libs/acore-assert.bash | 178 +++ .../bats_libs/acore-support.bash | 116 ++ apps/test-framework/helpers/test_common.sh | 143 ++ apps/test-framework/run-tests.sh | 290 ++++ conf/dist/config.sh | 5 + data/sql/create/create_mysql.sql | 7 +- 40 files changed, 4125 insertions(+), 384 deletions(-) create mode 100644 .github/workflows/dashboard-ci.yml create mode 100644 apps/startup-scripts/README.md delete mode 100644 apps/startup-scripts/conf.sh.dist delete mode 100644 apps/startup-scripts/examples/restarter-auth.sh delete mode 100644 apps/startup-scripts/examples/restarter-world.sh delete mode 100644 apps/startup-scripts/examples/starter-auth.sh delete mode 100644 apps/startup-scripts/examples/starter-world.sh delete mode 100644 apps/startup-scripts/run-engine delete mode 100755 apps/startup-scripts/simple-restarter create mode 100644 apps/startup-scripts/src/.gitignore create mode 100644 apps/startup-scripts/src/conf.sh.dist create mode 100755 apps/startup-scripts/src/examples/restarter-auth.sh create mode 100755 apps/startup-scripts/src/examples/restarter-world.sh create mode 100755 apps/startup-scripts/src/examples/starter-auth.sh create mode 100755 apps/startup-scripts/src/examples/starter-world.sh rename apps/startup-scripts/{ => src}/gdb.conf (100%) create mode 100755 apps/startup-scripts/src/run-engine create mode 100755 apps/startup-scripts/src/service-manager.sh create mode 100755 apps/startup-scripts/src/simple-restarter create mode 100755 apps/startup-scripts/src/starter delete mode 100644 apps/startup-scripts/starter create mode 100644 apps/startup-scripts/test/bats.conf create mode 100644 apps/startup-scripts/test/test_startup_scripts.bats create mode 100644 apps/test-framework/README.md create mode 100644 apps/test-framework/bats_libs/acore-assert.bash create mode 100644 apps/test-framework/bats_libs/acore-support.bash create mode 100644 apps/test-framework/helpers/test_common.sh create mode 100755 apps/test-framework/run-tests.sh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cdf6fc887..06b65aafd 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -17,24 +17,29 @@ "workspaceFolder": "/azerothcore", // Set *default* container specific settings.json values on container create. - "settings": { - "terminal.integrated.shell.linux": null + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.shell.linux": null + }, + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-vscode-remote.remote-containers", + "notskm.clang-tidy", + "xaver.clang-format", + "bbenoist.doxygen", + "ms-vscode.cpptools", + "ms-vscode.cmake-tools", + "mhutchie.git-graph", + "github.vscode-pull-request-github", + "eamodio.gitlens", + "cschlosser.doxdocgen", + "sanaajani.taskrunnercode", + "mads-hartmann.bash-ide-vscode" + ] + } }, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "notskm.clang-tidy", - "xaver.clang-format", - "bbenoist.doxygen", - "ms-vscode.cpptools", - "ms-vscode.cmake-tools", - "mhutchie.git-graph", - "github.vscode-pull-request-github", - "eamodio.gitlens", - "cschlosser.doxdocgen", - "sanaajani.taskrunnercode" - ], - // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], diff --git a/.github/workflows/dashboard-ci.yml b/.github/workflows/dashboard-ci.yml new file mode 100644 index 000000000..5e56cb1c8 --- /dev/null +++ b/.github/workflows/dashboard-ci.yml @@ -0,0 +1,82 @@ +name: Dashboard CI +description: | + This workflow runs tests and builds for the AzerothCore dashboard. + It includes testing of bash scripts and integration testing of the AzerothCore server. + Do not remove this if something is broken here and you don't know how to fix it, ping Yehonal instead. + +on: + push: + branches: + - 'master' + pull_request: + types: + - opened + - reopened + - synchronize + workflow_dispatch: + +concurrency: + group: ${{ github.head_ref }} || concat(${{ github.ref }}, ${{ github.workflow }}) + cancel-in-progress: true + +env: + CONTINUOUS_INTEGRATION: true + MYSQL_ROOT_PASSWORD: root + +jobs: + test-bash-scripts: + name: Test Bash Scripts + runs-on: ubuntu-24.04 + if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Install requirements + run: | + sudo apt install -y bats + ./acore.sh install-deps + + - name: Run bash script tests for ${{ matrix.test-module }} + env: + TERM: xterm-256color + run: | + cd apps/test-framework + ./run-tests.sh --tap + + build-and-test: + name: Build and Integration Test + runs-on: ubuntu-24.04 + if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Configure AzerothCore settings + run: | + # Create basic configuration + cp conf/dist/config.sh conf/config.sh + # Configure dashboard + sed -i 's/MTHREADS=.*/MTHREADS="4"/' conf/config.sh + + - name: Run complete installation (deps, compile, database, client-data) + run: | + # This runs: install-deps, compile, database setup, client-data download + ./acore.sh init + timeout-minutes: 120 + + - name: Test authserver dry-run + run: | + cd env/dist/bin + timeout 5m ./authserver -dry-run + continue-on-error: false + + - name: Test worldserver dry-run + run: | + cd env/dist/bin + timeout 5m ./worldserver -dry-run + continue-on-error: false diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 2b4b837a2..f5e39be64 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -10,6 +10,9 @@ "github.vscode-pull-request-github", "eamodio.gitlens", "cschlosser.doxdocgen", - "sanaajani.taskrunnercode" + "sanaajani.taskrunnercode", + "mads-hartmann.bash-ide-vscode", + "jetmartin.bats", + "ms-vscode.makefile-tools", ] } diff --git a/.vscode/settings.json b/.vscode/settings.json index 70f526afc..dcc292e7a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -115,5 +115,12 @@ "xutility": "cpp", "*.ipp": "cpp", "resumable": "cpp" - } + }, + "deno.enable": true, + "deno.path": "deps/deno/bin/deno", + "deno.lint": true, + "C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json", + "C_Cpp.default.cppStandard": "c++17", + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", + "C_Cpp.default.compilerPath": "/usr/bin/clang" } diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d9d4902a..494109cc6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -35,6 +35,9 @@ set(CMAKE_BUILD_WITH_INSTALL_RPATH 0) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH 1) +# Export compile commands for IDE support +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + set(AC_PATH_ROOT "${CMAKE_SOURCE_DIR}") # set macro-directory diff --git a/apps/compiler/includes/functions.sh b/apps/compiler/includes/functions.sh index 2e7299905..229991818 100644 --- a/apps/compiler/includes/functions.sh +++ b/apps/compiler/includes/functions.sh @@ -1,3 +1,7 @@ +#!/usr/bin/env bash + +# Set SUDO variable - one liner +SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") function comp_clean() { DIRTOCLEAN=${BUILDPATH:-var/build/obj} @@ -134,23 +138,21 @@ function comp_compile() { mkdir -p "$confDir" echo "Cmake install..." - sudo cmake --install . --config $CTYPE + $SUDO cmake --install . --config $CTYPE popd >> /dev/null || exit 1 # set all aplications SUID bit echo "Setting permissions on binary files" - find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec sudo chown root:root -- {} + - find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec sudo chmod u+s -- {} + + find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chown root:root -- {} + + find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chmod u+s -- {} + - if [[ -n "$DOCKER" ]]; then - [[ -f "$confDir/worldserver.conf.dist" ]] && \ - cp -nv "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf" - [[ -f "$confDir/authserver.conf.dist" ]] && \ - cp -nv "$confDir/authserver.conf.dist" "$confDir/authserver.conf" - [[ -f "$confDir/dbimport.conf.dist" ]] && \ - cp -nv "$confDir/dbimport.conf.dist" "$confDir/dbimport.conf" - fi + [[ -f "$confDir/worldserver.conf.dist" ]] && \ + cp -v --update=none "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf" + [[ -f "$confDir/authserver.conf.dist" ]] && \ + cp -v --update=none "$confDir/authserver.conf.dist" "$confDir/authserver.conf" + [[ -f "$confDir/dbimport.conf.dist" ]] && \ + cp -v --update=none "$confDir/dbimport.conf.dist" "$confDir/dbimport.conf" echo "Done" ;; diff --git a/apps/compiler/includes/includes.sh b/apps/compiler/includes/includes.sh index c425160dc..4f697947a 100644 --- a/apps/compiler/includes/includes.sh +++ b/apps/compiler/includes/includes.sh @@ -10,7 +10,7 @@ fi function ac_on_after_build() { # move the run engine - cp -rvf "$AC_PATH_APPS/startup-scripts/"* "$BINPATH" + cp -rvf "$AC_PATH_APPS/startup-scripts/src/"* "$BINPATH" } registerHooks "ON_AFTER_BUILD" ac_on_after_build diff --git a/apps/installer/includes/functions.sh b/apps/installer/includes/functions.sh index 7e95f78fc..b4bd14caf 100644 --- a/apps/installer/includes/functions.sh +++ b/apps/installer/includes/functions.sh @@ -1,3 +1,8 @@ +#!/usr/bin/env bash + +# Set SUDO variable - one liner +SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") + function inst_configureOS() { echo "Platform: $OSTYPE" case "$OSTYPE" in @@ -45,6 +50,42 @@ function inst_configureOS() { esac } +# Use the data/sql/create/create_mysql.sql to initialize the database +function inst_dbCreate() { + echo "Creating database..." + + # Attempt to connect with MYSQL_ROOT_PASSWORD + if [ ! -z "$MYSQL_ROOT_PASSWORD" ]; then + if $SUDO mysql -u root -p"$MYSQL_ROOT_PASSWORD" < "$AC_PATH_ROOT/data/sql/create/create_mysql.sql" 2>/dev/null; then + echo "Database created successfully." + return 0 + else + echo "Failed to connect with provided password, falling back to interactive mode..." + fi + fi + + # In CI environments or when no password is set, try without password first + if [[ "$CONTINUOUS_INTEGRATION" == "true" ]]; then + echo "CI environment detected, attempting connection without password..." + + if $SUDO mysql -u root < "$AC_PATH_ROOT/data/sql/create/create_mysql.sql" 2>/dev/null; then + echo "Database created successfully." + return 0 + else + echo "Failed to connect without password, falling back to interactive mode..." + fi + fi + + # Try with password (interactive mode) + echo "Please enter your sudo and your MySQL root password if prompted." + $SUDO mysql -u root -p < "$AC_PATH_ROOT/data/sql/create/create_mysql.sql" + if [ $? -ne 0 ]; then + echo "Database creation failed. Please check your MySQL server and credentials." + exit 1 + fi + echo "Database created successfully." +} + function inst_updateRepo() { cd "$AC_PATH_ROOT" if [ ! -z $INSTALLER_PULL_FROM ]; then @@ -73,7 +114,8 @@ function inst_cleanCompile() { function inst_allInOne() { inst_configureOS inst_compile - dbasm_import true true true + inst_dbCreate + inst_download_client_data } function inst_getVersionBranch() { @@ -215,7 +257,7 @@ function inst_module_remove { function inst_simple_restarter { echo "Running $1 ..." - bash "$AC_PATH_APPS/startup-scripts/simple-restarter" "$AC_BINPATH_FULL" "$1" + bash "$AC_PATH_APPS/startup-scripts/src/simple-restarter" "$AC_BINPATH_FULL" "$1" echo #disown -a #jobs -l diff --git a/apps/installer/includes/os_configs/debian.sh b/apps/installer/includes/os_configs/debian.sh index 418392565..0eb2b3b1e 100644 --- a/apps/installer/includes/os_configs/debian.sh +++ b/apps/installer/includes/os_configs/debian.sh @@ -2,8 +2,11 @@ CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +# Set SUDO variable - one liner +SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") + if ! command -v lsb_release &>/dev/null ; then - sudo apt-get install -y lsb-release + $SUDO apt-get install -y lsb-release fi DEBIAN_VERSION=$(lsb_release -sr) @@ -18,18 +21,18 @@ if [[ $DEBIAN_VERSION -lt $DEBIAN_VERSION_MIN ]]; then echo "########## ########## ##########" fi -sudo apt-get update -y +$SUDO apt-get update -y -sudo apt-get install -y gdbserver gdb unzip curl \ +$SUDO apt-get install -y gdbserver gdb unzip curl \ libncurses-dev libreadline-dev clang g++ \ gcc git cmake make ccache \ libssl-dev libbz2-dev \ - libboost-all-dev gnupg wget + libboost-all-dev gnupg wget jq screen tmux VAR_PATH="$CURRENT_PATH/../../../../var" # run noninteractive install for MYSQL 8.4 LTS wget https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb -P "$VAR_PATH" -sudo DEBIAN_FRONTEND="noninteractive" dpkg -i "$VAR_PATH/mysql-apt-config_0.8.32-1_all.deb" -sudo apt-get update -sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server libmysqlclient-dev +DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.32-1_all.deb" +$SUDO apt-get update +DEBIAN_FRONTEND="noninteractive" $SUDO apt-get install -y mysql-server libmysqlclient-dev diff --git a/apps/installer/includes/os_configs/ubuntu.sh b/apps/installer/includes/os_configs/ubuntu.sh index 02e6997cc..9b45b35c0 100644 --- a/apps/installer/includes/os_configs/ubuntu.sh +++ b/apps/installer/includes/os_configs/ubuntu.sh @@ -2,8 +2,11 @@ CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +# Set SUDO variable - one liner +SUDO=$([ "$EUID" -ne 0 ] && echo "sudo" || echo "") + if ! command -v lsb_release &>/dev/null ; then - sudo apt-get install -y lsb-release + $SUDO apt-get install -y lsb-release fi UBUNTU_VERSION=$(lsb_release -sr); @@ -23,28 +26,29 @@ case $UBUNTU_VERSION in ;; esac -sudo apt update +$SUDO apt update # shared deps -sudo DEBIAN_FRONTEND="noninteractive" \ -apt-get -y install ccache clang cmake curl google-perftools libmysqlclient-dev make unzip - -if [[ $CONTINUOUS_INTEGRATION || $DOCKER ]]; then - # TODO: update CI / Docker section for Ubuntu 22.04+ - sudo add-apt-repository -y ppa:mhier/libboost-latest && sudo apt update && sudo apt-get -y install build-essential cmake-data \ - libboost1.74-dev libbz2-dev libncurses5-dev libmysql++-dev libgoogle-perftools-dev libreadline6-dev libssl-dev libtool \ - openssl zlib1g-dev -else - sudo DEBIAN_FRONTEND="noninteractive" \ - apt-get install -y g++ gdb gdbserver gcc git \ - libboost-all-dev libbz2-dev libncurses-dev libreadline-dev \ - libssl-dev +DEBIAN_FRONTEND="noninteractive" $SUDO \ +apt-get -y install ccache clang cmake curl google-perftools libmysqlclient-dev make unzip jq screen tmux \ + libreadline-dev libncurses5-dev libncursesw5-dev libbz2-dev git gcc g++ libssl-dev \ + libncurses-dev libboost-all-dev gdb gdbserver VAR_PATH="$CURRENT_PATH/../../../../var" + +# Do not install MySQL if we are in docker (It will be used a docker container instead) or we are explicitly skipping it. +if [[ $DOCKER != 1 && $SKIP_MYSQL_INSTALL != 1 ]]; then # run noninteractive install for MYSQL 8.4 LTS wget https://dev.mysql.com/get/mysql-apt-config_0.8.32-1_all.deb -P "$VAR_PATH" - sudo DEBIAN_FRONTEND="noninteractive" dpkg -i "$VAR_PATH/mysql-apt-config_0.8.32-1_all.deb" - sudo apt-get update - sudo DEBIAN_FRONTEND="noninteractive" apt-get install -y mysql-server + DEBIAN_FRONTEND="noninteractive" $SUDO dpkg -i "$VAR_PATH/mysql-apt-config_0.8.32-1_all.deb" + $SUDO apt-get update + DEBIAN_FRONTEND="noninteractive" $SUDO apt-get install -y mysql-server fi + + +if [[ $CONTINUOUS_INTEGRATION ]]; then + $SUDO systemctl enable mysql.service + $SUDO systemctl start mysql.service +fi + diff --git a/apps/installer/main.sh b/apps/installer/main.sh index eb0eb4f89..396dafaad 100644 --- a/apps/installer/main.sh +++ b/apps/installer/main.sh @@ -19,7 +19,9 @@ options=( "run-worldserver (rw): execute a simple restarter for worldserver" # 11 "run-authserver (ra): execute a simple restarter for authserver" # 12 "docker (dr): Run docker tools" # 13 - "quit: Exit from this menu" # 14 + "version (v): Show AzerothCore version" # 14 + "service-manager (sm): Run service manager to run authserver and worldserver in background" # 15 + "quit: Exit from this menu" # 16 ) function _switch() { @@ -72,7 +74,11 @@ function _switch() { printf "AzerothCore Rev. %s\n" "$ACORE_VERSION" exit ;; - ""|"quit"|"15") + ""|"sm"|"service-manager"|"15") + bash "$AC_PATH_APPS/startup-scripts/src/service-manager.sh" "${@:2}" + exit + ;; + ""|"quit"|"16") echo "Goodbye!" exit ;; diff --git a/apps/startup-scripts/README.md b/apps/startup-scripts/README.md new file mode 100644 index 000000000..9a408dd6c --- /dev/null +++ b/apps/startup-scripts/README.md @@ -0,0 +1,497 @@ +# AzerothCore Startup Scripts + +A comprehensive suite of scripts for managing AzerothCore server instances with advanced session management, automatic restart capabilities, and production-ready service management. + +## 📋 Table of Contents + +- [Overview](#overview) +- [Components](#components) +- [Quick Start](#quick-start) +- [Configuration](#configuration) +- [Detailed Usage](#detailed-usage) +- [Multiple Realms Setup](#multiple-realms-setup) +- [Service Management](#service-management) +- [Troubleshooting](#troubleshooting) + +## 🎯 Overview + +The AzerothCore startup scripts provide multiple approaches to running server instances: + +1. **Development/Testing**: Simple execution for debugging and development +2. **Production with Restarts**: Automatic restart on crashes with crash detection +3. **Background Services**: Production-ready service management with PM2 or systemd +4. **Session Management**: Interactive console access via tmux/screen + +All scripts are integrated into the `acore.sh` dashboard for easy access. + +### 📦 Automatic Deployment + +**Important**: When you compile AzerothCore using the acore dashboard (`./acore.sh compiler build`), all startup scripts are automatically copied from `apps/startup-scripts/src/` to your `bin/` folder. This means: + +- ✅ **Portable Deployment**: You can copy the entire `bin/` folder to different servers +- ✅ **Self-Contained**: All restart and service management tools travel with your binaries +- ✅ **No Additional Setup**: Scripts work immediately after deployment +- ✅ **Production Ready**: Deploy to production servers without needing the full source code + +This makes it easy to deploy your compiled binaries along with the management scripts to production environments where you may not have the full AzerothCore source code. + +## 🔧 Components + +### Core Scripts + +- **`run-engine`**: Advanced script with session management and configuration priority +- **`simple-restarter`**: Wrapper around starter with restart functionality (legacy compatibility) +- **`starter`**: Basic binary execution with optional GDB support +- **`service-manager.sh`**: Production service management with PM2/systemd + +### Configuration + +- **`conf.sh.dist`**: Default configuration template +- **`conf.sh`**: User configuration (create from .dist) +- **`gdb.conf`**: GDB debugging configuration + +### Examples + +- **`restarter-auth.sh`**: Auth server restart example +- **`restarter-world.sh`**: World server restart example +- **`starter-auth.sh`**: Auth server basic start example +- **`starter-world.sh`**: World server basic start example + +## 🚀 Quick Start + +### 1. Basic Server Start (Development) + +```bash +# Start authserver directly +./starter /path/to/bin authserver + +# Start worldserver with config +./starter /path/to/bin worldserver "" /path/to/worldserver.conf +``` + +### 2. Start with Auto-Restart + +```bash +# Using simple-restarter (legacy) +./simple-restarter /path/to/bin authserver + +# Using run-engine (recommended) +./run-engine restart authserver --bin-path /path/to/bin +``` + +### 3. Production Service Management + +```bash +# Create and start a service +./service-manager.sh create auth authserver --bin-path /path/to/bin + +# List all services +./service-manager.sh list + +# Stop a service +./service-manager.sh stop auth +``` + +### 4. Using acore.sh Dashboard + +```bash +# Interactive dashboard +./acore.sh + +# Direct commands +./acore.sh run-authserver # Start authserver with restart +./acore.sh run-worldserver # Start worldserver with restart +./acore.sh service-manager # Access service manager +``` + +## ⚙️ Configuration + +### Configuration Priority (Highest to Lowest) + +1. **`conf.sh`** - User configuration file +2. **Command line arguments** - Runtime parameters +3. **Environment variables** - `RUN_ENGINE_*` variables +4. **`conf.sh.dist`** - Default configuration + +### Creating Configuration + +```bash +# Copy default configuration +cp scripts/conf.sh.dist scripts/conf.sh + +# Edit your configuration +nano scripts/conf.sh +``` + +### Key Configuration Options + +```bash +# Binary settings +export BINPATH="/path/to/azerothcore/bin" +export SERVERBIN="worldserver" # or "authserver" +export CONFIG="/path/to/worldserver.conf" + +# Session management +export SESSION_MANAGER="tmux" # none|auto|tmux|screen +export SESSION_NAME="ac-world" + +# Debugging +export GDB_ENABLED="1" # 0 or 1 +export GDB="/path/to/gdb.conf" + +# Logging +export LOGS_PATH="/path/to/logs" +export CRASHES_PATH="/path/to/crashes" +export LOG_PREFIX_NAME="realm1" +``` + +## 📖 Detailed Usage + +### 1. Run Engine + +The `run-engine` is the most advanced script with multiple operation modes: + +#### Basic Execution +```bash +# Start server once +./run-engine start worldserver --bin-path /path/to/bin + +# Start with configuration file +./run-engine start worldserver --config ./conf-world.sh + +# Start with specific server config +./run-engine start worldserver --server-config /path/to/worldserver.conf +``` + +#### Restart Mode +```bash +# Automatic restart on crash +./run-engine restart worldserver --bin-path /path/to/bin + +# Restart with session management +./run-engine restart worldserver --session-manager tmux +``` + +#### Session Management +```bash +# Start in tmux session +./run-engine start worldserver --session-manager tmux + +# Attach to existing session +tmux attach-session -t worldserver + +# Start in screen session +./run-engine start worldserver --session-manager screen + +# Attach to screen session +screen -r worldserver +``` + +#### Configuration Options +```bash +./run-engine restart worldserver \ + --bin-path /path/to/bin \ + --server-config /path/to/worldserver.conf \ + --session-manager tmux \ + --gdb-enabled 1 \ + --logs-path /path/to/logs \ + --crashes-path /path/to/crashes +``` + +### 2. Simple Restarter + +Legacy-compatible wrapper with restart functionality: + +```bash +# Basic restart +./simple-restarter /path/to/bin worldserver + +# With full parameters +./simple-restarter \ + /path/to/bin \ + worldserver \ + ./gdb.conf \ + /path/to/worldserver.conf \ + /path/to/system.log \ + /path/to/system.err \ + 1 \ + /path/to/crashes +``` + +**Parameters:** +1. Binary path (required) +2. Binary name (required) +3. GDB configuration file (optional) +4. Server configuration file (optional) +5. System log file (optional) +6. System error file (optional) +7. GDB enabled flag (0/1, optional) +8. Crashes directory path (optional) + +### 3. Starter + +Basic execution script without restart functionality: + +```bash +# Simple start +./starter /path/to/bin worldserver + +# With GDB debugging +./starter /path/to/bin worldserver ./gdb.conf /path/to/worldserver.conf "" "" 1 +``` + +### 4. Service Manager + +Production-ready service management: + +#### Creating Services +```bash +# Auto-detect provider (PM2 or systemd) +./service-manager.sh create auth authserver --bin-path /path/to/bin + +# Force PM2 +./service-manager.sh create world worldserver --provider pm2 --bin-path /path/to/bin + +# Force systemd +./service-manager.sh create world worldserver --provider systemd --bin-path /path/to/bin +``` + +#### Service Operations +```bash +# Start/stop services +./service-manager.sh start auth +./service-manager.sh stop world +./service-manager.sh restart auth + +# View logs +./service-manager.sh logs world +./service-manager.sh logs world --follow + +# Attach to console (interactive) +./service-manager.sh attach world + +# List services +./service-manager.sh list +./service-manager.sh list pm2 +./service-manager.sh list systemd + +# Delete service +./service-manager.sh delete auth +``` + +#### Service Configuration +```bash +# Update service settings +./service-manager.sh update world --session-manager screen --gdb-enabled 1 + +# Edit configuration +./service-manager.sh edit world +``` + +## 🌍 Multiple Realms Setup + +### Method 1: Using Service Manager (Recommended) + +```bash +# Create multiple world server instances +./service-manager.sh create world1 worldserver \ + --bin-path /path/to/bin \ + --server-config /path/to/worldserver-realm1.conf + +./service-manager.sh create world2 worldserver \ + --bin-path /path/to/bin \ + --server-config /path/to/worldserver-realm2.conf + +# Single auth server for all realms +./service-manager.sh create auth authserver \ + --bin-path /path/to/bin \ + --server-config /path/to/authserver.conf +``` + +### Method 2: Using Run Engine with Different Configurations + +Create separate configuration files for each realm: + +**conf-realm1.sh:** +```bash +export BINPATH="/path/to/bin" +export SERVERBIN="worldserver" +export CONFIG="/path/to/worldserver-realm1.conf" +export SESSION_NAME="ac-realm1" +export LOG_PREFIX_NAME="realm1" +export LOGS_PATH="/path/to/logs/realm1" +``` + +**conf-realm2.sh:** +```bash +export BINPATH="/path/to/bin" +export SERVERBIN="worldserver" +export CONFIG="/path/to/worldserver-realm2.conf" +export SESSION_NAME="ac-realm2" +export LOG_PREFIX_NAME="realm2" +export LOGS_PATH="/path/to/logs/realm2" +``` + +Start each realm: +```bash +./run-engine restart worldserver --config ./conf-realm1.sh +./run-engine restart worldserver --config ./conf-realm2.sh +``` + +### Method 3: Using Examples with Custom Configurations + +Copy and modify the example scripts: + +```bash +# Copy examples +cp examples/restarter-world.sh restarter-realm1.sh +cp examples/restarter-world.sh restarter-realm2.sh + +# Edit each script to point to different configuration files +# Then run: +./restarter-realm1.sh +./restarter-realm2.sh +``` + +## 🛠️ Service Management + +### PM2 Services + +When using PM2 as the service provider: + +```bash +# PM2-specific commands +pm2 list # List all PM2 processes +pm2 logs auth # View logs +pm2 monit # Real-time monitoring +pm2 restart auth # Restart service +pm2 delete auth # Remove service + +# Save PM2 configuration +pm2 save +pm2 startup # Auto-start on boot +``` + +### Systemd Services + +When using systemd as the service provider: + +```bash +# Systemd commands +systemctl --user status acore-auth # Check status +systemctl --user logs acore-auth # View logs +systemctl --user restart acore-auth # Restart +systemctl --user enable acore-auth # Enable auto-start + +# For system services (requires sudo) +sudo systemctl status acore-auth +sudo systemctl enable acore-auth +``` + +### Session Management in Services + +Services can be configured with session managers for interactive access: + +```bash +# Create service with tmux +./service-manager.sh create world worldserver \ + --bin-path /path/to/bin \ + --session-manager tmux + +# Attach to the session +./service-manager.sh attach world +# or directly: +tmux attach-session -t worldserver +``` + +## 🎮 Integration with acore.sh Dashboard + +The startup scripts are fully integrated into the AzerothCore dashboard: + +### Direct Commands + +```bash +# Run servers with simple restart (development/testing) +./acore.sh run-worldserver # Option 11 or 'rw' +./acore.sh run-authserver # Option 12 or 'ra' + +# Access service manager (production) +./acore.sh service-manager # Option 15 or 'sm' + +# Examples: +./acore.sh rw # Quick worldserver start +./acore.sh ra # Quick authserver start +./acore.sh sm create auth authserver --bin-path /path/to/bin +``` + +### What Happens Behind the Scenes + +- **run-worldserver/run-authserver**: Calls `simple-restarter` with appropriate binary +- **service-manager**: Provides full access to the service management interface +- Scripts automatically use the correct binary path from your build configuration + +## 🐛 Troubleshooting + +### Common Issues + +#### 1. Binary Not Found +```bash +Error: Binary '/path/to/bin/worldserver' not found +``` +**Solution**: Check binary path and ensure servers are compiled +```bash +# Check if binary exists +ls -la /path/to/bin/worldserver + +# Compile if needed +./acore.sh compiler build +``` + +#### 2. Configuration File Issues +```bash +Error: Configuration file not found +``` +**Solution**: Create configuration from template +```bash +cp scripts/conf.sh.dist scripts/conf.sh +# Edit conf.sh with correct paths +``` + +#### 3. Session Manager Not Available +```bash +Warning: tmux not found, falling back to direct execution +``` +**Solution**: Install required session manager +```bash +# Ubuntu/Debian +sudo apt install tmux screen + +# CentOS/RHEL +sudo yum install tmux screen +``` + +#### 4. Permission Issues (systemd) +```bash +Failed to create systemd service +``` +**Solution**: Check user permissions or use --system flag +```bash +# For user services (no sudo required) +./service-manager.sh create auth authserver --bin-path /path/to/bin + +# For system services (requires sudo) +./service-manager.sh create auth authserver --bin-path /path/to/bin --system +``` + +#### 5. PM2 Not Found +```bash +Error: PM2 is not installed +``` +**Solution**: Install PM2 +```bash +npm install -g pm2 +# or +sudo npm install -g pm2 +``` + + diff --git a/apps/startup-scripts/conf.sh.dist b/apps/startup-scripts/conf.sh.dist deleted file mode 100644 index 3a2c39578..000000000 --- a/apps/startup-scripts/conf.sh.dist +++ /dev/null @@ -1,50 +0,0 @@ -# enable/disable GDB execution -export GDB_ENABLED=0 - -# [optional] gdb file -# default: gdb.conf -export GDB="" - -# directory where binary are stored -export BINPATH="" - -# Put here the pid you configured on your worldserver.conf file -# needed when GDB_ENABLED=1 -export SERVERPID="" - -# path to configuration file (including the file name) -# ex: /home/user/azerothcore/etc/worldserver.conf -export CONFIG="" - -# path of log files -# needed by restarter to store its logs -export LOGS_PATH=""; - -# exec name -# ex: worldserver -export SERVERBIN="" - -# prefix name for log files -# to avoid collision with other restarters -export LOG_PREFIX_NAME="" - -# [optional] name of screen service -# if no specified, screen util won't be used -export SCREEN_NAME="" - -# [optional] overwrite default screen options: -A -m -d -S -# WARNING: if you are running it under a systemd service -# please do not remove -m -d arguments from screen if are you using it, -# or keep WITH_CONSOLE=0 . -# otherwise the journald-logging system will take 100% of CPU slowing -# down the whole machine. It's because a systemd service should have -# low console output. -export SCREEN_OPTIONS="" - -# enable/disable it to show the output -# within console, if disable the output will be redirect to -# logging files -# -export WITH_CONSOLE=0 - - diff --git a/apps/startup-scripts/examples/restarter-auth.sh b/apps/startup-scripts/examples/restarter-auth.sh deleted file mode 100644 index 61ea8b9cf..000000000 --- a/apps/startup-scripts/examples/restarter-auth.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -PATH_RUNENGINE="./" - -source "$PATH_RUNENGINE/run-engine" - -# you must create your conf -# copying conf.sh.dist -# and renaming as below -source "./conf-auth.sh" - -restarter - - diff --git a/apps/startup-scripts/examples/restarter-world.sh b/apps/startup-scripts/examples/restarter-world.sh deleted file mode 100644 index 9b34e114e..000000000 --- a/apps/startup-scripts/examples/restarter-world.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -PATH_RUNENGINE="./" - -source "$PATH_RUNENGINE/run-engine" - -# you must create your conf -# copying conf.sh.dist -# and renaming as below -source "./conf-world.sh" - -restarter - - diff --git a/apps/startup-scripts/examples/starter-auth.sh b/apps/startup-scripts/examples/starter-auth.sh deleted file mode 100644 index 734cfb5a2..000000000 --- a/apps/startup-scripts/examples/starter-auth.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash - -PATH_RUNENGINE="./" - -source "$PATH_RUNENGINE/run-engine" - -# you must create your conf -# copying conf.sh.dist -# and renaming as below -source "./conf-auth.sh" - -starter - diff --git a/apps/startup-scripts/examples/starter-world.sh b/apps/startup-scripts/examples/starter-world.sh deleted file mode 100644 index 697a2c85a..000000000 --- a/apps/startup-scripts/examples/starter-world.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash - -PATH_RUNENGINE="./" - -source "$PATH_RUNENGINE/run-engine" - -# you must create your conf -# copying conf.sh.dist -# and renaming as below -source "./conf-world.sh" - -starter - - diff --git a/apps/startup-scripts/run-engine b/apps/startup-scripts/run-engine deleted file mode 100644 index bc1c04a02..000000000 --- a/apps/startup-scripts/run-engine +++ /dev/null @@ -1,115 +0,0 @@ -export RUN_ENGINE_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# load default conf -if [ -e "$RUN_ENGINE_PATH/conf.dist" ]; then - source "$RUN_ENGINE_PATH/conf.sh.dist" -fi - -function finish { - if [ ! -z "$SCREEN_NAME" ]; then - screen -X -S "$SCREEN_NAME" quit - fi -} - -# disabled for now, but could be useful if we want -# shutdown the process if restarter crashes for some reason -# trap finish EXIT - -function configureFiles() { - TRACE_BEGIN_STRING="SIGSEGV" - TRACE_FILE="$LOGS_PATH/"$LOG_PREFIX_NAME"_trace.log" - ERR_FILE="$LOGS_PATH/"$LOG_PREFIX_NAME"_error.log" - SYSLOG="$LOGS_PATH/"$LOG_PREFIX_NAME"_system.log" - SYSERR="$LOGS_PATH/"$LOG_PREFIX_NAME"_system.err" - LINKS_FILE="$LOGS_PATH/"$LOG_PREFIX_NAME"_crash_links.link" -} - -function checkStatus() { - local ret=1 - # wipe do : destroy old screens + ls - #screen -wipe - #if screen -ls $1 | grep -q "No Sockets found" - #then - # return 0 - #fi - - local gdbres=$(pgrep -f "gdb -x $GDB --batch $SERVERBIN") - if [[ $GDB_ENABLED -eq 1 && ! -z $gdbres ]]; then - return 1 - fi - - # - # This is a specific check for Azeroth Core in case of screen failure - # It is possible since same binary file cannot be launched with same configuration file - # This is an extra check - # - local binres=$(pgrep -f "$SERVERBIN -c $CONFIG") - if [ ! -z $binres ]; then - return 1 - fi - - return 0 -} - -function run() { - echo $1 - if [ ! -z $1 ]; then - local OPTIONS="-A -m -d -S" - if [ ! -z "$SCREEN_OPTIONS" ]; then - OPTIONS=$SCREEN_OPTIONS - fi - - echo "> Starting with screen ( screen $OPTIONS )" - - screen $OPTIONS $1 "$RUN_ENGINE_PATH/starter" $2 $3 "$4" "$5" "$6" $7 "$BINPATH/crashes" - else - $RUN_ENGINE_PATH/starter $2 $3 "$4" "$5" "$6" $7 "$BINPATH/crashes" - fi -} - -function starter() { - cd $BINPATH - - mkdir -p "$LOGS_PATH" - mkdir -p "$BINPATH"/crashes - - configureFiles - - run "$SCREEN_NAME" "$SERVERBIN" "$GDB" "$CONFIG" "$SYSLOG" "$SYSERR" "$GDB_ENABLED" -} - - -function restarter() { - cd $BINPATH - - mkdir -p "$LOGS_PATH" - mkdir -p "$BINPATH"/crashes - - configureFiles - - if [ ! -f $TRACE_FILE ]; then - touch $TRACE_FILE - fi - - while : - do - if checkStatus $SCREEN_NAME; then - DATE=$(date) - echo "Restarting $SCREEN_NAME Core blizz($DATE)" - if [ $GDB_ENABLED -eq 1 ]; then - echo "GDB enabled" - grep -B 10 -A 1800 "$TRACE_BEGIN_STRING" "$SYSLOG" >> "$TRACE_FILE" - cat "$SYSERR" > "$ERR_FILE" - run "$SCREEN_NAME" "$SERVERBIN" "$GDB" "$CONFIG" "$SYSLOG" "$SYSERR" 1 - fi - - if [ $GDB_ENABLED -eq 0 ]; then - echo "GDB disabled" - run "$SCREEN_NAME" "$SERVERBIN" null "$CONFIG" null null 0 - fi - fi - - sleep 10 - done -} - diff --git a/apps/startup-scripts/simple-restarter b/apps/startup-scripts/simple-restarter deleted file mode 100755 index 6415c0a44..000000000 --- a/apps/startup-scripts/simple-restarter +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env bash - -#PARAMETER 1: directory -#PARAMETER 2: binary file -#PARAMETER 3: gdb on/off - -bin_path="${1:-$AC_RESTARTER_BINPATH}" -bin_file="${2:-$AC_RESTARTER_BINFILE}" -with_gdb="${3:-$AC_RESTARTER_WITHGDB}" - -CURRENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd ) - -_instant_crash_count=0 -_restart_count=0 - -if [ "$#" -ne 2 ]; then - echo "Usage: $0 path filename" - echo "Example: $0 $HOME/azerothcore/bin worldserver" - exit 1 -fi - -while true -do - if [ ! -f "$bin_path/$bin_file" ]; then - echo "$bin_path/$bin_file doesn't exists!" - exit 1 - fi - - STARTING_TIME=$(date +%s) - - cd "$bin_path"; - - if [ "$with_gdb" = true ]; then - echo "Running with GDB enabled" - gdb -x "$CURRENT_PATH/gdb.conf" --batch "./$bin_file" - else - echo "Running without GDB" - "./$bin_file" - fi - - _exit_code=$? - - echo "exit code: $_exit_code" - # stop restarter on SIGKILL (disabled for now) - # 128 + 9 (SIGKILL) - #if [ $_exit_code -eq 137 ]; then - # echo "$bin_file has been killed" - # exit 0 - #fi - - echo "$bin_file terminated, restarting..." - - ENDING_TIME=$(date +%s) - DIFFERENCE=$(( $ENDING_TIME - $STARTING_TIME )) - - ((_restart_count++)) - echo "$bin_file Terminated after $DIFFERENCE seconds, termination count: : $_restart_count" - - if [ $DIFFERENCE -lt 10 ]; then - # increment instant crash if runtime is lower than 10 seconds - ((_instant_crash_count++)) - else - _instant_crash_count=0 # reset count - fi - - if [ $_instant_crash_count -gt 5 ]; then - echo "$bin_file Restarter exited. Infinite crash loop prevented. Please check your system" - exit 1 - fi -done diff --git a/apps/startup-scripts/src/.gitignore b/apps/startup-scripts/src/.gitignore new file mode 100644 index 000000000..cd3d22536 --- /dev/null +++ b/apps/startup-scripts/src/.gitignore @@ -0,0 +1 @@ +logs \ No newline at end of file diff --git a/apps/startup-scripts/src/conf.sh.dist b/apps/startup-scripts/src/conf.sh.dist new file mode 100644 index 000000000..69fbeadb9 --- /dev/null +++ b/apps/startup-scripts/src/conf.sh.dist @@ -0,0 +1,57 @@ +# AzerothCore Run Engine Default Configuration +# This file contains default values that can be overridden by environment variables +# Priority order: conf.sh > environment variables > conf.sh.dist (this file) + +# Enable/disable GDB execution +export GDB_ENABLED="${RUN_ENGINE_GDB_ENABLED:-0}" + +# [optional] GDB configuration file +# default: gdb.conf +export GDB="${RUN_ENGINE_GDB:-}" + +# Directory where binaries are stored +export BINPATH="${RUN_ENGINE_BINPATH:-}" + +# Server binary name (e.g., worldserver, authserver) +export SERVERBIN="${RUN_ENGINE_SERVERBIN:-}" + +# Path to server configuration file (including the file name) +# ex: /home/user/azerothcore/etc/worldserver.conf +export CONFIG="${RUN_ENGINE_CONFIG:-}" + +# Session manager to use: none|auto|tmux|screen +# auto will detect the best available option +export SESSION_MANAGER="${RUN_ENGINE_SESSION_MANAGER:-none}" + +# Default session manager (fallback when SESSION_MANAGER is not set) +export DEFAULT_SESSION_MANAGER="${RUN_ENGINE_DEFAULT_SESSION_MANAGER:-none}" + +# Path of the crashes directory +# If not specified, it will be created in the same directory as logs named "crashes" +export CRASHES_PATH="${RUN_ENGINE_CRASHES_PATH:-}" + +# Path of log files directory +export LOGS_PATH="${RUN_ENGINE_LOGS_PATH:-}" + +# Prefix name for log files to avoid collision with other instances +export LOG_PREFIX_NAME="${RUN_ENGINE_LOG_PREFIX_NAME:-}" + +# [optional] Name of session (tmux session or screen session) +# If not specified, a default name will be generated based on server binary +export SESSION_NAME="${RUN_ENGINE_SESSION_NAME:-}" + +# [optional] Screen-specific options: -A -m -d -S +# WARNING: if you are running it under a systemd service +# please do not remove -m -d arguments from screen if you are using it, +# or keep WITH_CONSOLE=0. Otherwise the journald-logging system will take +# 100% of CPU slowing down the whole machine. +export SCREEN_OPTIONS="${RUN_ENGINE_SCREEN_OPTIONS:-}" + +# Enable/disable console output +# If disabled, output will be redirected to logging files +export WITH_CONSOLE="${RUN_ENGINE_WITH_CONSOLE:-0}" + +# Server PID (needed when GDB_ENABLED=1) +export SERVERPID="${RUN_ENGINE_SERVERPID:-}" + + diff --git a/apps/startup-scripts/src/examples/restarter-auth.sh b/apps/startup-scripts/src/examples/restarter-auth.sh new file mode 100755 index 000000000..9557ebdf3 --- /dev/null +++ b/apps/startup-scripts/src/examples/restarter-auth.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash + +# AzerothCore Auth Server Restarter Example +# This example shows how to use the run-engine with restart functionality for authserver + +PATH_RUNENGINE="./" +CONFIG_FILE="./conf-auth.sh" + +# Method 1: Using configuration file (recommended) +if [ -f "$CONFIG_FILE" ]; then + echo "Starting authserver with restart loop using config file: $CONFIG_FILE" + source "$CONFIG_FILE" + "$PATH_RUNENGINE/run-engine" restart "$SERVERBIN" --config "$CONFIG_FILE" +else + echo "Error: Configuration file not found: $CONFIG_FILE" + echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist" + echo "Make sure to set: export SERVERBIN=\"authserver\"" + echo "" + echo "Alternative: Start with binary path directly" + echo "Example: $PATH_RUNENGINE/run-engine restart /path/to/bin/authserver" + echo "Example: $PATH_RUNENGINE/run-engine restart authserver # if in PATH" + exit 1 +fi + +# Method 2: Direct binary path (full path) +# Uncomment the line below to start with full binary path +# +# "$PATH_RUNENGINE/run-engine" restart /home/user/azerothcore/bin/authserver --server-config /path/to/authserver.conf + +# Method 3: Binary name only (system PATH) +# Uncomment the line below if authserver is in your system PATH +# +# "$PATH_RUNENGINE/run-engine" restart authserver --server-config /path/to/authserver.conf + +# Method 4: With session manager (tmux/screen) +# Uncomment the line below to use tmux session +# +# "$PATH_RUNENGINE/run-engine" restart authserver --session-manager tmux --server-config /path/to/authserver.conf + +# Method 5: Environment variables only +# Uncomment the lines below for environment variable configuration +# +# export RUN_ENGINE_BINPATH="/path/to/your/bin" +# export RUN_ENGINE_SERVERBIN="authserver" +# export RUN_ENGINE_CONFIG="/path/to/authserver.conf" +# "$PATH_RUNENGINE/run-engine" restart authserver + + diff --git a/apps/startup-scripts/src/examples/restarter-world.sh b/apps/startup-scripts/src/examples/restarter-world.sh new file mode 100755 index 000000000..2649b3f84 --- /dev/null +++ b/apps/startup-scripts/src/examples/restarter-world.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# AzerothCore World Server Restarter Example +# This example shows how to use the run-engine with restart functionality for worldserver + +PATH_RUNENGINE="./" +CONFIG_FILE="./conf-world.sh" + +# Method 1: Using configuration file (recommended) +if [ -f "$CONFIG_FILE" ]; then + echo "Starting worldserver with restart loop using config file: $CONFIG_FILE" + "$PATH_RUNENGINE/run-engine" restart "$SERVERBIN" --config "$CONFIG_FILE" +else + echo "Error: Configuration file not found: $CONFIG_FILE" + echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist" + echo "Make sure to set: export SERVERBIN=\"worldserver\"" + echo "" + echo "Alternative: Start with binary path directly" + echo "Example: $PATH_RUNENGINE/run-engine restart /path/to/bin/worldserver" + echo "Example: $PATH_RUNENGINE/run-engine restart worldserver # if in PATH" + exit 1 +fi + +# Method 2: Direct binary path (full path) +# Uncomment the line below to start with full binary path +# +# "$PATH_RUNENGINE/run-engine" restart /home/user/azerothcore/bin/worldserver --server-config /path/to/worldserver.conf + +# Method 3: Binary name only (system PATH) +# Uncomment the line below if worldserver is in your system PATH +# +# "$PATH_RUNENGINE/run-engine" restart worldserver --server-config /path/to/worldserver.conf + +# Method 4: With session manager (tmux/screen) +# Uncomment the line below to use tmux session +# +# "$PATH_RUNENGINE/run-engine" restart worldserver --session-manager tmux --server-config /path/to/worldserver.conf + +# Method 5: Environment variables only +# Uncomment the lines below for environment variable configuration +# +# export RUN_ENGINE_BINPATH="/path/to/your/bin" +# export RUN_ENGINE_SERVERBIN="worldserver" +# export RUN_ENGINE_CONFIG="/path/to/worldserver.conf" +# "$PATH_RUNENGINE/run-engine" restart worldserver + + diff --git a/apps/startup-scripts/src/examples/starter-auth.sh b/apps/startup-scripts/src/examples/starter-auth.sh new file mode 100755 index 000000000..52fcb384c --- /dev/null +++ b/apps/startup-scripts/src/examples/starter-auth.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +# AzerothCore Auth Server Starter Example +# This example shows how to use the run-engine to start authserver without restart loop + +PATH_RUNENGINE="./" +CONFIG_FILE="./conf-auth.sh" + +# Method 1: Using configuration file (recommended) +if [ -f "$CONFIG_FILE" ]; then + echo "Starting authserver (single run) with config file: $CONFIG_FILE" + "$PATH_RUNENGINE/run-engine" start "$SERVERBIN" --config "$CONFIG_FILE" +else + echo "Error: Configuration file not found: $CONFIG_FILE" + echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist" + echo "Make sure to set: export SERVERBIN=\"authserver\"" + echo "" + echo "Alternative: Start with binary path directly" + echo "Example: $PATH_RUNENGINE/run-engine start /path/to/bin/authserver" + echo "Example: $PATH_RUNENGINE/run-engine start authserver # if in PATH" + exit 1 +fi + +# Method 2: Direct binary path (full path) +# Uncomment the line below to start with full binary path +# +# "$PATH_RUNENGINE/run-engine" start /home/user/azerothcore/bin/authserver --server-config /path/to/authserver.conf + +# Method 3: Binary name only (system PATH) +# Uncomment the line below if authserver is in your system PATH +# +# "$PATH_RUNENGINE/run-engine" start authserver --server-config /path/to/authserver.conf + +# Method 4: With session manager (tmux/screen) +# Uncomment the line below to use tmux session +# +# "$PATH_RUNENGINE/run-engine" start authserver --session-manager tmux --server-config /path/to/authserver.conf + +# Method 5: Environment variables only +# Uncomment the lines below for environment variable configuration +# +# export RUN_ENGINE_BINPATH="/path/to/your/bin" +# export RUN_ENGINE_SERVERBIN="authserver" +# export RUN_ENGINE_CONFIG="/path/to/authserver.conf" +# "$PATH_RUNENGINE/run-engine" start authserver + diff --git a/apps/startup-scripts/src/examples/starter-world.sh b/apps/startup-scripts/src/examples/starter-world.sh new file mode 100755 index 000000000..1eb1c4d32 --- /dev/null +++ b/apps/startup-scripts/src/examples/starter-world.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash + +# AzerothCore World Server Starter Example +# This example shows how to use the run-engine to start worldserver without restart loop + +PATH_RUNENGINE="./" +CONFIG_FILE="./conf-world.sh" + +# Method 1: Using configuration file (recommended) +if [ -f "$CONFIG_FILE" ]; then + echo "Starting worldserver (single run) with config file: $CONFIG_FILE" + "$PATH_RUNENGINE/run-engine" start "$SERVERBIN" --config "$CONFIG_FILE" +else + echo "Error: Configuration file not found: $CONFIG_FILE" + echo "Please create $CONFIG_FILE by copying and modifying conf.sh.dist" + echo "Make sure to set: export SERVERBIN=\"worldserver\"" + echo "" + echo "Alternative: Start with binary path directly" + echo "Example: $PATH_RUNENGINE/run-engine start /path/to/bin/worldserver" + echo "Example: $PATH_RUNENGINE/run-engine start worldserver # if in PATH" + exit 1 +fi + +# Method 2: Direct binary path (full path) +# Uncomment the line below to start with full binary path +# +# "$PATH_RUNENGINE/run-engine" start /home/user/azerothcore/bin/worldserver --server-config /path/to/worldserver.conf + +# Method 3: Binary name only (system PATH) +# Uncomment the line below if worldserver is in your system PATH +# +# "$PATH_RUNENGINE/run-engine" start worldserver --server-config /path/to/worldserver.conf + +# Method 4: With session manager (tmux/screen) +# Uncomment the line below to use tmux session +# +# "$PATH_RUNENGINE/run-engine" start worldserver --session-manager tmux --server-config /path/to/worldserver.conf + +# Method 5: Environment variables only +# Uncomment the lines below for environment variable configuration +# +# export RUN_ENGINE_BINPATH="/path/to/your/bin" +# export RUN_ENGINE_SERVERBIN="worldserver" +# export RUN_ENGINE_CONFIG="/path/to/worldserver.conf" +# "$PATH_RUNENGINE/run-engine" start worldserver + + diff --git a/apps/startup-scripts/gdb.conf b/apps/startup-scripts/src/gdb.conf similarity index 100% rename from apps/startup-scripts/gdb.conf rename to apps/startup-scripts/src/gdb.conf diff --git a/apps/startup-scripts/src/run-engine b/apps/startup-scripts/src/run-engine new file mode 100755 index 000000000..2860339a5 --- /dev/null +++ b/apps/startup-scripts/src/run-engine @@ -0,0 +1,467 @@ +#!/usr/bin/env bash + +# AzerothCore Run Engine +# Advanced script for running AzerothCore services with session management and restart capabilities +# +# This script can be sourced to provide functions or executed directly with parameters +# +# Configuration Priority Order (highest to lowest): +# 1. conf.sh - User configuration file (highest priority) +# 2. Command line arguments (--config, --server-config, etc.) +# 3. Environment variables (RUN_ENGINE_*) +# 4. conf.sh.dist - Default configuration (lowest priority) +# +# Environment Variables: +# RUN_ENGINE_CONFIG_FILE - Path to temporary configuration file (optional) +# RUN_ENGINE_SESSION_MANAGER - Session manager (none|auto|tmux|screen, default: auto) +# RUN_ENGINE_BINPATH - Binary directory path +# RUN_ENGINE_SERVERBIN - Server binary name (worldserver|authserver) +# RUN_ENGINE_CONFIG - Server configuration file path +# RUN_ENGINE_LOGS_PATH - Directory for log files +# RUN_ENGINE_CRASHES_PATH - Directory for crash dumps +# RUN_ENGINE_SESSION_NAME - Session name for tmux/screen + +export RUN_ENGINE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Configuration priority order: +# 1. conf.sh (highest priority - user overrides) +# 2. Environment variables (RUN_ENGINE_*) +# 3. conf.sh.dist (lowest priority - defaults) + +# Load default configuration first (sets defaults from environment variables) +if [ -e "$RUN_ENGINE_PATH/conf.sh.dist" ]; then + source "$RUN_ENGINE_PATH/conf.sh.dist" +fi + +# Load user configuration if exists (this takes priority over everything) +if [ -e "$RUN_ENGINE_PATH/conf.sh" ]; then + source "$RUN_ENGINE_PATH/conf.sh" +fi + +# Load configuration +function load_config() { + local config_file="$1" + + # If a specific config file is provided via command line, load it + # This allows temporary overrides for specific runs + if [ -n "$config_file" ] && [ -e "$config_file" ]; then + echo "Loading configuration from: $config_file" + source "$config_file" + elif [ -n "$RUN_ENGINE_CONFIG_FILE" ] && [ -e "$RUN_ENGINE_CONFIG_FILE" ]; then + echo "Loading configuration from environment: $RUN_ENGINE_CONFIG_FILE" + source "$RUN_ENGINE_CONFIG_FILE" + fi + + # Final override with any remaining environment variables + # This ensures that even after loading config files, environment variables take precedence + BINPATH="${RUN_ENGINE_BINPATH:-$BINPATH}" + SERVERBIN="${RUN_ENGINE_SERVERBIN:-$SERVERBIN}" + CONFIG="${RUN_ENGINE_CONFIG:-$CONFIG}" + SESSION_MANAGER="${RUN_ENGINE_SESSION_MANAGER:-$SESSION_MANAGER}" + LOGS_PATH="${RUN_ENGINE_LOGS_PATH:-$LOGS_PATH}" + CRASHES_PATH="${RUN_ENGINE_CRASHES_PATH:-$CRASHES_PATH}" +} + +# Detect available session manager +function detect_session_manager() { + if command -v tmux >/dev/null 2>&1; then + echo "tmux" + elif command -v screen >/dev/null 2>&1; then + echo "screen" + else + echo "none" + fi +} + +# Determine which session manager to use +function get_session_manager() { + local requested="$1" + + case "$requested" in + "none") + echo "none" + ;; + "auto") + detect_session_manager + ;; + "tmux") + if command -v tmux >/dev/null 2>&1; then + echo "tmux" + else + echo "error" + fi + ;; + "screen") + if command -v screen >/dev/null 2>&1; then + echo "screen" + else + echo "error" + fi + ;; + *) + echo "none" + ;; + esac +} + +# Configure log files +function configure_files() { + TRACE_BEGIN_STRING="SIGSEGV" + TRACE_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_trace.log" + ERR_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_error.log" + SYSLOG="$LOGS_PATH/${LOG_PREFIX_NAME}_system.log" + SYSERR="$LOGS_PATH/${LOG_PREFIX_NAME}_system.err" + LINKS_FILE="$LOGS_PATH/${LOG_PREFIX_NAME}_crash_links.link" +} + +# Check if service is running +function check_status() { + local session_name="$1" + local ret=1 + + # Check for GDB process + local gdbres=$(pgrep -f "gdb.*--batch.*$SERVERBIN") + if [[ "$GDB_ENABLED" -eq 1 && -n "$gdbres" ]]; then + return 1 + fi + + # Check for binary process + local binres=$(pgrep -f "$SERVERBIN -c $CONFIG") + if [ -n "$binres" ]; then + return 1 + fi + + # Check session manager + if [ -n "$session_name" ]; then + case "$(get_session_manager "${SESSION_MANAGER:-auto}")" in + "tmux") + tmux has-session -t "$session_name" 2>/dev/null && return 1 + ;; + "screen") + screen -ls "$session_name" 2>/dev/null | grep -q "$session_name" && return 1 + ;; + esac + fi + + return 0 +} + +# Run with session manager +function run_with_session() { + local session_manager="$1" + local session_name="$2" + local wrapper="$3" + shift 3 + local args=("$@") + + if [ "$wrapper" = "simple-restarter" ]; then + script_path="$RUN_ENGINE_PATH/simple-restarter" + else + script_path="$RUN_ENGINE_PATH/starter" + fi + + case "$session_manager" in + "tmux") + echo "> Starting with tmux session: $session_name - attach with 'tmux attach -t $session_name'" + tmux new-session -d -s "$session_name" -- "$script_path" "${args[@]}" + ;; + "screen") + local OPTIONS="-A -m -d -S" + if [ -n "$SCREEN_OPTIONS" ]; then + OPTIONS="$SCREEN_OPTIONS" + fi + echo "> Starting with screen session: $session_name (options: $OPTIONS) - attach with 'screen -r $session_name'" + echo "screen $OPTIONS \"$session_name\" -- \"$script_path\" ${args[*]}" + screen $OPTIONS "$session_name" -- "$script_path" "${args[@]}" + ;; + "none"|*) + echo "> Starting without session manager" + "$script_path" "${args[@]}" + ;; + esac +} + +# Parse command line arguments +function parse_arguments() { + local mode="$1" + local serverbin="$2" + shift 2 + + local config_file="" + local serverconfig="" + local session_manager="" + + # Parse named arguments + while [[ $# -gt 0 ]]; do + case $1 in + --config) + config_file="$2" + shift 2 + ;; + --server-config) + serverconfig="$2" + shift 2 + ;; + --session-manager) + session_manager="$2" + shift 2 + ;; + *) + echo "Unknown argument: $1" + return 1 + ;; + esac + done + + # Export parsed values for use by start_service + export PARSED_MODE="$mode" + export PARSED_SERVERBIN="$serverbin" + export PARSED_CONFIG_FILE="$config_file" + export PARSED_SERVERCONFIG="$serverconfig" + export PARSED_SESSION_MANAGER="$session_manager" +} + +# Start service (single run or with simple-restarter) +function start_service() { + local config_file="$1" + local serverbin_path="$2" + local serverconfig="$3" + local use_restarter="${4:-false}" + local session_manager_choice="$5" + + # Load configuration first + load_config "$config_file" + + # if no session manager is specified, get it from config + if [ -z "$session_manager_choice" ]; then + session_manager_choice="$SESSION_MANAGER" + fi + + + # Parse serverbin_path to extract BINPATH and SERVERBIN + if [ -n "$serverbin_path" ]; then + # If it's a full path, extract directory and binary name + if [[ "$serverbin_path" == */* ]]; then + BINPATH="$(dirname "$serverbin_path")" + SERVERBIN="$(basename "$serverbin_path")" + else + # If it's just a binary name, use it as-is (system PATH) + SERVERBIN="$serverbin_path" + BINPATH="${BINPATH:-""}" # Empty means use current directory or system PATH + fi + fi + + # Use environment/config values if not set from command line + BINPATH="${BINPATH:-$RUN_ENGINE_BINPATH}" + SERVERBIN="${SERVERBIN:-$RUN_ENGINE_SERVERBIN}" + CONFIG="${serverconfig:-$RUN_ENGINE_CONFIG}" + + echo "SERVERBIN: $SERVERBIN" + + # Validate required parameters + if [ -z "$SERVERBIN" ]; then + echo "Error: SERVERBIN is required" + echo "Could not determine server binary from: $serverbin_path" + echo "Provide it as:" + echo " - Full path: $0 /path/to/bin/worldserver" + echo " - Binary name: $0 worldserver" + echo " - Environment variables: RUN_ENGINE_SERVERBIN" + echo " - Configuration file with SERVERBIN variable" + return 1 + fi + + # If BINPATH is set, validate binary exists and create log paths + if [ -n "$BINPATH" ]; then + if [ ! -d "$BINPATH" ]; then + echo "Error: BINPATH not found: $BINPATH" + return 1 + fi + + # Set up directories and logging relative to BINPATH + LOGS_PATH="${LOGS_PATH:-"$BINPATH/logs"}" + mkdir -p "$LOGS_PATH" + mkdir -p "$LOGS_PATH/crashes" + else + # For system binaries, try to detect binary location and create logs accordingly + local detected_binpath="" + + # Try to find binary in system PATH + local binary_location=$(which "$SERVERBIN" 2>/dev/null) + if [ -n "$binary_location" ]; then + detected_binpath="$(dirname "$binary_location")" + echo "Binary found in system PATH: $binary_location" + # Set BINPATH to the detected location so starter script can find the binary + BINPATH="$detected_binpath" + fi + + # Set up log paths based on detected or fallback location + if [ -n "$detected_binpath" ]; then + LOGS_PATH="${LOGS_PATH:-"$detected_binpath/logs"}" + else + # Fallback to current directory for logs + LOGS_PATH="${LOGS_PATH:-./logs}" + fi + + CRASHES_PATH="${CRASHES_PATH:-"$LOGS_PATH/crashes"}" + + mkdir -p "$LOGS_PATH" + mkdir -p "$CRASHES_PATH" + fi + + # Set up logging names + LOG_PREFIX_NAME="${LOG_PREFIX_NAME:-${SERVERBIN%server}}" + + # Set up session name (with backward compatibility for SCREEN_NAME) + SESSION_NAME="${SESSION_NAME:-$SCREEN_NAME}" + SESSION_NAME="${SESSION_NAME:-AC-${SERVERBIN%server}}" + + configure_files + + local session_manager=$(get_session_manager "$session_manager_choice") + + if [ "$session_manager" = "error" ]; then + echo "Error: Invalid session manager specified: $session_manager_choice, is it installed?" + exit 1 + fi + + echo "Using session manager: $session_manager" + echo "Starting server: $SERVERBIN" + + if [ -n "$CONFIG" ]; then + echo "Server config: $CONFIG" + else + echo "Server config: default (not specified)" + fi + + if [ "$use_restarter" = "true" ]; then + # Use simple-restarter for restart functionality + local gdb_enabled="${GDB_ENABLED:-0}" + run_with_session "$session_manager" "$SESSION_NAME" "simple-restarter" "$BINPATH" "$SERVERBIN" "$GDB" "$CONFIG" "$SYSLOG" "$SYSERR" "$gdb_enabled" "$CRASHES_PATH" + else + # Single run using starter + local gdb_enabled="${GDB_ENABLED:-0}" + run_with_session "$session_manager" "$SESSION_NAME" "starter" "$BINPATH" "$SERVERBIN" "$GDB" "$CONFIG" "$SYSLOG" "$SYSERR" "$gdb_enabled" "$CRASHES_PATH" + fi +} + +# Cleanup function +function finish() { + local session_manager=$(get_session_manager "${SESSION_MANAGER:-auto}") + if [ -n "$SESSION_NAME" ]; then + case "$session_manager" in + "tmux") + tmux kill-session -t "$SESSION_NAME" 2>/dev/null || true + ;; + "screen") + screen -X -S "$SESSION_NAME" quit 2>/dev/null || true + ;; + esac + fi +} + +# Legacy compatibility functions for old examples +function restarter() { + echo "Legacy function 'restarter' called - redirecting to new API" + start_service "" "" "" "true" "${SESSION_MANAGER:-auto}" +} + +function starter() { + echo "Legacy function 'starter' called - redirecting to new API" + start_service "" "" "" "false" "${SESSION_MANAGER:-auto}" +} + +# Set trap for cleanup (currently disabled to avoid interfering with systemd) +# trap finish EXIT + +# Main execution when script is run directly +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + case "${1:-help}" in + "start"|"restart") + if [ $# -lt 2 ]; then + echo "Error: Missing required arguments" + echo "Usage: $0 [options]" + echo "Example: $0 start worldserver --config ./conf-world.sh --server-config worldserver.conf" + exit 1 + fi + + # Parse arguments + if ! parse_arguments "$@"; then + exit 1 + fi + + # Determine restart mode + use_restarter="false" + if [ "$PARSED_MODE" = "restart" ]; then + use_restarter="true" + fi + + # Start service with parsed arguments + start_service "$PARSED_CONFIG_FILE" "$PARSED_SERVERBIN" "$PARSED_SERVERCONFIG" "$use_restarter" "$PARSED_SESSION_MANAGER" + ;; + "help"|*) + echo "AzerothCore Run Engine" + echo "" + echo "Usage: $0 [options]" + echo "" + echo "Modes:" + echo " start - Start service once (no restart on crash)" + echo " restart - Start service with restart on crash (uses simple-restarter)" + echo "" + echo "Required Parameters:" + echo " serverbin - Server binary (full path or binary name)" + echo " Full path: /path/to/bin/worldserver" + echo " Binary name: worldserver (uses system PATH)" + echo "" + echo "Options:" + echo " --config - Path to configuration file" + echo " --server-config - Server configuration file (sets -c parameter)" + echo " --session-manager - Session manager: none|auto|tmux|screen (default: auto)" + echo "" + echo "Configuration Priority (highest to lowest):" + echo " 1. conf.sh - User configuration file" + echo " 2. Command line arguments (--config, --server-config, etc.)" + echo " 3. Environment variables (RUN_ENGINE_*)" + echo " 4. conf.sh.dist - Default configuration" + echo "" + echo "Environment Variables:" + echo " RUN_ENGINE_CONFIG_FILE - Config file path" + echo " RUN_ENGINE_SESSION_MANAGER - Session manager (default: auto)" + echo " RUN_ENGINE_BINPATH - Binary directory path" + echo " RUN_ENGINE_SERVERBIN - Server binary name" + echo " RUN_ENGINE_CONFIG - Server configuration file" + echo " RUN_ENGINE_LOGS_PATH - Directory for log files" + echo " RUN_ENGINE_CRASHES_PATH - Directory for crash dumps" + echo " RUN_ENGINE_SESSION_NAME - Session name for tmux/screen" + echo "" + echo "Examples:" + echo "" + echo " # Using full path to binary" + echo " $0 start /home/user/ac/bin/worldserver" + echo "" + echo " # Using binary name (system PATH)" + echo " $0 start worldserver" + echo "" + echo " # With configuration file" + echo " $0 start worldserver --config ./conf-world.sh" + echo "" + echo " # With server configuration (sets -c parameter)" + echo " $0 start /path/to/bin/worldserver --server-config /etc/worldserver.conf" + echo "" + echo " # With session manager" + echo " $0 restart worldserver --session-manager tmux" + echo "" + echo " # Complete example" + echo " $0 restart /home/user/ac/bin/worldserver --config ./conf-world.sh --server-config worldserver.conf --session-manager screen" + echo "" + echo "Binary Resolution:" + echo " - Full path (contains /): Extracts directory and binary name" + echo " - Binary name only: Uses system PATH to find executable" + echo " Auto-detection will check current directory first, then system PATH" + echo "" + echo "Server Config:" + echo " If --server-config is specified, it's passed as -c parameter to the server." + echo " If not specified, the server will use its default configuration." + ;; + esac +fi + diff --git a/apps/startup-scripts/src/service-manager.sh b/apps/startup-scripts/src/service-manager.sh new file mode 100755 index 000000000..1c44c9c24 --- /dev/null +++ b/apps/startup-scripts/src/service-manager.sh @@ -0,0 +1,1261 @@ +#!/usr/bin/env bash + +# AzerothCore Service Setup +# A unified interface for managing AzerothCore services with PM2 or systemd +# This script provides commands to create, update, delete, and manage server instances + +# Script location +CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +SCRIPT_DIR="$CURRENT_PATH" + +ROOT_DIR="$(cd "$CURRENT_PATH/../../.." && pwd)" + +# Configuration directory +CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/azerothcore/services" +REGISTRY_FILE="$CONFIG_DIR/service_registry.json" + +# Colors for output +YELLOW='\033[1;33m' +GREEN='\033[0;32m' +RED='\033[0;31m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Create config directory if it doesn't exist +mkdir -p "$CONFIG_DIR" + +# Initialize registry if it doesn't exist +if [ ! -f "$REGISTRY_FILE" ]; then + echo "[]" > "$REGISTRY_FILE" +fi + +# Check dependencies +check_dependencies() { + command -v jq >/dev/null 2>&1 || { + echo -e "${RED}Error: jq is required but not installed. Please install jq package.${NC}" + exit 1 + } +} + +# Check if PM2 is installed +check_pm2() { + if ! command -v pm2 >/dev/null 2>&1; then + echo -e "${RED}Error: PM2 is not installed. Please install PM2 first:${NC}" + echo " npm install -g pm2" + return 1 + fi +} + +# Check if systemd is available +check_systemd() { + if ! command -v systemctl >/dev/null 2>&1; then + echo -e "${RED}Error: systemd is not available on this system${NC}" + return 1 + fi +} + +# Auto-detect provider based on system availability +auto_detect_provider() { + if check_systemd 2>/dev/null; then + echo "systemd" + elif check_pm2 2>/dev/null; then + echo "pm2" + else + echo -e "${RED}Error: Neither systemd nor PM2 is available on this system${NC}" >&2 + echo -e "${YELLOW}Please install PM2 (npm install -g pm2) or ensure systemd is available${NC}" >&2 + return 1 + fi +} + +# Helper functions +function print_help() { + + local base_name="$(basename $0)" + + echo -e "${BLUE}AzerothCore Service Setup${NC}" + echo "A unified interface for managing AzerothCore services with PM2 or systemd" + echo "" + echo "Usage:" + echo " $base_name create [options]" + echo " $base_name update [options]" + echo " $base_name delete " + echo " $base_name list [provider]" + echo " $base_name start|stop|restart|status " + echo " $base_name logs [--follow]" + echo " $base_name attach " + echo " $base_name edit-config " + echo "" + echo "Providers:" + echo " pm2 - Use PM2 process manager" + echo " systemd - Use systemd service manager" + echo " auto - Automatically choose systemd or fallback to pm2 (default)" + echo "" + echo "Service Types:" + echo " auth - Authentication server" + echo " world - World server (use different names for multiple realms)" + echo "" + echo "Options:" + echo " --provider - Service provider (pm2|systemd|auto, default: auto)" + echo " --bin-path - Path to the server binary directory (required)" + echo " --server-config - Path to the server configuration file" + echo " --session-manager - Session manager (none|tmux|screen, default: none)" + echo " --gdb-enabled <0|1> - Enable GDB debugging (default: 0)" + echo " --system - Create as system service (systemd only, requires sudo)" + echo " --user - Create as user service (systemd only, default)" + echo " --max-memory - Maximum memory limit (PM2 only)" + echo " --max-restarts - Maximum restart attempts (PM2 only)" + echo " --no-start - Do not start the service after creation" + echo "" + echo "Examples:" + echo " # Create auth server (auto-detects provider)" + echo " $base_name create auth authserver --bin-path /home/user/azerothcore/bin" + echo "" + echo " # Create PM2 auth server explicitly" + echo " $base_name create auth authserver --provider pm2 --bin-path /home/user/azerothcore/bin" + echo "" + echo " # Create systemd world server with debugging enabled" + echo " $base_name create world worldserver-realm1 --provider systemd" + echo " --bin-path /home/user/azerothcore/bin" + echo " --server-config /home/user/azerothcore/etc/worldserver.conf" + echo " --gdb-enabled 1 --session-manager tmux" + echo "" + echo " # Create service without starting it" + echo " $base_name create auth authserver --bin-path /home/user/azerothcore/bin --no-start" + echo "" + echo " # Update run-engine configuration" + echo " $base_name update worldserver-realm1 --session-manager screen --gdb-enabled 0" + echo "" + echo " # Service management" + echo " $base_name start worldserver-realm1" + echo " $base_name logs worldserver-realm1 --follow" + echo " $base_name attach worldserver-realm1" + echo " $base_name list pm2" + echo "" + echo "Notes:" + echo " - Configuration editing modifies run-engine settings (GDB, session manager, etc.)" + echo " - Use --server-config for the actual server configuration file" + echo " - Services use run-engine in 'start' mode for single-shot execution" + echo " - Restart on crash is handled by PM2 or systemd, not by run-engine" + echo " - attach command automatically detects the configured session manager and connects appropriately" + echo " - attach always provides interactive access to the server console" + echo " - Use 'logs' command to view service logs without interaction" +} + +function register_service() { + local service_name="$1" + local provider="$2" + local service_type="$3" + local config_file="$CONFIG_DIR/$service_name.conf" + + # Add to registry + local tmp_file=$(mktemp) + jq --arg name "$service_name" \ + --arg provider "$provider" \ + --arg type "$service_type" \ + --arg config "$config_file" \ + '. += [{"name": $name, "provider": $provider, "type": $type, "config": $config}]' \ + "$REGISTRY_FILE" > "$tmp_file" + mv "$tmp_file" "$REGISTRY_FILE" + + echo -e "${GREEN}Service $service_name registered successfully${NC}" +} + +function validate_service_exists() { + local service_name="$1" + local provider="$2" + + if [ "$provider" = "pm2" ]; then + # Check if service exists in PM2 + if ! pm2 id "$service_name" > /dev/null 2>&1; then + return 1 # Service not found + fi + elif [ "$provider" = "systemd" ]; then + # Check if service exists in systemd + local systemd_type="--user" + if [ -f "/etc/systemd/system/$service_name.service" ]; then + systemd_type="--system" + fi + + if [ "$systemd_type" = "--system" ]; then + if ! systemctl is-active "$service_name.service" >/dev/null 2>&1 && \ + ! systemctl is-enabled "$service_name.service" >/dev/null 2>&1 && \ + ! systemctl is-failed "$service_name.service" >/dev/null 2>&1; then + return 1 # Service not found + fi + else + if ! systemctl --user is-active "$service_name.service" >/dev/null 2>&1 && \ + ! systemctl --user is-enabled "$service_name.service" >/dev/null 2>&1 && \ + ! systemctl --user is-failed "$service_name.service" >/dev/null 2>&1; then + return 1 # Service not found + fi + fi + fi + + return 0 # Service exists +} + +function sync_registry() { + echo -e "${YELLOW}Syncing service registry with actual services...${NC}" + + local services=$(jq -c '.[]' "$REGISTRY_FILE") + local tmp_file=$(mktemp) + + # Initialize with empty array + echo "[]" > "$tmp_file" + + # Check each service in registry + while read -r service_info; do + if [ -n "$service_info" ]; then + local name=$(echo "$service_info" | jq -r '.name') + local provider=$(echo "$service_info" | jq -r '.provider') + + if validate_service_exists "$name" "$provider"; then + # Service exists, add it to the new registry + jq --argjson service "$service_info" '. += [$service]' "$tmp_file" > "$tmp_file.new" + mv "$tmp_file.new" "$tmp_file" + else + echo -e "${YELLOW}Service '$name' no longer exists. Removing from registry.${NC}" + # Don't add to new registry + fi + fi + done <<< "$services" + + # Replace registry with synced version + mv "$tmp_file" "$REGISTRY_FILE" + echo -e "${GREEN}Registry synchronized.${NC}" +} + +function unregister_service() { + local service_name="$1" + + # Remove from registry + local tmp_file=$(mktemp) + jq --arg name "$service_name" '. | map(select(.name != $name))' "$REGISTRY_FILE" > "$tmp_file" + mv "$tmp_file" "$REGISTRY_FILE" + + # Remove configuration file + rm -f "$CONFIG_DIR/$service_name.conf" + + echo -e "${GREEN}Service $service_name unregistered${NC}" +} + +function get_service_info() { + local service_name="$1" + jq --arg name "$service_name" '.[] | select(.name == $name)' "$REGISTRY_FILE" +} + +# PM2 service management functions +function pm2_create_service() { + local service_name="$1" + local command="$2" + shift 2 + + check_pm2 || return 1 + + # Parse additional PM2 options + local max_memory="" + local max_restarts="" + local additional_args="" + + while [[ $# -gt 0 ]]; do + case "$1" in + --max-memory) + max_memory="$2" + shift 2 + ;; + --max-restarts) + max_restarts="$2" + shift 2 + ;; + *) + additional_args+=" $1" + shift + ;; + esac + done + + # Build PM2 start command + local pm2_cmd="pm2 start '$command$additional_args' --name '$service_name'" + + # Add memory limit if specified + if [ -n "$max_memory" ]; then + pm2_cmd+=" --max-memory-restart $max_memory" + fi + + # Add max restarts if specified + if [ -n "$max_restarts" ]; then + pm2_cmd+=" --max-restarts $max_restarts" + fi + + # Execute command + echo -e "${YELLOW}Creating PM2 service: $service_name${NC}" + + if eval "$pm2_cmd"; then + echo -e "${GREEN}PM2 service '$service_name' created successfully${NC}" + pm2 save + return 0 + else + echo -e "${RED}Failed to create PM2 service '$service_name'${NC}" + return 1 + fi +} + +function pm2_remove_service() { + local service_name="$1" + + check_pm2 || return 1 + + echo -e "${YELLOW}Stopping and removing PM2 service: $service_name${NC}" + + # Stop the service if it's running + if pm2 id "$service_name" > /dev/null 2>&1; then + pm2 stop "$service_name" 2>/dev/null || true + pm2 delete "$service_name" 2>/dev/null + + # Verify the service was removed + if pm2 id "$service_name" > /dev/null 2>&1; then + echo -e "${RED}Failed to remove PM2 service '$service_name'${NC}" + return 1 + fi + + pm2 save + echo -e "${GREEN}PM2 service '$service_name' stopped and removed${NC}" + else + echo -e "${YELLOW}PM2 service '$service_name' not found or already removed${NC}" + fi + + return 0 +} + +function pm2_service_action() { + local action="$1" + local service_name="$2" + + check_pm2 || return 1 + + echo -e "${YELLOW}${action^} PM2 service: $service_name${NC}" + pm2 "$action" "$service_name" +} + +function pm2_service_logs() { + local service_name="$1" + local follow="$2" + + check_pm2 || return 1 + + echo -e "${YELLOW}Showing PM2 logs for: $service_name${NC}" + if [ "$follow" = "true" ]; then + pm2 logs "$service_name" --lines 50 + else + pm2 logs "$service_name" --lines 50 --nostream + fi +} + +# Systemd service management functions +function get_systemd_dir() { + local type="$1" + if [ "$type" = "--system" ]; then + echo "/etc/systemd/system" + else + echo "${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user" + fi +} + +function systemd_create_service() { + local service_name="$1" + local command="$2" + local systemd_type="--user" + shift 2 + + check_systemd || return 1 + + # Parse systemd type + while [[ $# -gt 0 ]]; do + case "$1" in + --system|--user) + systemd_type="$1" + shift + ;; + *) + command+=" $1" + shift + ;; + esac + done + + local systemd_dir=$(get_systemd_dir "$systemd_type") + local service_file="$systemd_dir/$service_name.service" + + # Create systemd directory if it doesn't exist + if [ "$systemd_type" = "--system" ]; then + if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Error: System services require root privileges. Use sudo.${NC}" + return 1 + fi + mkdir -p "$systemd_dir" + else + mkdir -p "$systemd_dir" + fi + + # Create service file + echo -e "${YELLOW}Creating systemd service: $service_name${NC}" + + if [ "$systemd_type" = "--system" ]; then + # System service template (with User directive) + cat > "$service_file" << EOF +[Unit] +Description=AzerothCore $service_name +After=network.target + +[Service] +Type=forking +ExecStart=$command +Restart=always +RestartSec=3 +User=$(whoami) +Group=$(id -gn) +WorkingDirectory=$(realpath "$bin_path") +StandardOutput=journal+console +StandardError=journal+console + +[Install] +WantedBy=multi-user.target +EOF + else + # User service template (no User/Group directives) + cat > "$service_file" << EOF +[Unit] +Description=AzerothCore $service_name +After=network.target + +[Service] +Type=forking +ExecStart=$command +Restart=always +RestartSec=3 +WorkingDirectory=$(realpath "$bin_path") +StandardOutput=journal+console +StandardError=journal+console + +[Install] +WantedBy=default.target +EOF + fi + + if [ "$systemd_type" = "--system" ]; then + sed -i 's/WantedBy=default.target/WantedBy=multi-user.target/' "$service_file" + fi + + # Reload systemd and enable service + if [ "$systemd_type" = "--system" ]; then + systemctl daemon-reload + systemctl enable "$service_name.service" + else + systemctl --user daemon-reload + systemctl --user enable "$service_name.service" + fi + + echo -e "${GREEN}Systemd service '$service_name' created successfully${NC}" + return 0 +} + +function systemd_remove_service() { + local service_name="$1" + local systemd_type="--user" + + check_systemd || return 1 + + # Try to determine if it's a system or user service + if [ -f "/etc/systemd/system/$service_name.service" ]; then + systemd_type="--system" + fi + + local systemd_dir=$(get_systemd_dir "$systemd_type") + local service_file="$systemd_dir/$service_name.service" + + echo -e "${YELLOW}Stopping and removing systemd service: $service_name (${systemd_type#--})${NC}" + + # Check if service file exists + if [ ! -f "$service_file" ]; then + echo -e "${YELLOW}Systemd service file '$service_file' not found or already removed${NC}" + return 0 + fi + + # Stop and disable service + local removal_failed=false + if [ "$systemd_type" = "--system" ]; then + if [ "$EUID" -ne 0 ]; then + echo -e "${RED}Error: System services require root privileges. Use sudo.${NC}" + return 1 + fi + systemctl stop "$service_name.service" 2>/dev/null || true + systemctl disable "$service_name.service" 2>/dev/null || true + systemctl daemon-reload + + # Verify service is no longer active + if systemctl is-active "$service_name.service" >/dev/null 2>&1; then + echo -e "${RED}Warning: Failed to stop system service '$service_name'${NC}" + removal_failed=true + fi + else + systemctl --user stop "$service_name.service" 2>/dev/null || true + systemctl --user disable "$service_name.service" 2>/dev/null || true + systemctl --user daemon-reload + + # Verify service is no longer active + if systemctl --user is-active "$service_name.service" >/dev/null 2>&1; then + echo -e "${RED}Warning: Failed to stop user service '$service_name'${NC}" + removal_failed=true + fi + fi + + # Remove service file + if rm -f "$service_file"; then + echo -e "${GREEN}Systemd service '$service_name' stopped and removed${NC}" + if [ "$removal_failed" = "true" ]; then + echo -e "${YELLOW}Note: Service may still be running but configuration was removed${NC}" + fi + return 0 + else + echo -e "${RED}Failed to remove systemd service file '$service_file'${NC}" + return 1 + fi +} + +function systemd_service_action() { + local action="$1" + local service_name="$2" + local systemd_type="--user" + + check_systemd || return 1 + + # Try to determine if it's a system or user service + if [ -f "/etc/systemd/system/$service_name.service" ]; then + systemd_type="--system" + fi + + echo -e "${YELLOW}${action^} systemd service: $service_name${NC}" + + if [ "$systemd_type" = "--system" ]; then + systemctl "$action" "$service_name.service" + else + systemctl --user "$action" "$service_name.service" + fi +} + +function systemd_service_logs() { + local service_name="$1" + local follow="$2" + local systemd_type="--user" + + check_systemd || return 1 + + # Try to determine if it's a system or user service + if [ -f "/etc/systemd/system/$service_name.service" ]; then + systemd_type="--system" + fi + + echo -e "${YELLOW}Showing systemd logs for: $service_name${NC}" + if [ "$follow" = "true" ]; then + if [ "$systemd_type" = "--system" ]; then + journalctl --unit="$service_name.service" -e -f + else + journalctl --user-unit="$service_name.service" -e -f + fi + else + if [ "$systemd_type" = "--system" ]; then + journalctl --unit="$service_name.service" -e --lines 50 --no-pager + else + journalctl --user-unit="$service_name.service" -e --lines 50 --no-pager + fi + fi +} + +function create_service() { + local service_type="$1" + local service_name="$2" + shift 2 + + # Validate service type + if [[ "$service_type" != "auth" && "$service_type" != "world" ]]; then + echo -e "${RED}Error: Invalid service type. Use 'auth' or 'world'${NC}" + return 1 + fi + + # Check if service already exists + if [ -n "$(get_service_info "$service_name")" ]; then + echo -e "${RED}Error: Service '$service_name' already exists${NC}" + return 1 + fi + + # Default values for run-engine configuration + local provider="auto" + local bin_path="$BINPATH/bin" # get from config or environment + local server_config="" + local session_manager="none" + local gdb_enabled="0" + local systemd_type="--user" + local pm2_opts="" + local auto_start="true" + + # Parse options + while [[ $# -gt 0 ]]; do + case "$1" in + --provider) + provider="$2" + shift 2 + ;; + --bin-path) + bin_path="$2" + shift 2 + ;; + --server-config) + server_config="$2" + shift 2 + ;; + --session-manager) + session_manager="$2" + shift 2 + ;; + --gdb-enabled) + gdb_enabled="$2" + shift 2 + ;; + --system) + systemd_type="--system" + shift + ;; + --user) + systemd_type="--user" + shift + ;; + --max-memory|--max-restarts) + pm2_opts="$pm2_opts $1 $2" + shift 2 + ;; + --no-start) + auto_start="false" + shift + ;; + *) + echo -e "${RED}Error: Unknown option: $1${NC}" + return 1 + ;; + esac + done + + # Auto-detect provider if set to auto + if [ "$provider" = "auto" ]; then + if ! provider=$(auto_detect_provider); then + return 1 + fi + echo -e "${BLUE}Auto-detected provider: $provider${NC}" + fi + + # Validate provider + if [[ "$provider" != "pm2" && "$provider" != "systemd" ]]; then + echo -e "${RED}Error: Invalid provider. Use 'pm2', 'systemd', or 'auto'${NC}" + return 1 + fi + + # Determine server binary based on service type + local server_bin="${service_type}server" + local server_binary_path=$(realpath "$bin_path/$server_bin") + + # Check if binary exists + if [ ! -f "$server_binary_path" ]; then + echo -e "${RED}Error: Server binary not found: $server_binary_path${NC}" + return 1 + fi + + # Create run-engine configuration file for this service + local run_engine_config="$CONFIG_DIR/$service_name-run-engine.conf" + cat > "$run_engine_config" << EOF +# run-engine configuration for service: $service_name +# This file contains run-engine specific settings + +# Enable/disable GDB execution +export GDB_ENABLED=$gdb_enabled + +# Session manager (none|auto|tmux|screen) +export SESSION_MANAGER="$session_manager" + +# Session name for tmux/screen (optional) +export SESSION_NAME="${service_name}" + +# Binary directory path +export BINPATH="$bin_path" + +# Server binary name +export SERVERBIN="$server_bin" + +# Server configuration file path +export CONFIG="$server_config" + +# Show console output for easier debugging +export WITH_CONSOLE=1 +EOF + + # Create service configuration file for our registry + cat > "$CONFIG_DIR/$service_name.conf" << EOF +# AzerothCore service configuration for $service_name +# Created: $(date) +# Provider: $provider +# Service Type: $service_type + +# run-engine configuration file +RUN_ENGINE_CONFIG_FILE="$run_engine_config" + +# Provider-specific options +SYSTEMD_TYPE="$systemd_type" +PM2_OPTS="$pm2_opts" +EOF + + # Build run-engine command + local run_engine_cmd="$SCRIPT_DIR/run-engine start $server_binary_path --config $run_engine_config" + + # Create the actual service + local service_creation_success=false + if [ "$provider" = "pm2" ]; then + if [ -n "$pm2_opts" ]; then + if pm2_create_service "$service_name" "$run_engine_cmd" $pm2_opts; then + service_creation_success=true + fi + else + if pm2_create_service "$service_name" "$run_engine_cmd"; then + service_creation_success=true + fi + fi + + elif [ "$provider" = "systemd" ]; then + if systemd_create_service "$service_name" "$run_engine_cmd" "$systemd_type"; then + service_creation_success=true + fi + fi + + # Check if service creation was successful + if [ "$service_creation_success" = "true" ]; then + # Register the service + register_service "$service_name" "$provider" "$service_type" + echo -e "${GREEN}Service '$service_name' created successfully${NC}" + echo -e "${BLUE}Run-engine config: $run_engine_config${NC}" + + # Auto-start the service unless --no-start was specified + if [ "$auto_start" = "true" ]; then + echo -e "${YELLOW}Starting service '$service_name'...${NC}" + if service_action "start" "$service_name"; then + echo -e "${GREEN}Service '$service_name' started successfully${NC}" + else + echo -e "${YELLOW}Warning: Service '$service_name' was created but failed to start${NC}" + echo -e "${BLUE}You can start it manually with: $0 start $service_name${NC}" + fi + else + echo -e "${BLUE}Service created but not started (--no-start specified)${NC}" + echo -e "${BLUE}Start it manually with: $0 start $service_name${NC}" + fi + else + # Remove configuration files if service creation failed + rm -f "$CONFIG_DIR/$service_name.conf" + rm -f "$run_engine_config" + echo -e "${RED}Failed to create service '$service_name'${NC}" + return 1 + fi +} + +function update_service() { + local service_name="$1" + shift + + # Check if service exists + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" + return 1 + fi + + # Extract service information + local provider=$(echo "$service_info" | jq -r '.provider') + local service_type=$(echo "$service_info" | jq -r '.type') + local config_file=$(echo "$service_info" | jq -r '.config') + + # Load current configuration + source "$config_file" + + # Load current run-engine configuration + if [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then + source "$RUN_ENGINE_CONFIG_FILE" + fi + + # Parse options to update + local config_updated=false + while [[ $# -gt 0 ]]; do + case "$1" in + --bin-path) + export BINPATH="$2" + config_updated=true + shift 2 + ;; + --server-config) + export SERVER_CONFIG="$2" + config_updated=true + shift 2 + ;; + --session-manager) + export SESSION_MANAGER="$2" + config_updated=true + shift 2 + ;; + --gdb-enabled) + export GDB_ENABLED="$2" + config_updated=true + shift 2 + ;; + --system) + SYSTEMD_TYPE="--system" + shift + ;; + --user) + SYSTEMD_TYPE="--user" + shift + ;; + --max-memory|--max-restarts) + PM2_OPTS="$PM2_OPTS $1 $2" + shift 2 + ;; + *) + echo -e "${RED}Error: Unknown option: $1${NC}" + return 1 + ;; + esac + done + + if [ "$config_updated" = "true" ]; then + # Update run-engine configuration file + cat > "$RUN_ENGINE_CONFIG_FILE" << EOF +# run-engine configuration for service: $service_name +# Updated: $(date) + +# Enable/disable GDB execution +export GDB_ENABLED=${GDB_ENABLED:-0} + +# Session manager (none|auto|tmux|screen) +export SESSION_MANAGER="${SESSION_MANAGER:-none}" + +# Session name for tmux/screen +export SESSION_NAME="${service_name}" + +# Binary directory path +export BINPATH="${BINPATH}" + +# Server binary name +export SERVERBIN="${SERVERBIN}" + +# Server configuration file path +export CONFIG="${SERVER_CONFIG}" +EOF + + echo -e "${GREEN}Run-engine configuration updated: $RUN_ENGINE_CONFIG_FILE${NC}" + echo -e "${YELLOW}Note: Restart the service to apply changes${NC}" + else + echo -e "${YELLOW}No run-engine configuration changes made${NC}" + fi + + # Update service configuration + cat > "$config_file" << EOF +# AzerothCore service configuration for $service_name +# Updated: $(date) +# Provider: $provider +# Service Type: $service_type + +# run-engine configuration file +RUN_ENGINE_CONFIG_FILE="$RUN_ENGINE_CONFIG_FILE" + +# Provider-specific options +SYSTEMD_TYPE="$SYSTEMD_TYPE" +PM2_OPTS="$PM2_OPTS" +EOF +} + +function delete_service() { + local service_name="$1" + + # Check if service exists + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" + return 1 + fi + + # Extract provider and config + local provider=$(echo "$service_info" | jq -r '.provider') + local config_file=$(echo "$service_info" | jq -r '.config') + + # Load configuration to get run-engine config file + if [ -f "$config_file" ]; then + source "$config_file" + fi + + echo -e "${YELLOW}Deleting service '$service_name' (provider: $provider)...${NC}" + + # Stop and remove the service + local removal_success=false + if [ "$provider" = "pm2" ]; then + if pm2_remove_service "$service_name"; then + removal_success=true + fi + elif [ "$provider" = "systemd" ]; then + if systemd_remove_service "$service_name"; then + removal_success=true + fi + fi + + if [ "$removal_success" = "true" ]; then + # Remove run-engine configuration file + if [ -n "$RUN_ENGINE_CONFIG_FILE" ] && [ -f "$RUN_ENGINE_CONFIG_FILE" ]; then + rm -f "$RUN_ENGINE_CONFIG_FILE" + echo -e "${GREEN}Removed run-engine config: $RUN_ENGINE_CONFIG_FILE${NC}" + fi + + # Unregister service + unregister_service "$service_name" + echo -e "${GREEN}Service '$service_name' deleted successfully${NC}" + else + echo -e "${RED}Failed to remove service '$service_name' from $provider${NC}" + return 1 + fi +} + +function list_services() { + local provider_filter="$1" + + # Sync registry first + sync_registry + + echo -e "${BLUE}AzerothCore Services${NC}" + echo "=====================" + + if [ "$(jq 'length' "$REGISTRY_FILE")" = "0" ]; then + echo "No services registered" + return + fi + + # Show PM2 services + if [ -z "$provider_filter" ] || [ "$provider_filter" = "pm2" ]; then + local pm2_services=$(jq -r '.[] | select(.provider == "pm2") | .name' "$REGISTRY_FILE" 2>/dev/null) + if [ -n "$pm2_services" ] && command -v pm2 >/dev/null 2>&1; then + echo -e "\n${YELLOW}PM2 Services:${NC}" + pm2 list + fi + fi + + # Show systemd services + if [ -z "$provider_filter" ] || [ "$provider_filter" = "systemd" ]; then + local systemd_services=$(jq -r '.[] | select(.provider == "systemd") | .name' "$REGISTRY_FILE" 2>/dev/null) + if [ -n "$systemd_services" ] && command -v systemctl >/dev/null 2>&1; then + echo -e "\n${YELLOW}Systemd User Services:${NC}" + while read -r service_name; do + if [ -n "$service_name" ]; then + systemctl --user status "$service_name.service" --no-pager -l || true + echo "" + fi + done <<< "$systemd_services" + + # Also check for system services + local system_services="" + while read -r service_name; do + if [ -n "$service_name" ] && [ -f "/etc/systemd/system/$service_name.service" ]; then + system_services+="$service_name " + fi + done <<< "$systemd_services" + + if [ -n "$system_services" ]; then + echo -e "${YELLOW}Systemd System Services:${NC}" + for service_name in $system_services; do + systemctl status "$service_name.service" --no-pager -l || true + echo "" + done + fi + fi + fi +} + +function service_action() { + local action="$1" + local service_name="$2" + + # Check if service exists + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" + return 1 + fi + + # Extract provider + local provider=$(echo "$service_info" | jq -r '.provider') + + # Execute action + if [ "$provider" = "pm2" ]; then + pm2_service_action "$action" "$service_name" + elif [ "$provider" = "systemd" ]; then + systemd_service_action "$action" "$service_name" + fi +} + +function service_logs() { + local service_name="$1" + local follow="${2:-false}" + + # Check if service exists + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" + return 1 + fi + + # Extract provider + local provider=$(echo "$service_info" | jq -r '.provider') + + # Show logs + if [ "$provider" = "pm2" ]; then + pm2_service_logs "$service_name" "$follow" + elif [ "$provider" = "systemd" ]; then + systemd_service_logs "$service_name" "$follow" + fi +} + +function edit_config() { + local service_name="$1" + + # Check if service exists + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" + return 1 + fi + + # Get configuration file path + local config_file=$(echo "$service_info" | jq -r '.config') + + # Load configuration to get run-engine config file + source "$config_file" + + # Open run-engine configuration file in editor + echo -e "${YELLOW}Editing run-engine configuration for: $service_name${NC}" + echo -e "${BLUE}File: $RUN_ENGINE_CONFIG_FILE${NC}" + ${EDITOR:-nano} "$RUN_ENGINE_CONFIG_FILE" + + echo -e "${GREEN}Configuration updated. Run '$0 restart $service_name' to apply changes.${NC}" +} + +function attach_to_service() { + local service_name="$1" + + # Check if service exists + local service_info=$(get_service_info "$service_name") + if [ -z "$service_info" ]; then + echo -e "${RED}Error: Service '$service_name' not found${NC}" + return 1 + fi + + # Extract provider + local provider=$(echo "$service_info" | jq -r '.provider') + local config_file=$(echo "$service_info" | jq -r '.config') + + # Load configuration to get run-engine config file + if [ ! -f "$config_file" ]; then + echo -e "${RED}Error: Service configuration file not found: $config_file${NC}" + return 1 + fi + + source "$config_file" + + # Load run-engine configuration + if [ ! -f "$RUN_ENGINE_CONFIG_FILE" ]; then + echo -e "${RED}Error: Run-engine configuration file not found: $RUN_ENGINE_CONFIG_FILE${NC}" + return 1 + fi + + source "$RUN_ENGINE_CONFIG_FILE" + + # Auto-detect session manager and attach accordingly + case "$SESSION_MANAGER" in + "tmux") + attach_tmux_session "$service_name" "$provider" + ;; + "screen") + attach_screen_session "$service_name" "$provider" + ;; + "none"|"auto"|*) + # No session manager - launch interactive shell directly + attach_interactive_shell "$service_name" "$provider" + ;; + esac +} + +function attach_interactive_shell() { + local service_name="$1" + local provider="$2" + + # Get service info again to access configuration + local service_info=$(get_service_info "$service_name") + local config_file=$(echo "$service_info" | jq -r '.config') + + source "$config_file" + source "$RUN_ENGINE_CONFIG_FILE" + + echo -e "${RED}Error: Cannot attach to service '$service_name'${NC} [for now]" + echo -e "${YELLOW}Interactive attachment requires a session manager (tmux or screen).${NC}" + echo "" + echo -e "${BLUE}Current session manager: $SESSION_MANAGER${NC}" + echo "" + echo -e "${YELLOW}To enable interactive attachment:${NC}" + echo " 1. Update the service to use tmux or screen:" + echo " $0 update $service_name --session-manager tmux" + echo " 2. Restart the service:" + echo " $0 restart $service_name" + echo " 3. Then try attach again:" + echo " $0 attach $service_name" + echo "" + echo -e "${BLUE}Alternative: Use 'logs --follow' to monitor the service:${NC}" + echo " $0 logs $service_name --follow" + + return 1 +} + +function attach_tmux_session() { + local service_name="$1" + local provider="$2" + + # Check if tmux is available + if ! command -v tmux >/dev/null 2>&1; then + echo -e "${RED}Error: tmux is not installed${NC}" + echo -e "${BLUE}Starting interactive session without tmux...${NC}" + attach_interactive_shell "$service_name" "$provider" + return + fi + + # Try to attach to tmux session + local tmux_session="$service_name" + echo -e "${YELLOW}Attempting to attach to tmux session: $tmux_session${NC}" + + if tmux has-session -t "$tmux_session" 2>/dev/null; then + echo -e "${GREEN}Attaching to tmux session...${NC}" + tmux attach-session -t "$tmux_session" + else + echo -e "${RED}Error: tmux session '$tmux_session' not found${NC}" + echo -e "${YELLOW}Available tmux sessions:${NC}" + tmux list-sessions 2>/dev/null || echo "No active tmux sessions" + echo -e "${BLUE}Starting new interactive session instead...${NC}" + attach_interactive_shell "$service_name" "$provider" + fi +} + +function attach_screen_session() { + local service_name="$1" + local provider="$2" + + # Check if screen is available + if ! command -v screen >/dev/null 2>&1; then + echo -e "${RED}Error: screen is not installed${NC}" + echo -e "${BLUE}Starting interactive session without screen...${NC}" + attach_interactive_shell "$service_name" "$provider" + return + fi + + # Try to attach to screen session + local screen_session="$service_name" + echo -e "${YELLOW}Attempting to attach to screen session: $screen_session${NC}" + + if screen -list | grep -q "$screen_session"; then + echo -e "${GREEN}Attaching to screen session...${NC}" + screen -r "$screen_session" + else + echo -e "${RED}Error: screen session '$screen_session' not found${NC}" + echo -e "${YELLOW}Available screen sessions:${NC}" + screen -list 2>/dev/null || echo "No active screen sessions" + echo -e "${BLUE}Starting new interactive session instead...${NC}" + attach_interactive_shell "$service_name" "$provider" + fi +} + + + + +# Main execution +check_dependencies + +# Main command processing +case "${1:-help}" in + create) + if [ $# -lt 3 ]; then + echo -e "${RED}Error: Not enough arguments for create command${NC}" + print_help + exit 1 + fi + create_service "$2" "$3" "${@:4}" + ;; + update) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for update command${NC}" + print_help + exit 1 + fi + update_service "$2" "${@:3}" + ;; + delete) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for delete command${NC}" + print_help + exit 1 + fi + delete_service "$2" + ;; + list) + list_services "$2" + ;; + start|stop|restart|status) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for $1 command${NC}" + print_help + exit 1 + fi + service_action "$1" "$2" + ;; + logs) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for logs command${NC}" + print_help + exit 1 + fi + if [ "$3" = "--follow" ]; then + service_logs "$2" "true" + else + service_logs "$2" "false" + fi + ;; + edit-config) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for edit-config command${NC}" + print_help + exit 1 + fi + edit_config "$2" + ;; + attach) + if [ $# -lt 2 ]; then + echo -e "${RED}Error: Service name required for attach command${NC}" + print_help + exit 1 + fi + attach_to_service "$2" + ;; + help|--help|-h) + print_help + ;; + *) + echo -e "${RED}Error: Unknown command: $1${NC}" + print_help + exit 1 + ;; +esac diff --git a/apps/startup-scripts/src/simple-restarter b/apps/startup-scripts/src/simple-restarter new file mode 100755 index 000000000..c5540e4ab --- /dev/null +++ b/apps/startup-scripts/src/simple-restarter @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +# AzerothCore Simple Restarter +# This script is a wrapper around the starter script that provides restart functionality +# and maintains compatibility with the acore dashboard +# +# Usage: simple-restarter [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path] +# +# Parameters (same as starter): +# $1 - Binary to execute (required) +# $2 - GDB configuration file (optional) +# $3 - Configuration file path (optional) +# $4 - System log file (optional) +# $5 - System error file (optional) +# $6 - GDB enabled flag (0/1, optional) +# $7 - Crashes directory path (optional) + +# Get script directory +CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Parameters (same as starter) +BINPATH="$1" +BINFILE="$2" +GDB_FILE="$3" +CONFIG="$4" +SYSLOG="$5" +SYSERR="$6" +GDB_ENABLED="${7:-0}" +CRASHES_PATH="$8" + +BINARY="$BINPATH/$BINFILE" + +# Default values (same as starter) +DEFAULT_CRASHES_PATH="./crashes" +DEFAULT_GDB_FILE="$CURRENT_PATH/gdb.conf" + +# Set defaults if not provided +CRASHES_PATH="${CRASHES_PATH:-$DEFAULT_CRASHES_PATH}" +GDB_FILE="${GDB_FILE:-$DEFAULT_GDB_FILE}" + +# Counters for crash detection +_instant_crash_count=0 +_restart_count=0 + +# Check if starter script exists +STARTER_SCRIPT="$CURRENT_PATH/starter" +if [ ! -f "$STARTER_SCRIPT" ]; then + echo "Error: starter script not found at $STARTER_SCRIPT" + exit 1 +fi + +# Main restart loop +while true; do + STARTING_TIME=$(date +%s) + + # Use starter script to launch the binary with all parameters + "$STARTER_SCRIPT" "$BINPATH" "$BINFILE" "$GDB_FILE" "$CONFIG" "$SYSLOG" "$SYSERR" "$GDB_ENABLED" "$CRASHES_PATH" + + _exit_code=$? + + echo "$(basename "$BINARY") terminated with exit code: $_exit_code" + + # Calculate runtime + ENDING_TIME=$(date +%s) + DIFFERENCE=$((ENDING_TIME - STARTING_TIME)) + + ((_restart_count++)) + echo "$(basename "$BINARY") terminated after $DIFFERENCE seconds, restart count: $_restart_count" + + # Crash loop detection + if [ $DIFFERENCE -lt 10 ]; then + # Increment instant crash count if runtime is lower than 10 seconds + ((_instant_crash_count++)) + echo "Warning: Quick restart detected ($DIFFERENCE seconds) - instant crash count: $_instant_crash_count" + else + # Reset count on successful longer run + _instant_crash_count=0 + fi + + # Prevent infinite crash loops + if [ $_instant_crash_count -gt 5 ]; then + echo "Error: $(basename "$BINARY") restarter exited. Infinite crash loop prevented (6 crashes in under 10 seconds each)" + echo "Please check your system configuration and logs" + exit 1 + fi + + echo "$(basename "$BINARY") will restart in 3 seconds..." + sleep 3 +done diff --git a/apps/startup-scripts/src/starter b/apps/startup-scripts/src/starter new file mode 100755 index 000000000..967146efa --- /dev/null +++ b/apps/startup-scripts/src/starter @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +# AzerothCore Starter Script +# This script handles the execution of AzerothCore binaries with optional GDB support +# +# Usage: starter [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path] +# +# Parameters: +# $1 - Binary to execute (required) +# $2 - GDB configuration file (optional) +# $3 - Configuration file path (optional) +# $4 - System log file (optional) +# $5 - System error file (optional) +# $6 - GDB enabled flag (0/1, optional) +# $7 - Crashes directory path (optional) + +BINPATH="$1" +BINFILE="$2" +GDB_FILE="$3" +CONFIG="$4" +SYSLOG="$5" +SYSERR="$6" +GDB_ENABLED="${7:-0}" +CRASHES_PATH="$8" + +BINARY=$(realpath "$BINPATH/$BINFILE") + +# Default values +CURRENT_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +DEFAULT_CRASHES_PATH="$CURRENT_PATH/logs/crashes" +DEFAULT_GDB_FILE="$CURRENT_PATH/gdb.conf" + +# Set defaults if not provided +CONFIG="${CONFIG:-""}" +CRASHES_PATH="${CRASHES_PATH:-$DEFAULT_CRASHES_PATH}" +GDB_FILE="${GDB_FILE:-$DEFAULT_GDB_FILE}" + +# Validate binary +if [ -z "$BINARY" ]; then + echo "Error: Binary parameter is required" + echo "Usage: $0 [gdb_file] [config] [syslog] [syserr] [gdb_enabled] [crashes_path]" + exit 1 +fi + +if [ ! -f "$BINARY" ]; then + echo "Error: Binary '$BINARY' not found" + exit 1 +fi + +# Create crashes directory if it doesn't exist +mkdir -p "$CRASHES_PATH" + +cd $BINPATH || { + echo "Error: Could not change to binary path '$BINPATH'" + exit 1 +} + +EXECPATH=$(realpath "$BINFILE") + +if [ "$GDB_ENABLED" -eq 1 ]; then + echo "Starting $EXECPATH with GDB enabled" + + # Generate GDB configuration on the fly + TIMESTAMP=$(date +%Y-%m-%d-%H-%M-%S) + GDB_TEMP_FILE="$CRASHES_PATH/gdb-$TIMESTAMP.conf" + GDB_OUTPUT_FILE="$CRASHES_PATH/gdb-$TIMESTAMP.txt" + + # Create GDB configuration + cat > "$GDB_TEMP_FILE" << EOF +set logging file $GDB_OUTPUT_FILE +set logging enabled on +set debug timestamp +EOF + + # Add run command with config if specified + if [ -n "$CONFIG" ]; then + echo "run -c $CONFIG" >> "$GDB_TEMP_FILE" + else + echo "run" >> "$GDB_TEMP_FILE" + fi + + cat >> "$GDB_TEMP_FILE" << EOF +bt +bt full +info thread +thread apply all backtrace full +EOF + + # Create log files if specified + if [ -n "$SYSLOG" ]; then + [ ! -f "$SYSLOG" ] && touch "$SYSLOG" + fi + if [ -n "$SYSERR" ]; then + [ ! -f "$SYSERR" ] && touch "$SYSERR" + fi + + # Execute with GDB + if [ "${WITH_CONSOLE:-0}" -eq 0 ] && [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then + gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH" >> "$SYSLOG" 2>> "$SYSERR" + else + echo "> Console enabled" + if [ -n "$SYSLOG" ] && [ -n "$SYSERR" ]; then + gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH" > >(tee "$SYSLOG") 2> >(tee "$SYSERR" >&2) + else + gdb -x "$GDB_TEMP_FILE" --batch "$EXECPATH" + fi + fi + + # Cleanup temporary GDB file + rm -f "$GDB_TEMP_FILE" +else + if [ -n "$CONFIG" ]; then + script -q -e -c "$EXECPATH -c \"$CONFIG\"" + else + script -q -e -c "$EXECPATH" + fi +fi diff --git a/apps/startup-scripts/starter b/apps/startup-scripts/starter deleted file mode 100644 index 47bbedce7..000000000 --- a/apps/startup-scripts/starter +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -GDB_FILE="$2" -CONFIG="$3" -SYSLOG="$4" -SYSERR="$5" -GDB_ENABLED="$6" -CRASHES_PATH="$7" - -if [ $GDB_ENABLED -eq 1 ]; then - echo "set logging file "$CRASHES_PATH"/gdb-$(date +%Y-%m-%d-%H-%M-%S).txt" > "$GDB_FILE" - echo "set logging enabled on" >> "$GDB_FILE" - echo "set debug timestamp" >> "$GDB_FILE" - echo "run -c $3" >> "$GDB_FILE" - echo "bt" >> "$GDB_FILE" - echo "bt full" >> "$GDB_FILE" - echo "info thread" >> "$GDB_FILE" - echo "thread apply all backtrace full" >> "$GDB_FILE" - - [ ! -f "$SYSLOG" ] && touch "$SYSLOG" - [ ! -f "$SYSERR" ] && touch "$SYSERR" - - if [ $WITH_CONSOLE -eq 0 ]; then - gdb -x $GDB_FILE --batch $1 >> "$SYSLOG" 2>> "$SYSERR" - else - echo "> Console enabled" - gdb -x $GDB_FILE --batch $1 > >(tee ${SYSLOG}) 2> >(tee ${SYSERR} >&2) - fi - -elif [ $GDB_ENABLED -eq 0 ]; then - "./$1" -c "$CONFIG" -fi diff --git a/apps/startup-scripts/test/bats.conf b/apps/startup-scripts/test/bats.conf new file mode 100644 index 000000000..8034254e7 --- /dev/null +++ b/apps/startup-scripts/test/bats.conf @@ -0,0 +1,14 @@ +# BATS Test Configuration + +# Set test timeout (in seconds) +export BATS_TEST_TIMEOUT=30 + +# Enable verbose output for debugging +export BATS_VERBOSE_RUN=1 + +# Test output format +export BATS_FORMATTER=pretty + +# Enable colored output +export BATS_NO_PARALLELIZE_ACROSS_FILES=1 +export BATS_NO_PARALLELIZE_WITHIN_FILE=1 diff --git a/apps/startup-scripts/test/test_startup_scripts.bats b/apps/startup-scripts/test/test_startup_scripts.bats new file mode 100644 index 000000000..aaf95d1e1 --- /dev/null +++ b/apps/startup-scripts/test/test_startup_scripts.bats @@ -0,0 +1,147 @@ +#!/usr/bin/env bats + +# AzerothCore Startup Scripts Test Suite +# This script tests the basic functionality of the startup scripts using the unified test framework + +# Load the AzerothCore test framework +load '../../test-framework/bats_libs/acore-support' +load '../../test-framework/bats_libs/acore-assert' + +# Setup that runs before each test +setup() { + startup_scripts_setup + export SCRIPT_DIR="$(cd "$(dirname "$BATS_TEST_FILENAME")/../src" && pwd)" +} + +# Cleanup that runs after each test +teardown() { + acore_test_teardown +} + +# ===== STARTER SCRIPT TESTS ===== + +@test "starter: should fail with missing parameters" { + run timeout 3s "$SCRIPT_DIR/starter" '' '' + [ "$status" -ne 0 ] + [[ "$output" =~ "Error: Binary '/' not found" ]] +} + +@test "starter: should start with valid binary" { + cd "$TEST_DIR" + run timeout 5s "$SCRIPT_DIR/starter" "$TEST_DIR/bin" "test-server" "" "$TEST_DIR/test-server.conf" "" "" 0 + debug_on_failure + # The starter might have issues with the script command, so we check for specific behavior + # Either it should succeed or show a specific error we can work with + [[ "$output" =~ "Test server starting" ]] || [[ "$output" =~ "script:" ]] || [[ "$status" -eq 124 ]] +} + +@test "starter: should validate binary path exists" { + run "$SCRIPT_DIR/starter" "/nonexistent/path" "test-server" + [ "$status" -ne 0 ] + [[ "$output" =~ "Binary parameter is required" ]] || [[ "$output" =~ "No such file or directory" ]] +} + +# ===== SIMPLE RESTARTER TESTS ===== + +@test "simple-restarter: should fail with missing parameters" { + run timeout 3s "$SCRIPT_DIR/simple-restarter" '' '' + [ "$status" -ne 0 ] + [[ "$output" =~ "Error: Binary '/' not found" ]] +} + +@test "simple-restarter: should fail with missing binary" { + run timeout 3s "$SCRIPT_DIR/simple-restarter" "$TEST_DIR/bin" 'nonexistent' + [ "$status" -ne 0 ] + [[ "$output" =~ "not found" ]] || [[ "$output" =~ "terminated with exit code" ]] +} + +@test "simple-restarter: should detect starter script" { + # Test that it finds the starter script + run timeout 1s "$SCRIPT_DIR/simple-restarter" '' '' + # Should not fail because starter script is missing + [[ ! "$output" =~ "starter script not found" ]] +} + +# ===== RUN-ENGINE TESTS ===== + +@test "run-engine: should show help" { + run "$SCRIPT_DIR/run-engine" help + [ "$status" -eq 0 ] + [[ "$output" =~ "AzerothCore Run Engine" ]] +} + +@test "run-engine: should validate parameters for start command" { + run "$SCRIPT_DIR/run-engine" start + [ "$status" -ne 0 ] + [[ "$output" =~ "Missing required arguments" ]] +} + +@test "run-engine: should detect binary with full path" { + run timeout 5s "$SCRIPT_DIR/run-engine" start "$TEST_DIR/bin/test-server" --server-config "$TEST_DIR/test-server.conf" + debug_on_failure + [[ "$output" =~ "Starting server: test-server" ]] || [[ "$status" -eq 124 ]] +} + +@test "run-engine: should detect binary in current directory" { + cd "$TEST_DIR/bin" + run timeout 5s "$SCRIPT_DIR/run-engine" start test-server --server-config "$TEST_DIR/test-server.conf" + debug_on_failure + [[ "$output" =~ "Binary found in current directory" ]] || [[ "$output" =~ "Starting server: test-server" ]] || [[ "$status" -eq 124 ]] +} + +@test "run-engine: should support restart mode" { + run timeout 5s "$SCRIPT_DIR/run-engine" restart "$TEST_DIR/bin/test-server" --server-config "$TEST_DIR/test-server.conf" + debug_on_failure + [[ "$output" =~ "Starting server: test-server" ]] || [[ "$status" -eq 124 ]] +} + +# ===== SERVICE MANAGER TESTS ===== + +@test "service-manager: should show help" { + run "$SCRIPT_DIR/service-manager.sh" help + [ "$status" -eq 0 ] + [[ "$output" =~ "AzerothCore Service Setup" ]] +} + +@test "service-manager: should validate create command parameters" { + run "$SCRIPT_DIR/service-manager.sh" create + [ "$status" -ne 0 ] + [[ "$output" =~ "Missing required arguments" ]] || [[ "$output" =~ "Error:" ]] +} + +# ===== EXAMPLE SCRIPTS TESTS ===== + +@test "examples: restarter-world should show configuration error" { + run "$SCRIPT_DIR/examples/restarter-world.sh" + [[ "$output" =~ "Configuration file not found" ]] +} + +@test "examples: starter-auth should show configuration error" { + run "$SCRIPT_DIR/examples/starter-auth.sh" + [[ "$output" =~ "Configuration file not found" ]] +} + +@test "examples: restarter-auth should show configuration error" { + run "$SCRIPT_DIR/examples/restarter-auth.sh" + [[ "$output" =~ "Configuration file not found" ]] +} + +@test "examples: restarter-world should show alternative suggestions" { + run "$SCRIPT_DIR/examples/restarter-world.sh" + [[ "$output" =~ "Alternative: Start with binary path directly" ]] +} + +# ===== INTEGRATION TESTS ===== + +@test "integration: starter and simple-restarter work together" { + # Test that simple-restarter can use starter + run timeout 5s "$SCRIPT_DIR/simple-restarter" "$TEST_DIR/bin" "test-server" + # Should start and then restart at least once + [[ "$output" =~ "terminated with exit code" ]] || [[ "$status" -eq 124 ]] +} + +@test "integration: run-engine can handle missing config gracefully" { + run timeout 3s "$SCRIPT_DIR/run-engine" start "$TEST_DIR/bin/test-server" + # Should either work or give a meaningful error + [[ "$status" -eq 124 ]] || [[ "$status" -eq 0 ]] || [[ "$output" =~ "config" ]] +} diff --git a/apps/test-framework/README.md b/apps/test-framework/README.md new file mode 100644 index 000000000..dda523706 --- /dev/null +++ b/apps/test-framework/README.md @@ -0,0 +1,335 @@ +# AzerothCore Test Framework + +This is the centralized test framework for all AzerothCore bash scripts. It provides a unified way to write, run, and manage tests across all modules. + +## Structure + +``` +apps/test-framework/ +├── run-tests.sh # Universal test runner (single entry point) +├── README.md # This documentation +├── bats_libs/ # Custom BATS libraries +│ ├── acore-support.bash # Test setup and helpers +│ └── acore-assert.bash # Custom assertions +└── helpers/ # Test utilities + └── test_common.sh # Common test functions and setup +``` + +## Quick Start + +### From any module directory: +```bash +# Run tests for current module +../test-framework/run-tests.sh --dir . + +``` + +### From test-framework directory: +```bash +# Run all tests in all modules +./run-tests.sh --all + +# Run tests for specific module +./run-tests.sh startup-scripts + +# List available modules +./run-tests.sh --list + +# Run tests with debug info +./run-tests.sh --all --debug +``` + +### From project root: +```bash +# Run all tests +apps/test-framework/run-tests.sh --all + +# Run specific module +apps/test-framework/run-tests.sh startup-scripts + +# Run with verbose output +apps/test-framework/run-tests.sh startup-scripts --verbose +``` + +## Usage + +### Basic Commands + +```bash +# Run all tests +./run-tests.sh --all + +# Run tests for specific module +./run-tests.sh startup-scripts + +# Run tests matching pattern +./run-tests.sh --filter starter + +# Run tests in specific directory +./run-tests.sh --dir apps/docker + +# Show available modules +./run-tests.sh --list + +# Show test count +./run-tests.sh --count +``` + +### Output Formats + +```bash +# Pretty output (default) +./run-tests.sh --pretty + +# TAP output for CI/CD +./run-tests.sh --tap + +# Verbose output with debug info +./run-tests.sh --verbose --debug +``` + +## Writing Tests + +### Basic Test Structure + +```bash +#!/usr/bin/env bats + +# Load the AzerothCore test framework +load '../../test-framework/bats_libs/acore-support' +load '../../test-framework/bats_libs/acore-assert' + +setup() { + acore_test_setup # Standard setup + # or + startup_scripts_setup # For startup scripts + # or + compiler_setup # For compiler tests + # or + docker_setup # For docker tests +} + +teardown() { + acore_test_teardown +} + +@test "my test description" { + run my_command + assert_success + assert_output "expected output" +} +``` + +### Available Setup Functions + +- `acore_test_setup` - Basic setup for all tests +- `startup_scripts_setup` - Setup for startup script tests +- `compiler_setup` - Setup for compiler tests +- `docker_setup` - Setup for docker tests +- `extractor_setup` - Setup for extractor tests + +### Custom Assertions + +```bash +# Assert binary exists and is executable +assert_binary_exists "$TEST_DIR/bin/authserver" + +# Assert server started correctly +assert_acore_server_started "$output" "authserver" + +# Assert config was loaded +assert_config_loaded "$output" "authserver.conf" + +# Assert build success +assert_build_success "$output" + +# Assert timeout occurred (for long-running processes) +assert_timeout "$status" + +# Assert log contains content +assert_log_contains "$log_file" "Server started" +``` + +### Test Environment Variables + +When using the framework, these variables are automatically set: + +- `$TEST_DIR` - Temporary test directory +- `$AC_TEST_ROOT` - Project root directory +- `$AC_TEST_APPS` - Apps directory +- `$BUILDPATH` - Build directory path +- `$SRCPATH` - Source directory path +- `$BINPATH` - Binary directory path +- `$LOGS_PATH` - Logs directory path + +### Helper Functions + +```bash +# Create test binary +create_test_binary "authserver" 0 2 "Server started" + +# Create test config +create_test_config "authserver.conf" "Database.Info = \"127.0.0.1;3306;root;pass;db\"" + +# Create AzerothCore specific binaries and configs +create_acore_binaries +create_acore_configs + +# Run command with timeout +run_with_timeout 5s my_command + +# Wait for condition +wait_for_condition "test -f $TEST_DIR/ready" 10 1 + +# Debug test failure +debug_on_failure +``` + +## Module Integration + +### Adding Tests to a New Module + +1. Create a `test/` directory in your module: + ```bash + mkdir apps/my-module/test + ``` + +2. Create test files (ending in `.bats`): + ```bash + touch apps/my-module/test/test_my_feature.bats + ``` + +3. Write your tests using the framework (see examples above) + +### Running Tests + +From your module directory: +```bash +../test-framework/run-tests.sh --dir . +``` + +From the test framework: +```bash +./run-tests.sh my-module +``` + +From project root: +```bash +apps/test-framework/run-tests.sh my-module +``` + +## CI/CD Integration + +For continuous integration, use TAP output: + +```bash +# In your CI script +cd apps/test-framework +./run-tests.sh --all --tap > test-results.tap + +# Or from project root +apps/test-framework/run-tests.sh --all --tap > test-results.tap +``` + +## Available Commands + +All functionality is available through the single `run-tests.sh` script: + +### Basic Test Execution +- `./run-tests.sh --all` - Run all tests in all modules +- `./run-tests.sh ` - Run tests for specific module +- `./run-tests.sh --dir ` - Run tests in specific directory +- `./run-tests.sh --list` - List available modules +- `./run-tests.sh --count` - Show test count + +### Output Control +- `./run-tests.sh --verbose` - Verbose output with debug info +- `./run-tests.sh --tap` - TAP output for CI/CD +- `./run-tests.sh --debug` - Debug mode with failure details +- `./run-tests.sh --pretty` - Pretty output (default) + +### Test Filtering +- `./run-tests.sh --filter ` - Run tests matching pattern +- `./run-tests.sh --filter ` - Filter within module + +### Utility Functions +- `./run-tests.sh --help` - Show help message +- Install BATS: Use your system package manager (`apt install bats`, `brew install bats-core`, etc.) + + +### Direct Script Usage + +## Examples + +### Running Specific Tests +```bash +# Run only starter-related tests +./run-tests.sh --filter starter + +# Run only tests in startup-scripts module +./run-tests.sh startup-scripts + +# Run all tests with verbose output +./run-tests.sh --all --verbose + +# Run tests in specific directory with debug +./run-tests.sh --dir apps/docker --debug +``` + +### Development Workflow +```bash +# While developing, run tests frequently from module directory +cd apps/my-module +../test-framework/run-tests.sh --dir . + +# Debug failing tests +../test-framework/run-tests.sh --dir . --debug --verbose + +# Run specific test pattern +../test-framework/run-tests.sh --dir . --filter my-feature + +# From project root - run all tests +apps/test-framework/run-tests.sh --all + +# Quick test count check +apps/test-framework/run-tests.sh --count +``` + +## Benefits + +1. **No Boilerplate**: Minimal setup required for new test modules +2. **Consistent Environment**: All tests use the same setup/teardown +3. **Reusable Utilities**: Common functions available across all tests +4. **Centralized Management**: Single place to update test infrastructure +5. **Flexible Execution**: Run tests for one module, multiple modules, or all modules +6. **CI/CD Ready**: TAP output format supported +7. **Easy Debugging**: Built-in debug helpers and verbose output + +## Dependencies + +- [BATS (Bash Automated Testing System)](https://github.com/bats-core/bats-core) +- Standard Unix utilities (find, grep, timeout, etc.) + +Install BATS with your system package manager: +```bash +# Ubuntu/Debian +sudo apt update && sudo apt install bats + +# Fedora/RHEL +sudo dnf install bats + +# macOS +brew install bats-core + +# Arch Linux +sudo pacman -S bats +``` + +## Contributing + +When adding new test utilities: + +1. Add common functions to `helpers/test_common.sh` +2. Add BATS-specific helpers to `bats_libs/acore-support.bash` +3. Add custom assertions to `bats_libs/acore-assert.bash` +4. Update this README with new functionality diff --git a/apps/test-framework/bats_libs/acore-assert.bash b/apps/test-framework/bats_libs/acore-assert.bash new file mode 100644 index 000000000..ad0e6911e --- /dev/null +++ b/apps/test-framework/bats_libs/acore-assert.bash @@ -0,0 +1,178 @@ +#!/usr/bin/env bash + +# AzerothCore BATS Assertions Library +# Custom assertions for AzerothCore testing + +# Assert that a binary exists and is executable +assert_binary_exists() { + local binary_path="$1" + local message="${2:-Binary should exist and be executable}" + + if [[ ! -f "$binary_path" ]]; then + echo "Binary not found: $binary_path" + echo "$message" + return 1 + fi + + if [[ ! -x "$binary_path" ]]; then + echo "Binary not executable: $binary_path" + echo "$message" + return 1 + fi +} + +# Assert that output contains specific AzerothCore patterns +assert_acore_server_started() { + local output="$1" + local server_type="$2" + local message="${3:-Server should show startup message}" + + if [[ ! "$output" =~ $server_type.*starting ]]; then + echo "Server start message not found for $server_type" + echo "Expected pattern: '$server_type.*starting'" + echo "Actual output: $output" + echo "$message" + return 1 + fi +} + +# Assert that configuration file was loaded +assert_config_loaded() { + local output="$1" + local config_file="$2" + local message="${3:-Configuration file should be loaded}" + + if [[ ! "$output" =~ config.*$config_file ]] && [[ ! "$output" =~ $config_file ]]; then + echo "Configuration file loading not detected: $config_file" + echo "Expected to find: config.*$config_file OR $config_file" + echo "Actual output: $output" + echo "$message" + return 1 + fi +} + +# Assert that a process exited with expected code +assert_exit_code() { + local actual_code="$1" + local expected_code="$2" + local message="${3:-Process should exit with expected code}" + + if [[ "$actual_code" -ne "$expected_code" ]]; then + echo "Expected exit code: $expected_code" + echo "Actual exit code: $actual_code" + echo "$message" + return 1 + fi +} + +# Assert that output contains specific error pattern +assert_error_message() { + local output="$1" + local error_pattern="$2" + local message="${3:-Output should contain expected error message}" + + if [[ ! "$output" =~ $error_pattern ]]; then + echo "Expected error pattern not found: $error_pattern" + echo "Actual output: $output" + echo "$message" + return 1 + fi +} + +# Assert that a file was created +assert_file_created() { + local file_path="$1" + local message="${2:-File should be created}" + + if [[ ! -f "$file_path" ]]; then + echo "File not created: $file_path" + echo "$message" + return 1 + fi +} + +# Assert that a directory was created +assert_directory_created() { + local dir_path="$1" + local message="${2:-Directory should be created}" + + if [[ ! -d "$dir_path" ]]; then + echo "Directory not created: $dir_path" + echo "$message" + return 1 + fi +} + +# Assert that output contains success message +assert_success_message() { + local output="$1" + local success_pattern="${2:-success|completed|finished|done}" + local message="${3:-Output should contain success message}" + + if [[ ! "$output" =~ $success_pattern ]]; then + echo "Success message not found" + echo "Expected pattern: $success_pattern" + echo "Actual output: $output" + echo "$message" + return 1 + fi +} + +# Assert that build was successful +assert_build_success() { + local output="$1" + local message="${2:-Build should complete successfully}" + + local build_success_patterns="Build completed|compilation successful|build.*success|make.*success" + assert_success_message "$output" "$build_success_patterns" "$message" +} + +# Assert that server is responsive +assert_server_responsive() { + local output="$1" + local server_type="$2" + local message="${3:-Server should be responsive}" + + if [[ ! "$output" =~ $server_type.*initialized ]] && [[ ! "$output" =~ $server_type.*ready ]]; then + echo "Server responsiveness not detected for $server_type" + echo "Expected pattern: '$server_type.*initialized' OR '$server_type.*ready'" + echo "Actual output: $output" + echo "$message" + return 1 + fi +} + +# Assert that timeout occurred (for long-running processes) +assert_timeout() { + local exit_code="$1" + local message="${2:-Process should timeout as expected}" + + if [[ "$exit_code" -ne 124 ]]; then + echo "Expected timeout (exit code 124)" + echo "Actual exit code: $exit_code" + echo "$message" + return 1 + fi +} + +# Assert that log file contains expected content +assert_log_contains() { + local log_file="$1" + local expected_content="$2" + local message="${3:-Log file should contain expected content}" + + if [[ ! -f "$log_file" ]]; then + echo "Log file not found: $log_file" + echo "$message" + return 1 + fi + + if ! grep -q "$expected_content" "$log_file"; then + echo "Expected content not found in log: $expected_content" + echo "Log file: $log_file" + echo "Log contents:" + cat "$log_file" | head -20 + echo "$message" + return 1 + fi +} diff --git a/apps/test-framework/bats_libs/acore-support.bash b/apps/test-framework/bats_libs/acore-support.bash new file mode 100644 index 000000000..afbeb4fd9 --- /dev/null +++ b/apps/test-framework/bats_libs/acore-support.bash @@ -0,0 +1,116 @@ +#!/usr/bin/env bash + +# AzerothCore BATS Support Library +# Additional helper functions for BATS testing + +# Load common test utilities +source "$(dirname "${BASH_SOURCE[0]}")/../helpers/test_common.sh" + +# Standard setup for all AzerothCore tests +acore_test_setup() { + setup_test_env + create_acore_binaries + create_acore_configs +} + +# Standard teardown for all AzerothCore tests +acore_test_teardown() { + cleanup_test_env +} + +# Quick setup for startup script tests +startup_scripts_setup() { + acore_test_setup + create_test_script_config "test" "test-server" + + # Create additional test binary for startup scripts + create_test_binary "test-server" 0 2 "Test server starting with config:" + + # Create the test-server.conf file that tests expect + cat > "$TEST_DIR/test-server.conf" << EOF +# Test server configuration file +# Generated by AzerothCore test framework +Database.Info = "127.0.0.1;3306;acore;acore;acore_world" +LoginDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_auth" +CharacterDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_characters" +EOF +} + +# Quick setup for compiler tests +compiler_setup() { + acore_test_setup + + # Create mock build tools + create_test_binary "gcc" 0 1 + create_test_binary "g++" 0 1 + create_test_binary "ninja" 0 2 + + # Create mock CMake files + mkdir -p "$TEST_DIR/build" + touch "$TEST_DIR/build/CMakeCache.txt" + echo "CMAKE_BUILD_TYPE:STRING=RelWithDebInfo" > "$TEST_DIR/build/CMakeCache.txt" +} + +# Quick setup for docker tests +docker_setup() { + acore_test_setup + + # Create mock docker commands + create_test_binary "docker" 0 1 "Docker container started" + create_test_binary "docker-compose" 0 2 "Docker Compose services started" + + # Create test docker files + cat > "$TEST_DIR/Dockerfile" << 'EOF' +FROM ubuntu:20.04 +RUN apt-get update +EOF + + cat > "$TEST_DIR/docker-compose.yml" << 'EOF' +version: '3.8' +services: + test-service: + image: ubuntu:20.04 +EOF +} + +# Quick setup for extractor tests +extractor_setup() { + acore_test_setup + + # Create mock client data directories + mkdir -p "$TEST_DIR/client"/{Maps,vmaps,mmaps,dbc} + + # Create some test data files + echo "Test map data" > "$TEST_DIR/client/Maps/test.map" + echo "Test DBC data" > "$TEST_DIR/client/dbc/test.dbc" +} + +# Helper to run command with timeout and capture output +run_with_timeout() { + local timeout_duration="$1" + shift + run timeout "$timeout_duration" "$@" +} + +# Helper to check if a process is running +process_running() { + local process_name="$1" + pgrep -f "$process_name" >/dev/null 2>&1 +} + +# Helper to wait for a condition +wait_for_condition() { + local condition="$1" + local timeout="${2:-10}" + local interval="${3:-1}" + + local count=0 + while ! eval "$condition"; do + sleep "$interval" + count=$((count + interval)) + if [[ $count -ge $timeout ]]; then + return 1 + fi + done + return 0 +} diff --git a/apps/test-framework/helpers/test_common.sh b/apps/test-framework/helpers/test_common.sh new file mode 100644 index 000000000..67193cacb --- /dev/null +++ b/apps/test-framework/helpers/test_common.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash + +# AzerothCore Test Common Utilities +# Shared functions and setup for all BATS tests + +export AC_TEST_FRAMEWORK_VERSION="1.0.0" + +# Get paths +AC_TEST_FRAMEWORK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +AC_PROJECT_ROOT="$(cd "$AC_TEST_FRAMEWORK_DIR/../.." && pwd)" + +# Common test environment setup +setup_test_env() { + export TEST_DIR="$(mktemp -d)" + export AC_TEST_ROOT="$AC_PROJECT_ROOT" + export AC_TEST_APPS="$AC_TEST_ROOT/apps" + + # Create standard test directory structure + mkdir -p "$TEST_DIR"/{bin,etc,logs,data,crashes,build} + + # Set up test-specific environment variables + export ORIGINAL_PATH="$PATH" + export PATH="$TEST_DIR/bin:$PATH" + + # Common environment variables for AzerothCore + export BUILDPATH="$TEST_DIR/build" + export SRCPATH="$AC_TEST_ROOT" + export BINPATH="$TEST_DIR/bin" + export LOGS_PATH="$TEST_DIR/logs" +} + +cleanup_test_env() { + if [[ -n "$TEST_DIR" && -d "$TEST_DIR" ]]; then + rm -rf "$TEST_DIR" + fi + if [[ -n "$ORIGINAL_PATH" ]]; then + export PATH="$ORIGINAL_PATH" + fi +} + +# Create standard test binary +create_test_binary() { + local binary_name="$1" + local exit_code="${2:-0}" + local runtime="${3:-2}" + local extra_output="${4:-""}" + + cat > "$TEST_DIR/bin/$binary_name" << EOF +#!/usr/bin/env bash +echo "$binary_name starting with config: \$2" +echo "$binary_name running for $runtime seconds..." +if [[ -n "$extra_output" ]]; then + echo "$extra_output" +fi +sleep $runtime +echo "$binary_name exiting with code $exit_code" +exit $exit_code +EOF + chmod +x "$TEST_DIR/bin/$binary_name" +} + +# Create test configuration file +create_test_config() { + local config_name="$1" + local content="$2" + + cat > "$TEST_DIR/etc/$config_name" << EOF +# Test configuration file: $config_name +# Generated by AzerothCore test framework +$content +EOF +} + +# Create AzerothCore specific test binaries +create_acore_binaries() { + create_test_binary "authserver" 0 1 "AuthServer initialized" + create_test_binary "worldserver" 0 2 "WorldServer initialized" + create_test_binary "cmake" 0 1 "CMake configured" + create_test_binary "make" 0 2 "Build completed" + create_test_binary "mapextractor" 0 3 "Map extraction completed" + create_test_binary "vmap4extractor" 0 2 "VMap extraction completed" + create_test_binary "vmap4assembler" 0 1 "VMap assembly completed" + create_test_binary "mmaps_generator" 0 5 "MMap generation completed" +} + +# Create AzerothCore specific test configs +create_acore_configs() { + create_test_config "authserver.conf" 'Database.Info = "127.0.0.1;3306;acore;acore;acore_auth" +LoginDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_auth"' + + create_test_config "worldserver.conf" 'Database.Info = "127.0.0.1;3306;acore;acore;acore_world" +LoginDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_auth" +CharacterDatabaseInfo = "127.0.0.1;3306;acore;acore;acore_characters"' + + create_test_config "config.sh" "export BUILDPATH=\"$TEST_DIR/build\" +export SRCPATH=\"$AC_TEST_ROOT\" +export BINPATH=\"$TEST_DIR/bin\" +export LOGS_PATH=\"$TEST_DIR/logs\"" +} + +# Create a test script configuration (for startup scripts) +create_test_script_config() { + local script_name="$1" + local binary_name="${2:-authserver}" + + cat > "$TEST_DIR/conf-$script_name.sh" << EOF +export BINPATH="$TEST_DIR/bin" +export SERVERBIN="$binary_name" +export CONFIG="$TEST_DIR/etc/$binary_name.conf" +export LOGS_PATH="$TEST_DIR/logs" +export LOG_PREFIX_NAME="$script_name" +export SCREEN_NAME="AC-$script_name" +export GDB_ENABLED=0 +export WITH_CONSOLE=1 +EOF +} + +# Debug helper function +debug_on_failure() { + if [[ "$status" -ne 0 ]]; then + echo "Command failed with status: $status" >&3 + echo "Output was:" >&3 + echo "$output" >&3 + if [[ -n "$TEST_DIR" ]]; then + echo "Test directory contents:" >&3 + ls -la "$TEST_DIR" >&3 2>/dev/null || true + fi + fi +} + +# Print test environment info +print_test_env() { + echo "Test Environment:" >&3 + echo " TEST_DIR: $TEST_DIR" >&3 + echo " AC_TEST_ROOT: $AC_TEST_ROOT" >&3 + echo " AC_TEST_APPS: $AC_TEST_APPS" >&3 + echo " PATH: $PATH" >&3 +} + +# Check if running in test mode +is_test_mode() { + [[ -n "$BATS_TEST_FILENAME" ]] || [[ -n "$TEST_DIR" ]] +} diff --git a/apps/test-framework/run-tests.sh b/apps/test-framework/run-tests.sh new file mode 100755 index 000000000..0cf4c08a4 --- /dev/null +++ b/apps/test-framework/run-tests.sh @@ -0,0 +1,290 @@ +#!/usr/bin/env bash + +# AzerothCore Universal Test Runner +# This script provides a unified way to run BATS tests across all modules + +# Get the script directory and project root +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +show_help() { + echo -e "${BLUE}AzerothCore Universal Test Runner${NC}" + echo "" + echo "Usage: $0 [OPTIONS] [TEST_MODULES...]" + echo "" + echo "Options:" + echo " -h, --help Show this help message" + echo " -v, --verbose Enable verbose output" + echo " -t, --tap Use TAP output format (for CI/CD)" + echo " -p, --pretty Use pretty output format (default)" + echo " -f, --filter Run only tests matching pattern" + echo " -c, --count Show test count only" + echo " -d, --debug Enable debug mode (shows output on failure)" + echo " -l, --list List available test modules" + echo " --dir Run tests in specific directory" + echo " --all Run all tests in all modules" + echo "" + echo "Test Modules:" + echo " startup-scripts - Startup script tests" + echo " compiler - Compiler script tests" + echo " docker - Docker-related tests" + echo " installer - Installer script tests" + echo "" + echo "Examples:" + echo " $0 # Run tests in current directory" + echo " $0 --all # Run all tests in all modules" + echo " $0 startup-scripts # Run startup-scripts tests only" + echo " $0 --dir apps/docker # Run tests in specific directory" + echo " $0 --verbose startup-scripts # Run with verbose output" + echo " $0 --filter starter # Run only tests matching 'starter'" + echo " $0 --tap # Output in TAP format for CI" +} + +# Parse command line arguments +VERBOSE=false +TAP=false +PRETTY=true +FILTER="" +COUNT_ONLY=false +DEBUG=false +LIST_MODULES=false +RUN_ALL=false +TEST_DIRS=() +TEST_MODULES=() + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + show_help + exit 0 + ;; + -v|--verbose) + VERBOSE=true + shift + ;; + -t|--tap) + TAP=true + PRETTY=false + shift + ;; + -p|--pretty) + PRETTY=true + TAP=false + shift + ;; + -f|--filter) + FILTER="$2" + shift 2 + ;; + -c|--count) + COUNT_ONLY=true + shift + ;; + -d|--debug) + DEBUG=true + shift + ;; + -l|--list) + LIST_MODULES=true + shift + ;; + --dir) + TEST_DIRS+=("$2") + shift 2 + ;; + --all) + RUN_ALL=true + shift + ;; + *.bats) + # Individual test files + TEST_FILES+=("$1") + shift + ;; + *) + # Assume it's a module name + TEST_MODULES+=("$1") + shift + ;; + esac +done + +# Check if BATS is installed +if ! command -v bats >/dev/null 2>&1; then + echo -e "${RED}Error: BATS is not installed${NC}" + echo "Please install BATS first:" + echo " sudo apt install bats # On Ubuntu/Debian" + echo " brew install bats-core # On macOS" + echo "Or run: make install-bats" + exit 1 +fi + +# Function to find test directories +find_test_directories() { + local search_paths=() + + if [[ "$RUN_ALL" == true ]]; then + # Find all test directories + mapfile -t search_paths < <(find "$PROJECT_ROOT/apps" -type d -name "test" 2>/dev/null) + elif [[ ${#TEST_DIRS[@]} -gt 0 ]]; then + # Use specified directories + for dir in "${TEST_DIRS[@]}"; do + if [[ -d "$PROJECT_ROOT/$dir/test" ]]; then + search_paths+=("$PROJECT_ROOT/$dir/test") + elif [[ -d "$dir/test" ]]; then + search_paths+=("$dir/test") + elif [[ -d "$dir" ]]; then + search_paths+=("$dir") + else + echo -e "${YELLOW}Warning: Test directory not found: $dir${NC}" + fi + done + elif [[ ${#TEST_MODULES[@]} -gt 0 ]]; then + # Use specified modules + for module in "${TEST_MODULES[@]}"; do + if [[ -d "$PROJECT_ROOT/apps/$module/test" ]]; then + search_paths+=("$PROJECT_ROOT/apps/$module/test") + else + echo -e "${YELLOW}Warning: Module test directory not found: $module${NC}" + fi + done + else + # Default: use current directory or startup-scripts if run from test-framework + if [[ "$(basename "$PWD")" == "test-framework" ]]; then + search_paths=("$PROJECT_ROOT/apps/startup-scripts/test") + elif [[ -d "./test" ]]; then + search_paths=("./test") + else + echo -e "${YELLOW}No test directory found. Use --all or specify a module.${NC}" + exit 0 + fi + fi + + echo "${search_paths[@]}" +} + +# Function to list available modules +list_modules() { + echo -e "${BLUE}Available test modules:${NC}" + find "$PROJECT_ROOT/apps" -type d -name "test" 2>/dev/null | while read -r test_dir; do + module_name=$(basename "$(dirname "$test_dir")") + test_count=$(find "$test_dir" -name "*.bats" | wc -l) + echo -e " ${GREEN}$module_name${NC} ($test_count test files)" + done +} + +# Show available modules if requested +if [[ "$LIST_MODULES" == true ]]; then + list_modules + exit 0 +fi + +# Find test directories +TEST_SEARCH_PATHS=($(find_test_directories)) + +if [[ ${#TEST_SEARCH_PATHS[@]} -eq 0 ]]; then + echo -e "${YELLOW}No test directories found.${NC}" + echo "Use --list to see available modules." + exit 0 +fi + +# Collect all test files +TEST_FILES=() +for test_dir in "${TEST_SEARCH_PATHS[@]}"; do + if [[ -d "$test_dir" ]]; then + if [[ -n "$FILTER" ]]; then + # Find test files matching filter + mapfile -t filtered_files < <(find "$test_dir" -name "*.bats" -exec grep -l "$FILTER" {} \; 2>/dev/null) + TEST_FILES+=("${filtered_files[@]}") + else + # Use all test files in directory + mapfile -t dir_files < <(find "$test_dir" -name "*.bats" 2>/dev/null) + TEST_FILES+=("${dir_files[@]}") + fi + fi +done + +if [[ ${#TEST_FILES[@]} -eq 0 ]]; then + if [[ -n "$FILTER" ]]; then + echo -e "${YELLOW}No test files found matching filter: $FILTER${NC}" + else + echo -e "${YELLOW}No test files found in specified directories.${NC}" + fi + exit 0 +fi + +# Show test count only +if [[ "$COUNT_ONLY" == true ]]; then + total_tests=0 + for file in "${TEST_FILES[@]}"; do + count=$(grep -c "^@test" "$file" 2>/dev/null || echo 0) + total_tests=$((total_tests + count)) + done + echo "Total tests: $total_tests" + echo "Test files: ${#TEST_FILES[@]}" + echo "Test directories: ${#TEST_SEARCH_PATHS[@]}" + exit 0 +fi + +# Build BATS command +BATS_CMD="bats" + +# Set output format +if [[ "$TAP" == true ]]; then + BATS_CMD+=" --formatter tap" +elif [[ "$PRETTY" == true ]]; then + BATS_CMD+=" --formatter pretty" +fi + +# Enable verbose output +if [[ "$VERBOSE" == true ]]; then + BATS_CMD+=" --verbose-run" +fi + +# Add filter if specified +if [[ -n "$FILTER" ]]; then + BATS_CMD+=" --filter '$FILTER'" +fi + +# Add test files +BATS_CMD+=" ${TEST_FILES[*]}" + +echo -e "${BLUE}Running AzerothCore Tests${NC}" +echo -e "${YELLOW}Test directories: ${TEST_SEARCH_PATHS[*]}${NC}" +echo -e "${YELLOW}Test files: ${#TEST_FILES[@]}${NC}" +if [[ -n "$FILTER" ]]; then + echo -e "${YELLOW}Filter: $FILTER${NC}" +fi +echo "" + +# Run tests +if [[ "$DEBUG" == true ]]; then + echo -e "${YELLOW}Command: $BATS_CMD${NC}" + echo "" +fi + +# Execute BATS +if eval "$BATS_CMD"; then + echo "" + echo -e "${GREEN}✅ All tests passed!${NC}" + exit 0 +else + exit_code=$? + echo "" + echo -e "${RED}❌ Some tests failed!${NC}" + + if [[ "$DEBUG" == true ]]; then + echo -e "${YELLOW}Tip: Check the output above for detailed error information${NC}" + echo -e "${YELLOW}You can also run individual tests for more detailed debugging:${NC}" + echo -e "${YELLOW} $0 --verbose --filter ${NC}" + fi + + exit $exit_code +fi diff --git a/conf/dist/config.sh b/conf/dist/config.sh index 3c85c0c18..df7ddd3fc 100644 --- a/conf/dist/config.sh +++ b/conf/dist/config.sh @@ -14,6 +14,10 @@ BINPATH="$AC_PATH_ROOT/env/dist" # Change it if you really know what you're doing. # OSTYPE="" +# Configuration for the installer to skip the MySQL installation. +# This is useful when your MySQL is in a container or another machine. +SKIP_MYSQL_INSTALL=${SKIP_MYSQL_INSTALL:-false} + # When using linux, our installer automatically get information about your distro # using lsb_release. If your distro is not supported but it's based on ubuntu or debian, # please change it to one of these values. @@ -84,6 +88,7 @@ CCOREPCH=${CCOREPCH:-ON} CAPPS_BUILD=${CAPPS_BUILD:-all} # build tools list variable +# example: none, db-only, maps-only, all CTOOLS_BUILD=${CTOOLS_BUILD:-none} # build apps list diff --git a/data/sql/create/create_mysql.sql b/data/sql/create/create_mysql.sql index c9af8c3cd..5e362df79 100644 --- a/data/sql/create/create_mysql.sql +++ b/data/sql/create/create_mysql.sql @@ -3,11 +3,12 @@ CREATE USER 'acore'@'localhost' IDENTIFIED BY 'acore' WITH MAX_QUERIES_PER_HOUR GRANT ALL PRIVILEGES ON * . * TO 'acore'@'localhost' WITH GRANT OPTION; -CREATE DATABASE `acore_world` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; +-- Create databases for AzerothCore, only if they do not exist +CREATE DATABASE IF NOT EXISTS `acore_world` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; -CREATE DATABASE `acore_characters` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; +CREATE DATABASE IF NOT EXISTS `acore_characters` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; -CREATE DATABASE `acore_auth` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; +CREATE DATABASE IF NOT EXISTS `acore_auth` DEFAULT CHARACTER SET UTF8MB4 COLLATE utf8mb4_unicode_ci; GRANT ALL PRIVILEGES ON `acore_world` . * TO 'acore'@'localhost' WITH GRANT OPTION; From c581abc4cd255e643d4554d0ca515aef9d81bf1e Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Tue, 1 Jul 2025 16:39:05 +0200 Subject: [PATCH 17/43] fix (DB/Creature/Scarlet Enclave) Implement Ghouls and Gryphon spawn and behaviour. (#22348) Co-authored-by: Yehonal --- .../Gryph_Ghouls_mechanic.sql | 378 +++++++++++++++++ .../ScarletEnclave/chapter2.cpp | 399 ++++++++++++++++++ 2 files changed, 777 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Gryph_Ghouls_mechanic.sql diff --git a/data/sql/updates/pending_db_world/Gryph_Ghouls_mechanic.sql b/data/sql/updates/pending_db_world/Gryph_Ghouls_mechanic.sql new file mode 100644 index 000000000..7f61482cc --- /dev/null +++ b/data/sql/updates/pending_db_world/Gryph_Ghouls_mechanic.sql @@ -0,0 +1,378 @@ +-- Death Knight Initiates (Remove Wrong Guids) +DELETE FROM `creature` WHERE (`id1` = 28406) AND (`guid` IN (129516, 129517, 129518, 129544, 129545, 129555)); +DELETE FROM `creature_addon` WHERE (`guid` IN (129516, 129517, 129518, 129544, 129545, 129555)); + +-- Remove Scourge Gryphons +DELETE FROM `creature` WHERE `id1` = 28906; + +-- Remove Scarlet Ghouls +DELETE FROM `creature` WHERE `id1` = 28897; + +-- Gluttonous Geists (Remove Wrong Guid) +DELETE FROM `creature` WHERE (`id1` = 28905) AND (`guid` IN (130312)); + +-- Gothik the Harvester (Remove Wrong SmartAI and Actionlist) +UPDATE `creature_template` SET `AIName` = '' WHERE `entry` = 28890; +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28890) AND (`source_type` = 0); +DELETE FROM `smart_scripts` WHERE (`entryorguid` IN (2889000, 2889001, 2889002, 2889003)) AND (`source_type` = 9); + +-- Sniffed Waypoints +DELETE FROM `waypoint_data` WHERE `id` IN (13011800, 13011900, 13012000, 13012100, 13022500, 13022600, 13027500, 2889700, 2889701, 2889702, 2889703, 2889704, 2890600, 2890601, 2890602, 2890603, 2890604); +INSERT INTO `waypoint_data` (`id`, `point`, `position_x`, `position_y`, `position_z`, `orientation`, `delay`, `move_type`, `action`, `action_chance`, `wpguid`) VALUES + +-- Acherus Necromancers +("13011800", 1, 2192.1294, -5926.655, 101.12373, NULL, 0, 0, 0, 100, 0), +("13011800", 2, 2163.8767, -5928.8013, 99.28386, NULL, 0, 0, 0, 100, 0), +("13011800", 3, 2137.0066, -5913.505, 100.69998, NULL, 0, 0, 0, 100, 0), +("13011800", 4, 2128.6396, -5891.006, 102.839355, NULL, 0, 0, 0, 100, 0), +("13011800", 5, 2121.2722, -5864.696, 102.14012, NULL, 0, 0, 0, 100, 0), +("13011800", 6, 2143.547, -5855.4814, 101.34996, NULL, 0, 0, 0, 100, 0), +("13011800", 7, 2161.3018, -5863.1533, 101.344574, NULL, 0, 0, 0, 100, 0), +("13011800", 8, 2180.6997, -5890.154, 100.975784, NULL, 0, 0, 0, 100, 0), +("13011800", 9, 2201.1484, -5914.511, 101.01622, NULL, 0, 0, 0, 100, 0), +("13011900", 1, 2301.3235, -5846.832, 100.93423, NULL, 0, 0, 0, 100, 0), +("13011900", 2, 2346.4392, -5857.874, 101.7633, NULL, 0, 0, 0, 100, 0), +("13011900", 3, 2387.449, -5852.9507, 106.41135, NULL, 0, 0, 0, 100, 0), +("13011900", 4, 2374.95, -5877.5527, 104.83713, NULL, 0, 0, 0, 100, 0), +("13011900", 5, 2341.982, -5880.2476, 103.6744, NULL, 0, 0, 0, 100, 0), +("13011900", 6, 2299.1343, -5867.592, 100.96091, NULL, 0, 0, 0, 100, 0), +("13012000", 1, 2121.3906, -5834.899, 101.629395, NULL, 0, 0, 0, 100, 0), +("13012000", 2, 2155.612, -5825.2485, 101.583824, NULL, 0, 0, 0, 100, 0), +("13012000", 3, 2168.8728, -5791.4727, 101.15188, NULL, 0, 0, 0, 100, 0), +("13012000", 4, 2201.668, -5760.944, 101.81218, NULL, 0, 0, 0, 100, 0), +("13012000", 5, 2190.6316, -5745.712, 102.219986, NULL, 0, 0, 0, 100, 0), +("13012000", 6, 2155.179, -5760.8345, 100.59677, NULL, 0, 0, 0, 100, 0), +("13012000", 7, 2122.3743, -5761.1772, 98.24538, NULL, 0, 0, 0, 100, 0), +("13012000", 8, 2101.5413, -5781.742, 99.2695, NULL, 0, 0, 0, 100, 0), +("13012000", 9, 2092.2913, -5813.873, 102.13019, NULL, 0, 0, 0, 100, 0), +("13022500", 1, 2268.467, -6093.4062, 5.9264297, NULL, 0, 0, 0, 100, 0), +("13022500", 2, 2278.5603, -6116.154, 3.778914, NULL, 0, 0, 0, 100, 0), +("13022500", 3, 2264.345, -6146.061, 1.8427824, NULL, 0, 0, 0, 100, 0), +("13022500", 4, 2228.4512, -6150.0234, 2.0125759, NULL, 0, 0, 0, 100, 0), +("13022500", 5, 2219.048, -6135.287, 4.938764, NULL, 0, 0, 0, 100, 0), +("13022500", 6, 2223.313, -6104.604, 4.9672318, NULL, 0, 0, 0, 100, 0), +("13022500", 7, 2240.6729, -6085.976, 5.9909015, NULL, 0, 0, 0, 100, 0), +("13022600", 1, 2191.675, -6144.397, 5.0053396, NULL, 0, 0, 0, 100, 0), +("13022600", 2, 2192.3867, -6118.4536, 1.5058692, NULL, 0, 0, 0, 100, 0), +("13022600", 3, 2196.8098, -6080.9766, 2.6130714, NULL, 0, 0, 0, 100, 0), +("13022600", 4, 2198.929, -6066.7637, 5.22815, NULL, 0, 0, 0, 100, 0), +("13022600", 5, 2196.8098, -6080.9766, 2.6130714, NULL, 0, 0, 0, 100, 0), +("13022600", 6, 2192.3867, -6118.4536, 1.5058692, NULL, 0, 0, 0, 100, 0), +("13022600", 7, 2191.675, -6144.397, 5.0053396, NULL, 0, 0, 0, 100, 0), +("13022600", 8, 2192.4177, -6164.6465, 1.7256374, NULL, 0, 0, 0, 100, 0), +("13027500", 1, 2164.9028, -6122.6245, 1.2296143, NULL, 0, 0, 0, 100, 0), +("13027500", 2, 2159.8381, -6102.25, 5.9564657, NULL, 0, 0, 0, 100, 0), +("13027500", 3, 2136.0344, -6084.53, 5.6670914, NULL, 0, 0, 0, 100, 0), +("13027500", 4, 2102.7676, -6104.246, 5.5596447, NULL, 0, 0, 0, 100, 0), +("13027500", 5, 2103.9224, -6143.393, 4.1376762, NULL, 0, 0, 0, 100, 0), +("13027500", 6, 2148.8723, -6160.114, 1.278019, NULL, 0, 0, 0, 100, 0), + +-- Gothik the Harvester +("13012100", 1, 2199.8826, -5906.583, 100.88099, NULL, 0, 0, 0, 100, 0), +("13012100", 2, 2179.8838, -5893.7793, 100.85527, NULL, 0, 0, 0, 100, 0), +("13012100", 3, 2168.7725, -5866.6797, 101.337105, NULL, 0, 0, 0, 100, 0), +("13012100", 4, 2156.3, -5843.0005, 102.0316, NULL, 0, 0, 0, 100, 0), +("13012100", 5, 2139.4514, -5813.6753, 100.5411, NULL, 0, 0, 0, 100, 0), +("13012100", 6, 2125.513, -5785.7827, 98.66528, NULL, 0, 0, 0, 100, 0), +("13012100", 7, 2111.2576, -5763.4824, 98.55336, NULL, 0, 0, 0, 100, 0), +("13012100", 8, 2104.5195, -5735.549, 100.19841, NULL, 0, 0, 0, 100, 0), +("13012100", 9, 2126.6138, -5720.4775, 100.45774, NULL, 0, 0, 0, 100, 0), +("13012100", 10, 2159.1223, -5715.318, 102.24907, NULL, 0, 0, 0, 100, 0), +("13012100", 11, 2172.173, -5736.729, 101.68556, NULL, 0, 0, 0, 100, 0), +("13012100", 12, 2189.5996, -5760.0767, 101.66215, NULL, 0, 0, 0, 100, 0), +("13012100", 13, 2220.4363, -5784.1973, 101.70216, NULL, 0, 0, 0, 100, 0), +("13012100", 14, 2245.4631, -5803.29, 100.99215, NULL, 0, 0, 0, 100, 0), +("13012100", 15, 2261.9277, -5823.2, 100.949066, NULL, 0, 0, 0, 100, 0), +("13012100", 16, 2266.139, -5838.5776, 100.95463, NULL, 0, 0, 0, 100, 0), +("13012100", 17, 2259.798, -5867.397, 101.46121, NULL, 0, 0, 0, 100, 0), +("13012100", 18, 2238.359, -5890.6216, 101.02715, NULL, 0, 0, 0, 100, 0), +("13012100", 19, 2221.4436, -5905.7744, 101.2207, NULL, 0, 0, 0, 100, 0), + +-- Scourge Gryphons (patrol) +("2890600", 1, 1841.5955, -5838.637, 132.42058, NULL, 0, 2, 0, 100, 0), +("2890600", 2, 1784.4916, -5822.41, 136.97614, NULL, 0, 2, 0, 100, 0), +("2890600", 3, 1778.6383, -5898.7744, 135.94832, NULL, 0, 2, 0, 100, 0), +("2890600", 4, 1799.5969, -5927.9746, 135.94832, NULL, 0, 2, 0, 100, 0), +("2890600", 5, 1840.5508, -5923.3677, 135.94832, NULL, 0, 2, 0, 100, 0), +("2890600", 6, 1851.1428, -5908.0063, 132.42058, NULL, 0, 2, 0, 100, 0), +("2890601", 1, 1768.2115, -5862.899, 144.99065, NULL, 0, 2, 0, 100, 0), +("2890601", 2, 1813.2302, -5876.425, 166.76842, NULL, 0, 2, 0, 100, 0), +("2890601", 3, 1764.9922, -5872.4487, 160.85176, NULL, 0, 2, 0, 100, 0), +("2890601", 4, 1743.6553, -5895.7915, 160.85176, NULL, 0, 2, 0, 100, 0), +("2890601", 5, 1774.8065, -5966.014, 160.85176, NULL, 0, 2, 0, 100, 0), +("2890601", 6, 1848.6844, -5976.056, 160.85176, NULL, 0, 2, 0, 100, 0), +("2890601", 7, 1880.036, -5950.2583, 144.99065, NULL, 0, 2, 0, 100, 0), +("2890601", 8, 1848.8954, -5893.6606, 118.93514, NULL, 0, 2, 0, 100, 0), +("2890601", 9, 1832.9224, -5836.408, 125.35181, NULL, 0, 2, 0, 100, 0), +("2890602", 1, 1776.9531, -5897.504, 138.92468, NULL, 0, 2, 0, 100, 0), +("2890602", 2, 1868.4694, -5846.4653, 138.92468, NULL, 0, 2, 0, 100, 0), +("2890602", 3, 1909.5326, -5854.746, 138.92468, NULL, 0, 2, 0, 100, 0), +("2890602", 4, 1895.7626, -5905.596, 138.92468, NULL, 0, 2, 0, 100, 0), +("2890602", 5, 1824.9314, -5931.985, 135.24397, NULL, 0, 2, 0, 100, 0), +("2890602", 6, 1784.5319, -5930.701, 135.43845, NULL, 0, 2, 0, 100, 0), +("2890603", 1, 1791.9382, -5823.7505, 134.85405, NULL, 0, 2, 0, 100, 0), +("2890603", 2, 1835.8387, -5818.3184, 118.6983, NULL, 0, 2, 0, 100, 0), +("2890603", 3, 1836.2428, -5921.221, 130.0594, NULL, 0, 2, 0, 100, 0), +("2890603", 4, 1797.7716, -5938.2046, 130.0594, NULL, 0, 2, 0, 100, 0), +("2890603", 5, 1774.739, -5878.426, 142.21509, NULL, 0, 2, 0, 100, 0), +("2890604", 1, 1841.5955, -5838.637, 132.42058, NULL, 0, 2, 0, 100, 0), +("2890604", 2, 1851.1428, -5908.0063, 132.42058, NULL, 0, 2, 0, 100, 0), +("2890604", 3, 1840.5508, -5923.3677, 135.94832, NULL, 0, 2, 0, 100, 0), +("2890604", 4, 1799.5969, -5927.9746, 135.94832, NULL, 0, 2, 0, 100, 0), +("2890604", 5, 1778.6383, -5898.7744, 135.94832, NULL, 0, 2, 0, 100, 0), +("2890604", 6, 1784.4916, -5822.41, 136.97614, NULL, 0, 2, 0, 100, 0); + +-- Scarlet Ghouls waypoints (must be optimized). +DELETE FROM `waypoints` WHERE `entry` IN (2889700, 2889701, 2889702, 2889703, 2889704, 2889705, 2889706); +INSERT INTO `waypoints` (`entry`, `pointid`, `position_x`, `position_y`, `position_z`, `orientation`, `point_comment`) VALUES +("2889700", 1, 2195.3638, -6096.684, 1.9554013, NULL, 'Scarlet Ghoul'), +("2889700", 2, 2134.17, -6095.6626, 6.1250257, NULL, 'Scarlet Ghoul'), +("2889700", 3, 2093.4673, -6034.3447, 9.515682, NULL, 'Scarlet Ghoul'), +("2889700", 4, 2071.7117, -6016.3516, 12.850294, NULL, 'Scarlet Ghoul'), +("2889700", 5, 2055.4822, -6009.923, 18.771358, NULL, 'Scarlet Ghoul'), +("2889700", 6, 2039.4476, -6003.6445, 26.167065, NULL, 'Scarlet Ghoul'), +("2889700", 7, 2031.4705, -6000.2095, 32.67357, NULL, 'Scarlet Ghoul'), +("2889700", 8, 2027.7562, -6003.44, 37.135815, NULL, 'Scarlet Ghoul'), +("2889700", 9, 2011.0663, -5996.0796, 44.111214, NULL, 'Scarlet Ghoul'), +("2889700", 10, 1997.2365, -5990.1543, 54.388668, NULL, 'Scarlet Ghoul'), +("2889700", 11, 1982.8765, -5984.335, 66.13348, NULL, 'Scarlet Ghoul'), +("2889700", 12, 1969.1548, -5978.072, 77.45486, NULL, 'Scarlet Ghoul'), +("2889700", 13, 1956.6283, -5972.649, 88.85666, NULL, 'Scarlet Ghoul'), +("2889700", 14, 1941.6298, -5965.9805, 100.383446, NULL, 'Scarlet Ghoul'), +("2889700", 15, 1916.4559, -5952.4526, 101.24492, NULL, 'Scarlet Ghoul'), +("2889700", 16, 1882.0538, -5938.461, 103.13395, NULL, 'Scarlet Ghoul'), +("2889700", 17, 1830.4075, -5918.2676, 109.342636, NULL, 'Scarlet Ghoul'), +("2889701", 1, 2134.17, -6095.6626, 6.1250257, NULL, 'Scarlet Ghoul'), +("2889701", 2, 2093.4673, -6034.3447, 9.515682, NULL, 'Scarlet Ghoul'), +("2889701", 3, 2071.7117, -6016.3516, 12.850294, NULL, 'Scarlet Ghoul'), +("2889701", 4, 2055.4822, -6009.923, 18.771358, NULL, 'Scarlet Ghoul'), +("2889701", 5, 2039.4476, -6003.6445, 26.167065, NULL, 'Scarlet Ghoul'), +("2889701", 6, 2031.4705, -6000.2095, 32.67357, NULL, 'Scarlet Ghoul'), +("2889701", 7, 2027.7562, -6003.44, 37.135815, NULL, 'Scarlet Ghoul'), +("2889701", 8, 2011.0663, -5996.0796, 44.111214, NULL, 'Scarlet Ghoul'), +("2889701", 9, 1997.2365, -5990.1543, 54.388668, NULL, 'Scarlet Ghoul'), +("2889701", 10, 1982.8765, -5984.335, 66.13348, NULL, 'Scarlet Ghoul'), +("2889701", 11, 1969.1548, -5978.072, 77.45486, NULL, 'Scarlet Ghoul'), +("2889701", 12, 1956.6283, -5972.649, 88.85666, NULL, 'Scarlet Ghoul'), +("2889701", 13, 1941.6298, -5965.9805, 100.383446, NULL, 'Scarlet Ghoul'), +("2889701", 14, 1932.219, -5938.6284, 102.60785, NULL, 'Scarlet Ghoul'), +("2889701", 15, 1922.8248, -5911.5547, 101.57721, NULL, 'Scarlet Ghoul'), +("2889701", 16, 1904.4485, -5886.1274, 101.34244, NULL, 'Scarlet Ghoul'), +("2889701", 17, 1885.0721, -5868.9033, 102.31583, NULL, 'Scarlet Ghoul'), +("2889701", 18, 1865.0361, -5856.944, 102.96336, NULL, 'Scarlet Ghoul'), +("2889701", 19, 1845.4574, -5845.153, 102.1159, NULL, 'Scarlet Ghoul'), +("2889701", 20, 1827.5663, -5833.8936, 102.35004, NULL, 'Scarlet Ghoul'), +("2889701", 21, 1815.0728, -5826.0312, 104.49583, NULL, 'Scarlet Ghoul'), +("2889701", 22, 1803.1136, -5819.4585, 108.53935, NULL, 'Scarlet Ghoul'), +("2889702", 1, 2149.137, -5851.649, 101.358665, NULL, 'Scarlet Ghoul'), +("2889702", 2, 2053.9631, -5848.3438, 102.19084, NULL, 'Scarlet Ghoul'), +("2889702", 3, 1979.5673, -5854.149, 100.74358, NULL, 'Scarlet Ghoul'), +("2889702", 4, 1892.4893, -5856.9663, 101.901276, NULL, 'Scarlet Ghoul'), +("2889702", 5, 1876.3115, -5847.535, 102.11675, NULL, 'Scarlet Ghoul'), +("2889702", 6, 1854.0287, -5836.069, 101.78623, NULL, 'Scarlet Ghoul'), +("2889702", 7, 1835.2474, -5825.923, 100.77055, NULL, 'Scarlet Ghoul'), +("2889702", 8, 1819.5686, -5818.2095, 104.0615, NULL, 'Scarlet Ghoul'), +("2889702", 9, 1804.9038, -5811.438, 108.21074, NULL, 'Scarlet Ghoul'), +("2889703", 1, 2137.5742, -5793.847, 99.60594, NULL, 'Scarlet Ghoul'), +("2889703", 2, 2061.7412, -5811.5776, 103.39335, NULL, 'Scarlet Ghoul'), +("2889703", 3, 1981.0283, -5807.502, 101.002556, NULL, 'Scarlet Ghoul'), +("2889703", 4, 1912.7769, -5768.238, 103.644135, NULL, 'Scarlet Ghoul'), +("2889703", 5, 1904.1472, -5806.2334, 100.84862, NULL, 'Scarlet Ghoul'), +("2889703", 6, 1896.8984, -5836.7305, 101.094154, NULL, 'Scarlet Ghoul'), +("2889703", 7, 1892.4893, -5856.9663, 101.901276, NULL, 'Scarlet Ghoul'), +("2889703", 8, 1887.3644, -5884.821, 102.246506, NULL, 'Scarlet Ghoul'), +("2889703", 9, 1871.814, -5893.7964, 103.64108, NULL, 'Scarlet Ghoul'), +("2889703", 10, 1857.1747, -5902.0386, 104.01655, NULL, 'Scarlet Ghoul'), +("2889703", 11, 1830.3524, -5917.68, 109.23609, NULL, 'Scarlet Ghoul'), +("2889704", 1, 2135.5713, -5917.6436, 99.79425, NULL, 'Scarlet Ghoul'), +("2889704", 2, 2128.1082, -5918.4746, 102.57842, NULL, 'Scarlet Ghoul'), +("2889704", 3, 2119.8977, -5919.75, 104.845924, NULL, 'Scarlet Ghoul'), +("2889704", 4, 2106.674, -5921.8564, 105.8994, NULL, 'Scarlet Ghoul'), +("2889704", 5, 2098.4468, -5923.068, 106.78917, NULL, 'Scarlet Ghoul'), +("2889704", 6, 2085.5823, -5925.1177, 105.65261, NULL, 'Scarlet Ghoul'), +("2889704", 7, 2072.7903, -5927.2314, 106.47965, NULL, 'Scarlet Ghoul'), +("2889704", 8, 2058.0674, -5929.905, 105.883446, NULL, 'Scarlet Ghoul'), +("2889704", 9, 1993.3854, -5934.4653, 103.23653, NULL, 'Scarlet Ghoul'), +("2889704", 10, 1914.4014, -5934.455, 103.03427, NULL, 'Scarlet Ghoul'), +("2889704", 11, 1897.3982, -5930.1514, 103.310394, NULL, 'Scarlet Ghoul'), +("2889704", 12, 1879.5057, -5926.649, 104.29986, NULL, 'Scarlet Ghoul'), +("2889704", 13, 1859.5677, -5922.3164, 104.62177, NULL, 'Scarlet Ghoul'), +("2889704", 14, 1844.7861, -5919.962, 106.564575, NULL, 'Scarlet Ghoul'), +("2889704", 15, 1830.4172, -5918.243, 109.36247, NULL, 'Scarlet Ghoul'), +("2889705", 1, 2339.3877, -5872.3906, 102.40258, NULL, 'Scarlet Ghoul'), +("2889705", 2, 2277.8735, -5881.4644, 100.51856, NULL, 'Scarlet Ghoul'), +("2889705", 3, 2237.9512, -5908.162, 100.5426, NULL, 'Scarlet Ghoul'), +("2889705", 4, 2179.2607, -5916.5723, 100.833466, NULL, 'Scarlet Ghoul'), +("2889705", 5, 2135.5713, -5917.6436, 99.79425, NULL, 'Scarlet Ghoul'), +("2889705", 6, 2128.1082, -5918.4746, 102.57842, NULL, 'Scarlet Ghoul'), +("2889705", 7, 2119.8977, -5919.75, 104.845924, NULL, 'Scarlet Ghoul'), +("2889705", 8, 2106.674, -5921.8564, 105.8994, NULL, 'Scarlet Ghoul'), +("2889705", 9, 2098.4468, -5923.068, 106.78917, NULL, 'Scarlet Ghoul'), +("2889705", 10, 2085.5823, -5925.1177, 105.65261, NULL, 'Scarlet Ghoul'), +("2889705", 11, 2072.7903, -5927.2314, 106.47965, NULL, 'Scarlet Ghoul'), +("2889705", 12, 2058.0674, -5929.905, 105.883446, NULL, 'Scarlet Ghoul'), +("2889705", 13, 1993.3854, -5934.4653, 103.23653, NULL, 'Scarlet Ghoul'), +("2889705", 14, 1914.4014, -5934.455, 103.03427, NULL, 'Scarlet Ghoul'), +("2889705", 15, 1897.3982, -5930.1514, 103.310394, NULL, 'Scarlet Ghoul'), +("2889705", 16, 1879.5057, -5926.649, 104.29986, NULL, 'Scarlet Ghoul'), +("2889705", 17, 1859.5677, -5922.3164, 104.62177, NULL, 'Scarlet Ghoul'), +("2889705", 18, 1844.7861, -5919.962, 106.564575, NULL, 'Scarlet Ghoul'), +("2889705", 19, 1830.4172, -5918.243, 109.36247, NULL, 'Scarlet Ghoul'), +("2889706", 1, 2278.1655, -5838.218, 100.934555, NULL, 'Scarlet Ghoul'), +("2889706", 2, 2226.8245, -5841.505, 101.31162, NULL, 'Scarlet Ghoul'), +("2889706", 3, 2172.0278, -5844.5312, 101.348076, NULL, 'Scarlet Ghoul'), +("2889706", 4, 2149.137, -5851.649, 101.358665, NULL, 'Scarlet Ghoul'), +("2889706", 5, 2053.9631, -5848.3438, 102.19084, NULL, 'Scarlet Ghoul'), +("2889706", 6, 1979.5673, -5854.149, 100.74358, NULL, 'Scarlet Ghoul'), +("2889706", 7, 1892.4893, -5856.9663, 101.901276, NULL, 'Scarlet Ghoul'), +("2889706", 8, 1876.3115, -5847.535, 102.11675, NULL, 'Scarlet Ghoul'), +("2889706", 9, 1854.0287, -5836.069, 101.78623, NULL, 'Scarlet Ghoul'), +("2889706", 10, 1835.2474, -5825.923, 100.77055, NULL, 'Scarlet Ghoul'), +("2889706", 11, 1819.5686, -5818.2095, 104.0615, NULL, 'Scarlet Ghoul'), +("2889706", 12, 1804.9038, -5811.438, 108.21074, NULL, 'Scarlet Ghoul'); + +-- Update Spawn Position, MT and WD for Gluttonous Geists +UPDATE `creature` SET `position_x` = 2388.3235, `position_y` = -5898.371, `position_z` = 108.7139 , `orientation` = 0.833046, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130297)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2356.6306, `position_y` = -5879.3896, `position_z` = 104.7101, `orientation` = 1.95747, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130298)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2312.349, `position_y` = -5901.662, `position_z` = 93.44686, `orientation` = 3.51433, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130299)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2428.0354, `position_y` = -5865.3955, `position_z` = 105.913315, `orientation` = 0.92807, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130300)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2247.437, `position_y` = -5865.558, `position_z` = 100.95554, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130301)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2252.52, `position_y` = -5820.546, `position_z` = 101.00846, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130302)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2235.5793, `position_y` = -5752.613, `position_z` = 101.88058, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130303)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2214.799, `position_y` = -5777.5337, `position_z` = 101.7837, `orientation` = 1.6580, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130304)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2192.6064, `position_y` = -5737.7773, `position_z` = 102.33999, `orientation` = 1.34156, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130305)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2147.7168, `position_y` = -5722.7305, `position_z` = 101.01196, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130306)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2131.7708, `position_y` = -5680.339, `position_z` = 101.83525, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130307)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2092.2354, `position_y` = -5713.4717, `position_z` = 100.232216, `orientation` = 0.86879, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130308)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2085.712, `position_y` = -5754.1934, `position_z` = 99.33097, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130309)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2112.1594, `position_y` = -5754.3667, `position_z` = 98.92251, `orientation` = 4.29416, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130310)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2146.0344, `position_y` = -5768.425, `position_z` = 99.74828, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130311)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2282.6775, `position_y` = -6127.073, `position_z` = 3.128897, `orientation` = 1.570796, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130313)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2155.9587, `position_y` = -5775.278, `position_z` = 100.73741, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130314)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2138.0042, `position_y` = -5892.933, `position_z` = 101.96939, `orientation` = 4.05716, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130315)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2141.9036, `position_y` = -5887.86, `position_z` = 101.54953, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130316)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2169.8506, `position_y` = -5863.065, `position_z` = 101.369194, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130317)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2183.372, `position_y` = -5923.872, `position_z` = 101.57689, `orientation` = 4.8328, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130318)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2249.3577, `position_y` = -5915.88, `position_z` = 99.13981, `orientation` = 1.57079, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130319)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2249.8372, `position_y` = -6081.656, `position_z` = 6.1703725, `orientation` = 1.570796, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130320)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2217.7444, `position_y` = -6110.887, `position_z` = 4.716755, `orientation` = 1.570796, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130321)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2223.4993, `position_y` = -6090.174, `position_z` = 4.934598, `orientation` = 2.358956, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130322)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2245.426, `position_y` = -6154.503, `position_z` = 1.8226079, `orientation` = 0.860172, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130323)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2208.11, `position_y` = -6158.6616, `position_z` = 1.6163365, `orientation` = 1.570796, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130324)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2206.7603, `position_y` = -6125.985, `position_z` = 4.9951015, `orientation` = 1.570796, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130325)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2145.946, `position_y` = -6108.169, `position_z` = 4.5653887, `orientation` = 2.261537, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130326)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2160.8967, `position_y` = -6068.0996, `position_z` = 5.431866, `orientation` = 1.570796, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130327)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2136.2654, `position_y` = -6072.4595, `position_z` = 5.4788465, `orientation` = 1.570796, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130328)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2126.696, `position_y` = -6146.5674, `position_z` = 2.2910354, `orientation` = 5.106129, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130330)) AND (`id1` IN (28905)); +UPDATE `creature` SET `position_x` = 2116.3901, `position_y` = -6183.808, `position_z` = 13.845215, `orientation` = 1.570796, `MovementType` = 1, `wander_distance` = 10 WHERE (`guid` IN (130331)) AND (`id1` IN (28905)); + +-- Update Spawn Position, MT and WD for Acherus Necromancers +UPDATE `creature` SET `position_x` = 2190.7517, `position_y` = -5913.1743, `position_z` = 101.022514, `orientation` = 1.20243, `wander_distance` = 0, `MovementType` = 0 WHERE (`guid` IN (130118)) AND (`id1` IN (28889)); +UPDATE `creature` SET `position_x` = 2299.1343, `position_y` = -5867.592, `position_z` = 100.96091, `orientation` = 0.84453, `wander_distance` = 0, `MovementType` = 0 WHERE (`guid` IN (130119)) AND (`id1` IN (28889)); +UPDATE `creature` SET `position_x` = 2092.2913, `position_y` = -5813.873, `position_z` = 102.13019, `orientation` = 5.17755, `wander_distance` = 0, `MovementType` = 0 WHERE (`guid` IN (130120)) AND (`id1` IN (28889)); + +-- Add new Acherus Necromancers +DELETE FROM `creature` WHERE (`guid` IN (130225, 130226, 130275)) AND (`id1` = 28889); +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 +(130225, 28889, 0, 0, 609, 0, 0, 1, 4, 0, 2240.6729, -6085.976, 5.9909015, 5.99090, 360, 0, 0, 8982, 3155, 0, 0, 0, 0, '', 0, 0, NULL), +(130226, 28889, 0, 0, 609, 0, 0, 1, 4, 0, 2192.4177, -6164.6465, 1.7256374, 1.58369, 360, 0, 0, 8982, 3155, 0, 0, 0, 0, '', 0, 0, NULL), +(130275, 28889, 0, 0, 609, 0, 0, 1, 4, 0, 2148.8723, -6160.114, 1.278019, 2.03137, 360, 0, 0, 8982, 3155, 0, 0, 0, 0, '', 0, 0, NULL); + +-- Add creature addon for Acherus Necromancers +DELETE FROM `creature_addon` WHERE (`guid` IN (130118, 130119, 130120, 130225, 130226, 130275)); +INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(130118, 13011800, 0, 0, 0, 0, 0, ''), +(130119, 13011900, 0, 0, 0, 0, 0, ''), +(130120, 13012000, 0, 0, 0, 0, 0, ''), +(130225, 13022500, 0, 0, 0, 0, 0, ''), +(130226, 13022600, 0, 0, 0, 0, 0, ''), +(130275, 13027500, 0, 0, 0, 0, 0, ''); + +-- Update Spawn and MT for Gothik the Harvester +UPDATE `creature` SET `position_x` = 2221.4436, `position_y` = -5905.7744, `position_z` = 101.2207, `orientation` = 3.2595, `wander_distance` = 0, `MovementType` = 0 WHERE (`guid` IN (130121)) AND (`id1` IN (28890)); + +-- Update Waypoint ID for Gothik the Harvester +UPDATE `creature_addon` SET `path_id` = 13012100 WHERE (`guid` IN (130121)); + +-- Update text_table for Gothik the Harvester +DELETE FROM `creature_text` WHERE(`CreatureID` = 28890); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(28890, 0, 0, 'You will fly again, beast...', 12, 0, 100, 5, 0, 0, 29043, 0, 'Gothik the Harvester'), +(28890, 0, 1, 'Death comes on bony wings...', 12, 0, 100, 5, 0, 0, 29044, 0, 'Gothik the Harvester'), +(28890, 0, 2, 'Rise, minion. Rise and fly for the Scourge!', 12, 0, 100, 5, 0, 0, 29045, 0, 'Gothik the Harvester'), +(28890, 1, 0, 'Hrm? Lets see what we get out of this one.', 12, 0, 100, 5, 0, 0, 29032, 0, 'Gothik the Harvester'), +(28890, 1, 1, 'Blasted geists! They ve practically devoured this one whole...', 12, 0, 100, 5, 0, 0, 29033, 0, 'Gothik the Harvester'), +(28890, 1, 2, 'Contemptible wretches! Stay away from the bodies!', 12, 0, 100, 5, 0, 0, 29034, 0, 'Gothik the Harvester'), +(28890, 1, 3, 'This one is especially ugly. Perfect for a ghoul...', 12, 0, 100, 5, 0, 0, 29035, 0, 'Gothik the Harvester'), +(28890, 1, 4, 'I think its spine is broken. Ghoul it is!', 12, 0, 100, 5, 0, 0, 29036, 0, 'Gothik the Harvester'), +(28890, 1, 5, 'Is Gothik the Harvester going to have to choke a geist?', 12, 0, 100, 5, 0, 0, 29037, 0, 'Gothik the Harvester'), +(28890, 1, 6, 'Surprise, surprise! Another ghoul!', 12, 0, 100, 5, 0, 0, 29038, 0, 'Gothik the Harvester'), +(28890, 2, 0, 'Death is the only escape!', 12, 0, 100, 5, 0, 0, 29039, 0, 'Gothik the Harvester'); + +-- Create Scarlet Ghouls template_movement. +DELETE FROM `creature_template_movement` WHERE (`CreatureId` = 28897); +INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES +(28897, 1, 0, 0, 0, 0, 2, 0); + +-- Update Scarlet Ghouls SmartAI/ActionList +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28897; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28897); +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 +(28897, 0, 0, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 2889700, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - On Just Summoned - Run Script'), +(28897, 0, 1, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 37, 300000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - On Just Summoned - Kill Self'), +(28897, 0, 2, 0, 58, 0, 100, 0, 0, 0, 0, 0, 0, 0, 89, 30, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - On Path 0 Finished - Start Random Movement'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2889700); +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 +(2889700, 9, 0, 0, 0, 0, 100, 0, 2000, 2000, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - Actionlist - Say Line 0'), +(2889700, 9, 1, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 113, 2889700, 2889706, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - Actionlist - Start Closest Waypoint 2889700-2889706'); + +-- Update Scourge Gryphons SmartAI/ActionList +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28906; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28906); +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 +(28906, 0, 0, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 87, 2890600, 2890601, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scourge Gryphon - On Just Summoned - Run Random Script'), +(28906, 0, 1, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 37, 300000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scourge Gryphon - On Just Summoned - Kill Self'), +(28906, 0, 2, 0, 34, 0, 100, 0, 8, 1, 0, 0, 0, 0, 233, 2890600, 2890604, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scourge Gryphon - On Reached Point 1 - Start Random Path 2890600-2890604'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2890600); +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 +(2890600, 9, 0, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 1805.06, -5810.82, 128.38, 0, 'Scourge Gryphon - Actionlist - Move To Position'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 9 AND `entryorguid` = 2890601); +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 +(2890601, 9, 0, 0, 0, 0, 100, 0, 3000, 3000, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 1831.77, -5913.56, 129.329, 0, 'Scourge Gryphon - Actionlist - Move To Position'); + +-- Set Despawn on Spellhit +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE (`entry` IN (28896, 28898, 28892, 28891, 28886, 28893)); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0) AND (`entryorguid` IN (28896, 28898, 28892, 28891, 28886, 28893)); +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 +(28896, 0, 0, 0, 8, 0, 100, 512, 52683, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Infantryman - On Spellhit \'Scarlet Ghoul\' - Despawn Instant'), +(28898, 0, 0, 0, 8, 0, 100, 512, 52683, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Captain - On Spellhit \'Scarlet Ghoul\' - Despawn Instant'), +(28892, 0, 0, 0, 8, 0, 100, 512, 52683, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Peasant - On Spellhit \'Scarlet Ghoul\' - Despawn Instant'), +(28891, 0, 0, 0, 8, 0, 100, 512, 52683, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Miner - On Spellhit \'Scarlet Ghoul\' - Despawn Instant'), +(28886, 0, 0, 0, 8, 0, 100, 512, 52683, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Fleet Defender - On Spellhit \'Scarlet Ghoul\' - Despawn Instant'), +(28893, 0, 0, 0, 8, 0, 100, 512, 52685, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Gryphon - On Spellhit \'Scourge Gryphon\' - Despawn Instant'); + +-- Acherus Dummies (disable gravity and set active) +DELETE FROM `creature_template_movement` WHERE (`CreatureId` = 28935); +INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES +(28935, 0, 0, 1, 0, 0, 0, 0); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28935; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28935); +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 +(28935, 0, 0, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 48, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Acherus Dummy - On Reset - Set Active On'); + +-- Link creature entries to the implemented C++ scripts +UPDATE `creature_template` SET `ScriptName` = 'npc_acherus_necromancer' WHERE `entry` = 28889; +UPDATE `creature_template` SET `ScriptName` = 'npc_gothik_the_harvester' WHERE `entry` = 28890; + +-- Fixes missing condition for Scarlet Ghoul +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 13 AND `SourceGroup` = 1 AND `SourceEntry` = 52683 AND `SourceId` = 0 AND `ConditionTypeOrReference` = 31 AND `ConditionTarget` = 0 AND `ConditionValue1` = 3 AND `ConditionValue2` = 28895 AND `ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`SourceId`,`ElseGroup`,`ConditionTypeOrReference`,`ConditionTarget`,`ConditionValue1`,`ConditionValue2`,`ConditionValue3`,`NegativeCondition`,`ErrorType`,`ErrorTextId`,`ScriptName`,`Comment`) VALUES +(13,1,52683,0,5,31,0,3,28895,0,0,0,0,'','Scarlet Ghoul only targets Scarlet Medic'); diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp index 16adbed9c..5d2505b15 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter2.cpp @@ -24,6 +24,9 @@ #include "ScriptedEscortAI.h" #include "SpellInfo.h" #include "SpellScript.h" +#include "MotionMaster.h" +#include "ObjectAccessor.h" +#include //How to win friends and influence enemies // texts signed for creature 28939 but used for 28939, 28940, 28610 @@ -701,10 +704,406 @@ public: }; }; +// Spell and NPC IDs for Scourge Assault event +enum NecroSpells +{ + SPELL_SCARLET_GHOUL = 52683, // Raises a Scarlet Ghoul from a humanoid corpse + SPELL_SCOURGE_GRYPHON = 52685, // Raises a Scourge Gryphon from a gryphon corpse + SPELL_GHOULPLOSION = 52672 // Causes a Gluttonous Geist to explode (kill) +}; + +enum NecroNPCs +{ + NPC_GLUTTONOUS_GEIST = 28905, + NPC_DEAD_SCARLET_MEDIC = 28895, + NPC_DEAD_SCARLET_INFANTRYMAN = 28896, + NPC_DEAD_SCARLET_CAPTAIN = 28898, + NPC_DEAD_SCARLET_PEASANT = 28892, + NPC_DEAD_SCARLET_MINER = 28891, + NPC_DEAD_SCARLET_FLEET_DEFENDER = 28886, + NPC_DEAD_SCARLET_GRYPHON = 28893 +}; + +/*###### +## npc_acherus_necromancer (Entry 28889) +######*/ +class npc_acherus_necromancer : public CreatureScript +{ +public: + npc_acherus_necromancer() : CreatureScript("npc_acherus_necromancer") { } + + struct npc_acherus_necromancerAI : public ScriptedAI + { + npc_acherus_necromancerAI(Creature* creature) : ScriptedAI(creature) { } + + EventMap events; + ObjectGuid targetCorpseGUID; + ObjectGuid geistGUID; + bool isOnRitual; + + // Event timers (IDs) + enum Events + { + EVENT_START_RITUAL = 1, + EVENT_GHOULPLOSION, + EVENT_RAISE_GHOUL, + EVENT_RESUME_WP + }; + + // Point ID for movement + enum Points + { + POINT_CORPSE_REACHED = 1 + }; + + void Reset() override + { + events.Reset(); + targetCorpseGUID.Clear(); + geistGUID.Clear(); + isOnRitual = false; + // Start waypoint movement using WaypointMovementGenerator + if (uint32 pathId = me->GetWaypointPath()) + { + me->GetMotionMaster()->MovePath(pathId, true); // true = repeatable + } + // Schedule the first ritual after 20-30s + events.ScheduleEvent(EVENT_START_RITUAL, urand(20000, 30000)); + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_START_RITUAL: + { + if (isOnRitual) // Already performing ritual + { + events.ScheduleEvent(EVENT_START_RITUAL, urand(5000, 10000)); + break; + } + + // Find nearest dead Scarlet humanoid (exclude gryphon) + Creature* nearestCorpse = nullptr; + float nearestDist = std::numeric_limits::max(); + static const uint32 corpseEntries[] = { + NPC_DEAD_SCARLET_MEDIC, NPC_DEAD_SCARLET_INFANTRYMAN, NPC_DEAD_SCARLET_CAPTAIN, + NPC_DEAD_SCARLET_PEASANT, NPC_DEAD_SCARLET_MINER, NPC_DEAD_SCARLET_FLEET_DEFENDER + }; + for (uint32 entry : corpseEntries) + { + // Search up to 60 yards for each type + if (Creature* corpse = me->FindNearestCreature(entry, 60.0f, true)) + { + float dist = me->GetDistance(corpse); + if (dist < nearestDist) + { + nearestDist = dist; + nearestCorpse = corpse; + } + } + } + if (!nearestCorpse) + { + // No corpse found nearby: try again later + events.ScheduleEvent(EVENT_START_RITUAL, urand(5000, 10000)); + break; + } + // Start ritual + isOnRitual = true; + targetCorpseGUID = nearestCorpse->GetGUID(); + geistGUID.Clear(); + // Pause waypoint movement and move to the corpse + me->PauseMovement(); + float x, y, z; + // Keep it at a distance from the corpse + nearestCorpse->GetClosePoint(x, y, z, me->GetObjectSize()); + me->GetMotionMaster()->MovePoint(POINT_CORPSE_REACHED, x, y, z); + break; + } + + case EVENT_GHOULPLOSION: + { + if (Creature* geist = ObjectAccessor::GetCreature(*me, geistGUID)) + { + me->SetFacingToObject(geist); + DoCast(geist, SPELL_GHOULPLOSION); + } + break; + } + + case EVENT_RAISE_GHOUL: + { + if (Creature* corpse = ObjectAccessor::GetCreature(*me, targetCorpseGUID)) + { + // Cast Scarlet Ghoul on the corpse (always a humanoid for necromancer) + me->SetFacingToObject(corpse); + DoCast(corpse, SPELL_SCARLET_GHOUL); + } + break; + } + + case EVENT_RESUME_WP: + { + // Resume waypoint movement + isOnRitual = false; + + targetCorpseGUID.Clear(); + + // Resume paused waypoint movement + me->ResumeMovement(); + // Schedule next ritual in 20-30s + events.ScheduleEvent(EVENT_START_RITUAL, urand(20000, 30000)); + break; + } + } + } + + // Necromancers are not expected to engage in combat; no melee UpdateAI needed beyond events. + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == POINT_CORPSE_REACHED) + { + // Reached the corpse + // Check for nearby Gluttonous Geist within ~3 yards + Creature* geist = me->FindNearestCreature(NPC_GLUTTONOUS_GEIST, 3.0f, true); + if (geist) + { + me->SetFacingToObject(geist); + geistGUID = geist->GetGUID(); + // Geist found: schedule Ghoulplosion at +3s, then raising at +6s, then resume at +9s + events.ScheduleEvent(EVENT_GHOULPLOSION, 3000); + events.ScheduleEvent(EVENT_RAISE_GHOUL, 6000); + events.ScheduleEvent(EVENT_RESUME_WP, 9000); + } + else + { + // No Geist: just raise after 3s, resume 3s later + + Creature* corpse = ObjectAccessor::GetCreature(*me, targetCorpseGUID); + if (corpse) + { + me->SetFacingToObject(corpse); + } + + events.ScheduleEvent(EVENT_RAISE_GHOUL, 3000); + events.ScheduleEvent(EVENT_RESUME_WP, 6000); + } + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_acherus_necromancerAI(creature); + } +}; + +/*###### +## npc_gothik_the_harvester (Entry 28890) +######*/ +class npc_gothik_the_harvester : public CreatureScript +{ +public: + npc_gothik_the_harvester() : CreatureScript("npc_gothik_the_harvester") { } + + struct npc_gothik_the_harvesterAI : public ScriptedAI + { + npc_gothik_the_harvesterAI(Creature* creature) : ScriptedAI(creature) { } + + EventMap events; + ObjectGuid targetCorpseGUID; + ObjectGuid geistGUID; + bool isOnRitual; + + enum Events + { + EVENT_START_RITUAL = 1, + EVENT_GHOULPLOSION, + EVENT_RAISE_DEAD, + EVENT_RESUME_WP + }; + + enum Points + { + POINT_CORPSE_REACHED = 1 + }; + + // Text identifiers for creature_text (see SQL below) + enum Says + { + SAY_GRYPHON = 0, // "You will fly again, beast..." + SAY_GHOUL = 1, // "Surprise, surprise! Another ghoul!" + SAY_GEIST = 2 // "Is Gothik the Harvester going to have to choke a geist?" + }; + + void Reset() override + { + events.Reset(); + targetCorpseGUID.Clear(); + geistGUID.Clear(); + isOnRitual = false; + // Start waypoint movement using WaypointMovementGenerator + if (uint32 pathId = me->GetWaypointPath()) + { + me->GetMotionMaster()->MovePath(pathId, true); // true = repeatable + } + // Schedule the first ritual after 50-60s + events.ScheduleEvent(EVENT_START_RITUAL, urand(50000, 60000)); + } + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + if (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_START_RITUAL: + { + if (isOnRitual) // Already performing ritual + { + events.ScheduleEvent(EVENT_START_RITUAL, urand(5000, 10000)); + break; + } + + // Find nearest dead Scarlet NPC (including gryphon) + Creature* nearestCorpse = nullptr; + float nearestDist = std::numeric_limits::max(); + static const uint32 corpseEntries[] = { + NPC_DEAD_SCARLET_MEDIC, NPC_DEAD_SCARLET_INFANTRYMAN, NPC_DEAD_SCARLET_CAPTAIN, + NPC_DEAD_SCARLET_PEASANT, NPC_DEAD_SCARLET_MINER, NPC_DEAD_SCARLET_FLEET_DEFENDER, + NPC_DEAD_SCARLET_GRYPHON + }; + for (uint32 entry : corpseEntries) + { + // Search up to 60 yards for each type + if (Creature* corpse = me->FindNearestCreature(entry, 60.0f, true)) + { + float dist = me->GetDistance(corpse); + if (dist < nearestDist) + { + nearestDist = dist; + nearestCorpse = corpse; + } + } + } + if (!nearestCorpse) + { + events.ScheduleEvent(EVENT_START_RITUAL, urand(5000, 10000)); + break; + } + // Start ritual + isOnRitual = true; + targetCorpseGUID = nearestCorpse->GetGUID(); + geistGUID.Clear(); + // Pause waypoint movement and move to the corpse + me->PauseMovement(); + float x, y, z; + // Keep it at a distance from the corpse + nearestCorpse->GetClosePoint(x, y, z, me->GetObjectSize()); + me->GetMotionMaster()->MovePoint(POINT_CORPSE_REACHED, x, y, z); + break; + } + case EVENT_GHOULPLOSION: + { + // Cast Ghoulplosion on the Geist and say the Geist line + if (Creature* geist = ObjectAccessor::GetCreature(*me, geistGUID)) + { + Talk(SAY_GEIST); + me->SetFacingToObject(geist); + DoCast(geist, SPELL_GHOULPLOSION); + } + break; + } + + case EVENT_RAISE_DEAD: + { + // Cast the appropriate raise spell on the corpse (griffon or ghoul) + if (Creature* corpse = ObjectAccessor::GetCreature(*me, targetCorpseGUID)) + { + me->SetFacingToObject(corpse); + uint32 entry = corpse->GetEntry(); + if (entry == NPC_DEAD_SCARLET_GRYPHON) + { + DoCast(corpse, SPELL_SCOURGE_GRYPHON); + } + else + { + DoCast(corpse, SPELL_SCARLET_GHOUL); + } + } + break; + } + case EVENT_RESUME_WP: + { + // Resume waypoint movement + isOnRitual = false; + targetCorpseGUID.Clear(); + // Resume paused waypoint movement + me->ResumeMovement(); + // Schedule next ritual in 50-60s + events.ScheduleEvent(EVENT_START_RITUAL, urand(50000, 60000)); + break; + } + } + } + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == POINT_CORPSE_REACHED) + { + // Reached the target corpse + Creature* corpse = ObjectAccessor::GetCreature(*me, targetCorpseGUID); + if (corpse) + { + me->SetFacingToObject(corpse); + // Say line depending on corpse type (gryphon or humanoid) + if (corpse->GetEntry() == NPC_DEAD_SCARLET_GRYPHON) + Talk(SAY_GRYPHON); + else + Talk(SAY_GHOUL); + } + // Check for Geist nearby + Creature* geist = me->FindNearestCreature(NPC_GLUTTONOUS_GEIST, 3.0f, true); + if (geist) + { + me->SetFacingToObject(geist); + geistGUID = geist->GetGUID(); + // Geist present: Ghoulplosion in 3s (with SAY_GEIST), raise in 6s, resume in 9s + events.ScheduleEvent(EVENT_GHOULPLOSION, 3000); + events.ScheduleEvent(EVENT_RAISE_DEAD, 6000); + events.ScheduleEvent(EVENT_RESUME_WP, 9000); + } + else + { + // No Geist: raise in 3s, resume in 6s + events.ScheduleEvent(EVENT_RAISE_DEAD, 3000); + events.ScheduleEvent(EVENT_RESUME_WP, 6000); + } + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_gothik_the_harvesterAI(creature); + } +}; + void AddSC_the_scarlet_enclave_c2() { new npc_crusade_persuaded(); new npc_scarlet_courier(); new npc_koltira_deathweaver(); new npc_a_special_surprise(); + new npc_acherus_necromancer(); + new npc_gothik_the_harvester(); } From e01286fcb24467a84be86f1982876b5e29d52884 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Jul 2025 14:40:07 +0000 Subject: [PATCH 18/43] chore(DB): import pending files Referenced commit(s): c581abc4cd255e643d4554d0ca515aef9d81bf1e --- .../Gryph_Ghouls_mechanic.sql => db_world/2025_07_01_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Gryph_Ghouls_mechanic.sql => db_world/2025_07_01_01.sql} (99%) diff --git a/data/sql/updates/pending_db_world/Gryph_Ghouls_mechanic.sql b/data/sql/updates/db_world/2025_07_01_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/Gryph_Ghouls_mechanic.sql rename to data/sql/updates/db_world/2025_07_01_01.sql index 7f61482cc..93de4440e 100644 --- a/data/sql/updates/pending_db_world/Gryph_Ghouls_mechanic.sql +++ b/data/sql/updates/db_world/2025_07_01_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_01_00 -> 2025_07_01_01 -- Death Knight Initiates (Remove Wrong Guids) DELETE FROM `creature` WHERE (`id1` = 28406) AND (`guid` IN (129516, 129517, 129518, 129544, 129545, 129555)); DELETE FROM `creature_addon` WHERE (`guid` IN (129516, 129517, 129518, 129544, 129545, 129555)); From 77529b0769ff88e5c0cb41219835b3c2e1a022ec Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Tue, 1 Jul 2025 16:41:46 +0200 Subject: [PATCH 19/43] fix (DB/Creature) Elite npcs no longer die during The Light of Dawn battle. (#22402) --- .../sql/updates/pending_db_world/Npcs_Lod.sql | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Npcs_Lod.sql diff --git a/data/sql/updates/pending_db_world/Npcs_Lod.sql b/data/sql/updates/pending_db_world/Npcs_Lod.sql new file mode 100644 index 000000000..01d6f2572 --- /dev/null +++ b/data/sql/updates/pending_db_world/Npcs_Lod.sql @@ -0,0 +1,159 @@ + +-- Korfax, Champion of the Light +DELETE FROM `creature_text` WHERE (`CreatureID` = 29176) AND (`GroupID` = 0); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(29176, 0, 0, '%s falls unconscious.', 41, 0, 100, 0, 0, 0, 29725, 0, ''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29176; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29176); +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 +(29176, 0, 0, 1, 2, 0, 100, 0, 0, 3, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - Between 0-3% Health - Stop Combat'), +(29176, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - Between 0-3% Health - Set Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(29176, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - Between 0-3% Health - Set Flag Standstate Dead'), +(29176, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 65, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - Between 0-3% Health - Set Emote State 65'), +(29176, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - Between 0-3% Health - Say Line 0'), +(29176, 0, 5, 6, 72, 0, 100, 1, 2, 0, 0, 0, 0, 0, 19, 33554432, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - On Action 2 Done - Remove Flags Not Selectable (No Repeat)'), +(29176, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 91, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - On Action 2 Done - Remove FlagStandstate Dead (No Repeat)'), +(29176, 0, 7, 8, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - On Action 2 Done - Set Emote State 0 (No Repeat)'), +(29176, 0, 8, 0, 61, 0, 100, 1, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2285.7, -5303.2, 85.92, 1.59, 'Korfax, Champion of the Light - On Action 2 Done - Move To Position (No Repeat)'), +(29176, 0, 9, 10, 34, 0, 100, 513, 8, 2, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2285.7, -5303.2, 85.92, 1.59, 'Korfax, Champion of the Light - On Reached Point 2 - Set Orientation 1.59 (No Repeat)'), +(29176, 0, 10, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 90, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - On Reached Point 2 - Set Flag Standstate Kneel (No Repeat)'), +(29176, 0, 11, 0, 0, 0, 100, 0, 10000, 20000, 10000, 10000, 0, 0, 11, 53631, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - In Combat - Cast \'Cleave\''), +(29176, 0, 12, 0, 0, 0, 100, 0, 10000, 20000, 10000, 10000, 0, 0, 11, 53625, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - In Combat - Cast \'Heroic Leap\''), +(29176, 0, 13, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 42, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Korfax, Champion of the Light - On Respawn - Set Invincibility Hp 1%'); + +-- Commander Eligor Dawnbringer +DELETE FROM `creature_text` WHERE (`CreatureID` = 29177) AND (`GroupID` = 0); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(29177, 0, 0, '%s falls unconscious.', 41, 0, 100, 0, 0, 0, 29725, 0, ''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29177; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29177); +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 +(29177, 0, 0, 1, 2, 0, 100, 0, 0, 3, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - Between 0-3% Health - Stop Combat'), +(29177, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - Between 0-3% Health - Set Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(29177, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - Between 0-3% Health - Set Flag Standstate Dead'), +(29177, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 65, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - Between 0-3% Health - Set Emote State 65'), +(29177, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - Between 0-3% Health - Say Line 0'), +(29177, 0, 5, 6, 72, 0, 100, 1, 2, 0, 0, 0, 0, 0, 19, 33554432, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - On Action 2 Done - Remove Flags Not Selectable (No Repeat)'), +(29177, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 91, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - On Action 2 Done - Remove FlagStandstate Dead (No Repeat)'), +(29177, 0, 7, 8, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - On Action 2 Done - Set Emote State 0 (No Repeat)'), +(29177, 0, 8, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2276.13, -5312, 87.09, 1.47, 'Commander Eligor Dawnbringer - On Action 2 Done - Move To Position (No Repeat)'), +(29177, 0, 9, 0, 34, 0, 100, 513, 8, 2, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2276.13, -5312, 87.09, 1.47, 'Commander Eligor Dawnbringer - On Reached Point 2 - Set Orientation 1.47 (No Repeat)'), +(29177, 0, 10, 0, 74, 0, 100, 0, 0, 0, 5000, 10000, 20, 0, 11, 37979, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - On Friendly Below 20% Health - Cast \'Holy Light\''), +(29177, 0, 11, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 42, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Commander Eligor Dawnbringer - On Respawn - Set Invincibility Hp 1%'); + +-- Lord Maxwell Tyrosus +DELETE FROM `creature_text` WHERE (`CreatureID` = 29178) AND (`GroupID` = 0); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(29178, 0, 0, '%s falls unconscious.', 41, 0, 100, 0, 0, 0, 29725, 0, ''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29178; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29178); +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 +(29178, 0, 0, 1, 2, 0, 100, 0, 0, 3, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - Between 0-3% Health - Stop Combat'), +(29178, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - Between 0-3% Health - Set Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(29178, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - Between 0-3% Health - Set Flag Standstate Dead'), +(29178, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 65, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - Between 0-3% Health - Set Emote State 65'), +(29178, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - Between 0-3% Health - Say Line 0'), +(29178, 0, 5, 6, 72, 0, 100, 1, 2, 0, 0, 0, 0, 0, 19, 33554432, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - On Action 2 Done - Remove Flags Not Selectable (No Repeat)'), +(29178, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 91, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - On Action 2 Done - Remove FlagStandstate Dead (No Repeat)'), +(29178, 0, 7, 8, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - On Action 2 Done - Set Emote State 0 (No Repeat)'), +(29178, 0, 8, 0, 61, 0, 100, 1, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2276.3, -5305.75, 85.89, 1.54, 'Lord Maxwell Tyrosus - On Action 2 Done - Move To Position (No Repeat)'), +(29178, 0, 9, 10, 34, 0, 100, 513, 8, 2, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2276.3, -5305.75, 85.89, 1.54, 'Lord Maxwell Tyrosus - On Reached Point 2 - Set Orientation 1.54 (No Repeat)'), +(29178, 0, 10, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 90, 8, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - On Reached Point 2 - Set Flag Standstate Kneel (No Repeat)'), +(29178, 0, 11, 0, 11, 0, 100, 1, 0, 0, 0, 0, 0, 0, 1, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - On Respawn - Say Line 7 (No Repeat)'), +(29178, 0, 12, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 42, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Lord Maxwell Tyrosus - On Respawn - Set Invincibility Hp 1%'); + +-- Leonid Barthalomew the Revered +DELETE FROM `creature_text` WHERE (`CreatureID` = 29179) AND (`GroupID` = 0); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(29179, 0, 0, '%s falls unconscious.', 41, 0, 100, 0, 0, 0, 29725, 0, ''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29179; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29179); +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 +(29179, 0, 0, 1, 2, 0, 100, 0, 0, 3, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - Between 0-3% Health - Stop Combat'), +(29179, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - Between 0-3% Health - Set Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(29179, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - Between 0-3% Health - Set Flag Standstate Dead'), +(29179, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 65, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - Between 0-3% Health - Set Emote State 65'), +(29179, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - Between 0-3% Health - Say Line 0'), +(29179, 0, 5, 6, 72, 0, 100, 1, 2, 0, 0, 0, 0, 0, 19, 33554432, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - On Action 2 Done - Remove Flags Not Selectable (No Repeat)'), +(29179, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 91, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - On Action 2 Done - Remove FlagStandstate Dead (No Repeat)'), +(29179, 0, 7, 8, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - On Action 2 Done - Set Emote State 0 (No Repeat)'), +(29179, 0, 8, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2276.32, -5319.22, 88.55, 1.47, 'Leonid Barthalomew the Revered - On Action 2 Done - Move To Position (No Repeat)'), +(29179, 0, 9, 0, 34, 0, 100, 513, 8, 2, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2276.32, -5319.22, 88.55, 1.47, 'Leonid Barthalomew the Revered - On Reached Point 2 - Set Orientation 1.47 (No Repeat)'), +(29179, 0, 10, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 42, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Leonid Barthalomew the Revered - On Respawn - Set Invincibility Hp 1%'); + +-- Duke Nicholas Zverenhoff +DELETE FROM `creature_text` WHERE (`CreatureID` = 29180) AND (`GroupID` = 0); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(29180, 0, 0, '%s falls unconscious.', 41, 0, 100, 0, 0, 0, 29725, 0, ''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29180; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29180); +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 +(29180, 0, 0, 1, 2, 0, 100, 0, 0, 3, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - Between 0-3% Health - Stop Combat'), +(29180, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - Between 0-3% Health - Set Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(29180, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - Between 0-3% Health - Set Flag Standstate Dead'), +(29180, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 65, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - Between 0-3% Health - Set Emote State 65'), +(29180, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - Between 0-3% Health - Say Line 0'), +(29180, 0, 5, 6, 72, 0, 100, 1, 2, 0, 0, 0, 0, 0, 19, 33554432, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - On Action 2 Done - Remove Flags Not Selectable (No Repeat)'), +(29180, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 91, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - On Action 2 Done - Remove FlagStandstate Dead (No Repeat)'), +(29180, 0, 7, 8, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - On Action 2 Done - Set Emote State 0 (No Repeat)'), +(29180, 0, 8, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2285.49, -5312.6, 88.01, 1.5, 'Duke Nicholas Zverenhoff - On Action 2 Done - Move To Position (No Repeat)'), +(29180, 0, 9, 0, 34, 0, 100, 513, 8, 2, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2285.49, -5312.6, 88.01, 1.5, 'Duke Nicholas Zverenhoff - On Reached Point 2 - Set Orientation 1.5 (No Repeat)'), +(29180, 0, 10, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 42, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Duke Nicholas Zverenhoff - On Respawn - Set Invincibility Hp 1%'); + +-- Rayne +DELETE FROM `creature_text` WHERE (`CreatureID` = 29181) AND (`GroupID` = 0); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(29181, 0, 0, '%s falls unconscious.', 41, 0, 100, 0, 0, 0, 29725, 0, ''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29181; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29181); +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 +(29181, 0, 0, 1, 2, 0, 100, 0, 0, 3, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - Between 0-3% Health - Stop Combat'), +(29181, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - Between 0-3% Health - Set Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(29181, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - Between 0-3% Health - Set Flag Standstate Dead'), +(29181, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 65, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - Between 0-3% Health - Set Emote State 65'), +(29181, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - Between 0-3% Health - Say Line 0'), +(29181, 0, 5, 6, 72, 0, 100, 1, 2, 0, 0, 0, 0, 0, 19, 33554432, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - On Action 2 Done - Remove Flags Not Selectable (No Repeat)'), +(29181, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 91, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - On Action 2 Done - Remove FlagStandstate Dead (No Repeat)'), +(29181, 0, 7, 8, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - On Action 2 Done - Set Emote State 0 (No Repeat)'), +(29181, 0, 8, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2272.7, -5315.06, 87.14, 1.31, 'Rayne - On Action 2 Done - Move To Position (No Repeat)'), +(29181, 0, 9, 0, 34, 0, 100, 513, 8, 2, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2272.7, -5315.06, 87.14, 1.31, 'Rayne - On Reached Point 2 - Set Orientation 1.31 (No Repeat)'), +(29181, 0, 10, 0, 0, 0, 100, 0, 10000, 20000, 10000, 20000, 0, 0, 11, 20687, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - In Combat - Cast \'Starfall\''), +(29181, 0, 11, 0, 0, 0, 100, 0, 10000, 20000, 10000, 20000, 0, 0, 11, 21807, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - In Combat - Cast \'Wrath\''), +(29181, 0, 12, 13, 74, 0, 100, 0, 0, 0, 5000, 10000, 20, 0, 11, 20664, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - On Friendly Below 20% Health - Cast \'Rejuvenation\''), +(29181, 0, 13, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 25817, 1, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - On Friendly Below 0% Health - Cast \'Tranquility\''), +(29181, 0, 14, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 42, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rayne - On Respawn - Set Invincibility Hp 1%'); + +-- Rimblat Earthshatter +DELETE FROM `creature_text` WHERE (`CreatureID` = 29182) AND (`GroupID` = 0); +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(29182, 0, 0, '%s falls unconscious.', 41, 0, 100, 0, 0, 0, 29725, 0, ''); + +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29182; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29182); +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 +(29182, 0, 0, 1, 2, 0, 100, 0, 0, 3, 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - Between 0-3% Health - Stop Combat'), +(29182, 0, 1, 2, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 18, 33555200, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - Between 0-3% Health - Set Flags Immune To Players & Immune To NPC\'s & Not Selectable'), +(29182, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 90, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - Between 0-3% Health - Set Flag Standstate Dead'), +(29182, 0, 3, 4, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 65, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - Between 0-3% Health - Set Emote State 65'), +(29182, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - Between 0-3% Health - Say Line 0'), +(29182, 0, 5, 6, 72, 0, 100, 1, 2, 0, 0, 0, 0, 0, 19, 33554432, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - On Action 2 Done - Remove Flags Not Selectable (No Repeat)'), +(29182, 0, 6, 7, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 91, 7, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - On Action 2 Done - Remove FlagStandstate Dead (No Repeat)'), +(29182, 0, 7, 8, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - On Action 2 Done - Set Emote State 0 (No Repeat)'), +(29182, 0, 8, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 69, 2, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2279.26, -5316.37, 88.27, 1.52, 'Rimblat Earthshatter - On Action 2 Done - Move To Position (No Repeat)'), +(29182, 0, 9, 0, 34, 0, 100, 513, 8, 2, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 2279.26, -5316.37, 88.27, 1.52, 'Rimblat Earthshatter - On Reached Point 2 - Set Orientation 1.52 (No Repeat)'), +(29182, 0, 10, 0, 0, 0, 100, 0, 10000, 20000, 10000, 10000, 0, 0, 11, 53630, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - In Combat - Cast \'Thunder\''), +(29182, 0, 11, 0, 74, 0, 100, 0, 0, 0, 5000, 10000, 20, 0, 11, 33642, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - On Friendly Below 20% Health - Cast \'Chain Heal\''), +(29182, 0, 12, 0, 11, 0, 100, 0, 0, 0, 0, 0, 0, 0, 42, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Rimblat Earthshatter - On Respawn - Set Invincibility Hp 1%'); From fcda60bbea9c440d83c64899b8a4be9c97a2cc4b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Jul 2025 14:42:57 +0000 Subject: [PATCH 20/43] chore(DB): import pending files Referenced commit(s): 77529b0769ff88e5c0cb41219835b3c2e1a022ec --- .../Npcs_Lod.sql => db_world/2025_07_01_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Npcs_Lod.sql => db_world/2025_07_01_02.sql} (99%) diff --git a/data/sql/updates/pending_db_world/Npcs_Lod.sql b/data/sql/updates/db_world/2025_07_01_02.sql similarity index 99% rename from data/sql/updates/pending_db_world/Npcs_Lod.sql rename to data/sql/updates/db_world/2025_07_01_02.sql index 01d6f2572..6666ef291 100644 --- a/data/sql/updates/pending_db_world/Npcs_Lod.sql +++ b/data/sql/updates/db_world/2025_07_01_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_01_01 -> 2025_07_01_02 -- Korfax, Champion of the Light DELETE FROM `creature_text` WHERE (`CreatureID` = 29176) AND (`GroupID` = 0); From a04375e4fee90b4934f574c454c0b1b114963dc4 Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Tue, 1 Jul 2025 20:37:02 +0200 Subject: [PATCH 21/43] fix (DB/Creature) A couple of simple SmartAI corrections for Scarlet Ghouls and Scourge Gryphons. (#22405) --- .../Gryp_Ghou_Corrections.sql | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Gryp_Ghou_Corrections.sql diff --git a/data/sql/updates/pending_db_world/Gryp_Ghou_Corrections.sql b/data/sql/updates/pending_db_world/Gryp_Ghou_Corrections.sql new file mode 100644 index 000000000..296d3c39b --- /dev/null +++ b/data/sql/updates/pending_db_world/Gryp_Ghou_Corrections.sql @@ -0,0 +1,21 @@ + +-- Scourge Gryphons +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28906; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28906); +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 +(28906, 0, 0, 1, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scourge Gryphon - On Just Summoned - Set Reactstate Passive'), +(28906, 0, 1, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 87, 2890600, 2890601, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scourge Gryphon - On Just Summoned - Run Random Script'), +(28906, 0, 2, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 37, 300000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scourge Gryphon - On Just Summoned - Kill Self'), +(28906, 0, 3, 4, 34, 0, 100, 0, 8, 1, 0, 0, 0, 0, 233, 2890600, 2890604, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scourge Gryphon - On Reached Point 1 - Start Random Path 2890600-2890604'), +(28906, 0, 4, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scourge Gryphon - On Reached Point 1 - Set Reactstate Aggressive'); + +-- Scarlet Ghouls +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28897; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28897); +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 +(28897, 0, 0, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 80, 2889700, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - On Just Summoned - Run Script'), +(28897, 0, 1, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 37, 300000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - On Just Summoned - Kill Self'), +(28897, 0, 2, 0, 58, 0, 100, 0, 0, 0, 0, 0, 0, 0, 89, 30, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - On Path 0 Finished - Start Random Movement'), +(28897, 0, 3, 0, 25, 0, 100, 0, 0, 0, 0, 0, 0, 0, 205, 30, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Scarlet Ghoul - On Reset - Set combat distance to 30'); From 273607b38ab14ef3d5beaf2a9fec2f02321bdbcf Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 1 Jul 2025 18:38:05 +0000 Subject: [PATCH 22/43] chore(DB): import pending files Referenced commit(s): a04375e4fee90b4934f574c454c0b1b114963dc4 --- .../Gryp_Ghou_Corrections.sql => db_world/2025_07_01_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Gryp_Ghou_Corrections.sql => db_world/2025_07_01_03.sql} (98%) diff --git a/data/sql/updates/pending_db_world/Gryp_Ghou_Corrections.sql b/data/sql/updates/db_world/2025_07_01_03.sql similarity index 98% rename from data/sql/updates/pending_db_world/Gryp_Ghou_Corrections.sql rename to data/sql/updates/db_world/2025_07_01_03.sql index 296d3c39b..a77186f06 100644 --- a/data/sql/updates/pending_db_world/Gryp_Ghou_Corrections.sql +++ b/data/sql/updates/db_world/2025_07_01_03.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_01_02 -> 2025_07_01_03 -- Scourge Gryphons UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 28906; From 12328920ddebfdce99faf3ed99e3a16fcda955f4 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Tue, 1 Jul 2025 14:44:48 -0400 Subject: [PATCH 23/43] fix(Core/Reputation): Remove `At War` flag when rising above hated reputation. (#22386) --- src/server/game/Reputation/ReputationMgr.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index 497d5078e..2aae16a1c 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -432,6 +432,9 @@ bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, fl if (new_rank <= REP_HOSTILE) SetAtWar(&itr->second, true); + if (old_rank == REP_HOSTILE && new_rank >= REP_UNFRIENDLY && factionEntry->CanBeSetAtWar()) + SetAtWar(&itr->second, false); + if (new_rank > old_rank) _sendFactionIncreased = true; From 8dccc64668137457a29e756cc03c7a9d121d661d Mon Sep 17 00:00:00 2001 From: Rocco Silipo <108557877+Rorschach91@users.noreply.github.com> Date: Wed, 2 Jul 2025 09:02:52 +0200 Subject: [PATCH 24/43] fix (DB/Creature) Tirion doesn't get back on his horse anymore during The Light of Dawn event. (#22406) --- data/sql/updates/pending_db_world/Tirion_Mount.sql | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 data/sql/updates/pending_db_world/Tirion_Mount.sql diff --git a/data/sql/updates/pending_db_world/Tirion_Mount.sql b/data/sql/updates/pending_db_world/Tirion_Mount.sql new file mode 100644 index 000000000..9bb88c5ae --- /dev/null +++ b/data/sql/updates/pending_db_world/Tirion_Mount.sql @@ -0,0 +1,12 @@ + +-- Remove Mount from creature_template_addon +UPDATE `creature_template_addon` SET `mount` = 0 WHERE (`entry` = 29175); + +-- Edit SmartAI +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 29175; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 29175); +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 +(29175, 0, 0, 0, 54, 0, 100, 0, 0, 0, 0, 0, 0, 0, 43, 0, 14338, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Highlord Tirion Fordring - On Just Summoned - Mount To Model 14338'), +(29175, 0, 1, 2, 109, 0, 100, 0, 0, 0, 0, 0, 0, 0, 206, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Highlord Tirion Fordring - On Path 0 Finished - Dismount'), +(29175, 0, 2, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 1.69297, 'Highlord Tirion Fordring - On Path 0 Finished - Set Orientation 1.69297'); From ab38edb81b16c3aae299d8d59ddebf761dbd9ad9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 2 Jul 2025 07:03:53 +0000 Subject: [PATCH 25/43] chore(DB): import pending files Referenced commit(s): 8dccc64668137457a29e756cc03c7a9d121d661d --- .../Tirion_Mount.sql => db_world/2025_07_02_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/Tirion_Mount.sql => db_world/2025_07_02_00.sql} (96%) diff --git a/data/sql/updates/pending_db_world/Tirion_Mount.sql b/data/sql/updates/db_world/2025_07_02_00.sql similarity index 96% rename from data/sql/updates/pending_db_world/Tirion_Mount.sql rename to data/sql/updates/db_world/2025_07_02_00.sql index 9bb88c5ae..eadd24645 100644 --- a/data/sql/updates/pending_db_world/Tirion_Mount.sql +++ b/data/sql/updates/db_world/2025_07_02_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_01_03 -> 2025_07_02_00 -- Remove Mount from creature_template_addon UPDATE `creature_template_addon` SET `mount` = 0 WHERE (`entry` = 29175); From ba7407b56283b2a97cfbd0feda4f085153f8d100 Mon Sep 17 00:00:00 2001 From: Andrew <47818697+Nyeriah@users.noreply.github.com> Date: Wed, 2 Jul 2025 20:42:44 -0300 Subject: [PATCH 26/43] =?UTF-8?q?fix(DB/SAI:=20Fix=20Archerus=20Deathcharg?= =?UTF-8?q?er=20not=20despawning=20if=20players=20move=20=E2=80=A6=20(#224?= =?UTF-8?q?11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pending_db_world/rev_1751498063531200700.sql | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1751498063531200700.sql diff --git a/data/sql/updates/pending_db_world/rev_1751498063531200700.sql b/data/sql/updates/pending_db_world/rev_1751498063531200700.sql new file mode 100644 index 000000000..877c74918 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1751498063531200700.sql @@ -0,0 +1,14 @@ +-- +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28788) AND (`source_type` = 0) AND (`id` IN (5)); +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 +(28788, 0, 5, 6, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 203, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 0, 0, 0, 0, 0, 'Salanar the Horseman - On Update - Exit vehicle (No Repeat)'); + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 28782); +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 +(28782, 0, 0, 0, 27, 0, 100, 512, 0, 0, 0, 0, 0, 0, 103, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Acherus Deathcharger - On Passenger Boarded - Set Rooted Off'), +(28782, 0, 2, 3, 28, 0, 100, 0, 0, 0, 0, 0, 0, 0, 5, 377, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Acherus Deathcharger - On Passenger Removed - Play Emote 377'), +(28782, 0, 3, 0, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 3000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Acherus Deathcharger - On Passenger Removed - Despawn In 3000 ms'); + +DELETE FROM `conditions` WHERE (`SourceTypeOrReferenceId` = 22) AND (`SourceGroup` = 3) AND (`SourceEntry` = 28782) AND (`SourceId` = 0) AND (`ElseGroup` = 0) AND (`ConditionTypeOrReference` = 32) AND (`ConditionTarget` = 0) AND (`ConditionValue1` = 16) AND (`ConditionValue2` = 0) AND (`ConditionValue3` = 0); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(22, 3, 28782, 0, 0, 32, 0, 16, 0, 0, 0, 0, 0, '', 'Only despawn Archerus Deathcharger if dismounting unit is player'); From 10e1f3af8cac2bea91d9cd27158fb58ffa1029c5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 2 Jul 2025 23:43:46 +0000 Subject: [PATCH 27/43] chore(DB): import pending files Referenced commit(s): ba7407b56283b2a97cfbd0feda4f085153f8d100 --- .../rev_1751498063531200700.sql => db_world/2025_07_02_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1751498063531200700.sql => db_world/2025_07_02_01.sql} (98%) diff --git a/data/sql/updates/pending_db_world/rev_1751498063531200700.sql b/data/sql/updates/db_world/2025_07_02_01.sql similarity index 98% rename from data/sql/updates/pending_db_world/rev_1751498063531200700.sql rename to data/sql/updates/db_world/2025_07_02_01.sql index 877c74918..ecc0d5d0d 100644 --- a/data/sql/updates/pending_db_world/rev_1751498063531200700.sql +++ b/data/sql/updates/db_world/2025_07_02_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_02_00 -> 2025_07_02_01 -- DELETE FROM `smart_scripts` WHERE (`entryorguid` = 28788) AND (`source_type` = 0) AND (`id` IN (5)); 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 780c24044018ee2d9d30a25f7ad57e0cf2108834 Mon Sep 17 00:00:00 2001 From: Ryan Turner Date: Thu, 3 Jul 2025 07:06:19 +0100 Subject: [PATCH 28/43] fix(DB/SAI) - Corrects Corpse.Decay for Patchwerk from the end of the Death Knight zone (#22412) --- data/sql/updates/pending_db_world/rev_1751514394329775600.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1751514394329775600.sql diff --git a/data/sql/updates/pending_db_world/rev_1751514394329775600.sql b/data/sql/updates/pending_db_world/rev_1751514394329775600.sql new file mode 100644 index 000000000..e70b5ef8a --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1751514394329775600.sql @@ -0,0 +1,4 @@ +-- Forces dispawn ignoring the "boss" rank for corpseDecay, his body should dispawn 20 seconds of dying and spawn 2 mins after +DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `entryorguid` = 31099 AND `id` = 3; +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 +(31099, 0, 3, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 41, 20000, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Patchwerk - On Just Died - Despawn In 20000 ms'); From 583c1156608ea355200d9aaefd982371b6e3941d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Jul 2025 06:07:22 +0000 Subject: [PATCH 29/43] chore(DB): import pending files Referenced commit(s): 780c24044018ee2d9d30a25f7ad57e0cf2108834 --- .../rev_1751514394329775600.sql => db_world/2025_07_03_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1751514394329775600.sql => db_world/2025_07_03_00.sql} (95%) diff --git a/data/sql/updates/pending_db_world/rev_1751514394329775600.sql b/data/sql/updates/db_world/2025_07_03_00.sql similarity index 95% rename from data/sql/updates/pending_db_world/rev_1751514394329775600.sql rename to data/sql/updates/db_world/2025_07_03_00.sql index e70b5ef8a..fbadbcb08 100644 --- a/data/sql/updates/pending_db_world/rev_1751514394329775600.sql +++ b/data/sql/updates/db_world/2025_07_03_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_02_01 -> 2025_07_03_00 -- Forces dispawn ignoring the "boss" rank for corpseDecay, his body should dispawn 20 seconds of dying and spawn 2 mins after DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `entryorguid` = 31099 AND `id` = 3; 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 04547d90b3a9b0b530c867467562945bd089ef0d Mon Sep 17 00:00:00 2001 From: Yehonal Date: Thu, 3 Jul 2025 11:03:16 +0200 Subject: [PATCH 30/43] fix(CI): update build job to support multiple Ubuntu versions (#22413) --- .github/workflows/dashboard-ci.yml | 16 ++++++++++++++-- apps/compiler/includes/functions.sh | 6 +++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/.github/workflows/dashboard-ci.yml b/.github/workflows/dashboard-ci.yml index 5e56cb1c8..25843a690 100644 --- a/.github/workflows/dashboard-ci.yml +++ b/.github/workflows/dashboard-ci.yml @@ -26,7 +26,13 @@ env: jobs: test-bash-scripts: name: Test Bash Scripts - runs-on: ubuntu-24.04 + strategy: + fail-fast: true + matrix: + include: + - os: ubuntu-22.04 + - os: ubuntu-24.04 + runs-on: ${{ matrix.os }} if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft steps: - name: Checkout repository @@ -48,7 +54,13 @@ jobs: build-and-test: name: Build and Integration Test - runs-on: ubuntu-24.04 + strategy: + fail-fast: true + matrix: + include: + - os: ubuntu-22.04 + - os: ubuntu-24.04 + runs-on: ${{ matrix.os }} if: github.repository == 'azerothcore/azerothcore-wotlk' && !github.event.pull_request.draft steps: - name: Checkout repository diff --git a/apps/compiler/includes/functions.sh b/apps/compiler/includes/functions.sh index 229991818..5f9581e7c 100644 --- a/apps/compiler/includes/functions.sh +++ b/apps/compiler/includes/functions.sh @@ -148,11 +148,11 @@ function comp_compile() { find "$AC_BINPATH_FULL" -mindepth 1 -maxdepth 1 -type f -exec $SUDO chmod u+s -- {} + [[ -f "$confDir/worldserver.conf.dist" ]] && \ - cp -v --update=none "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf" + cp -v --no-clobber "$confDir/worldserver.conf.dist" "$confDir/worldserver.conf" [[ -f "$confDir/authserver.conf.dist" ]] && \ - cp -v --update=none "$confDir/authserver.conf.dist" "$confDir/authserver.conf" + cp -v --no-clobber "$confDir/authserver.conf.dist" "$confDir/authserver.conf" [[ -f "$confDir/dbimport.conf.dist" ]] && \ - cp -v --update=none "$confDir/dbimport.conf.dist" "$confDir/dbimport.conf" + cp -v --no-clobber "$confDir/dbimport.conf.dist" "$confDir/dbimport.conf" echo "Done" ;; From f0a38b30916dae2560af36693c6b84e255829712 Mon Sep 17 00:00:00 2001 From: IntelligentQuantum Date: Thu, 3 Jul 2025 17:16:02 +0330 Subject: [PATCH 31/43] feat(Core/LFG): RDF CF (#22360) --- src/server/game/DungeonFinding/LFGMgr.cpp | 31 +++++++++++++++-------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index b8c434f10..fd59cb6eb 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -1495,6 +1495,11 @@ namespace lfg for (LfgLockMap::const_iterator it2 = cachedLockMap.begin(); it2 != cachedLockMap.end() && !dungeons.empty(); ++it2) { uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids + + // Skip faction-specific locks if cross-faction is enabled + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP) && (it2->second == LFG_LOCKSTATUS_QUEST_NOT_COMPLETED || it2->second == LFG_LOCKSTATUS_MISSING_ITEM)) + continue; + LfgDungeonSet::iterator itDungeon = dungeons.find(dungeonId); if (itDungeon != dungeons.end()) { @@ -1670,11 +1675,8 @@ namespace lfg } else if (group != grp) { - // pussywizard: if (!grp->IsFull()) grp->AddMember(player); - //else // some cleanup? LeaveLFG? - // ; } grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role); @@ -2685,17 +2687,24 @@ namespace lfg LFGQueue& LFGMgr::GetQueue(ObjectGuid guid) { uint8 queueId = 0; - if (guid.IsGroup()) + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) { - LfgGuidSet const& players = GetPlayers(guid); - ObjectGuid pguid = players.empty() ? ObjectGuid::Empty : (*players.begin()); - if (pguid) - queueId = GetTeam(pguid); - else - queueId = GetTeam(GetLeader(guid)); + queueId = TEAM_ALLIANCE; } else - queueId = GetTeam(guid); + { + if (guid.IsGroup()) + { + LfgGuidSet const& players = GetPlayers(guid); + ObjectGuid pguid = players.empty() ? ObjectGuid::Empty : (*players.begin()); + if (pguid) + queueId = GetTeam(pguid); + else + queueId = GetTeam(GetLeader(guid)); + } + else + queueId = GetTeam(guid); + } return QueuesStore[queueId]; } From 7561147e7c9ceccdbed5e1961c59bd37225993f1 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Thu, 3 Jul 2025 17:35:06 +0200 Subject: [PATCH 32/43] fix(Core/Spells): No longer add a signature to items created by NPCs (#22415) --- src/server/game/Entities/Item/ItemTemplate.h | 3 +-- src/server/game/Spells/SpellEffects.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 1550b3883..29831d235 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -700,8 +700,7 @@ struct ItemTemplate return GetMaxStackSize() == 1 && Class != ITEM_CLASS_CONSUMABLE && Class != ITEM_CLASS_QUEST && - !HasFlag(ITEM_FLAG_NO_CREATOR) && - ItemId != 6948; /*Hearthstone*/ + !HasFlag(ITEM_FLAG_NO_CREATOR); } [[nodiscard]] bool CanChangeEquipStateInCombat() const diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index d176f869b..74a1a1bba 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1759,7 +1759,7 @@ void Spell::DoCreateItem(uint8 /*effIndex*/, uint32 itemId) } // set the "Crafted by ..." property of the item - if (pItem->GetTemplate()->HasSignature()) + if (m_caster->IsPlayer() && pItem->GetTemplate()->HasSignature()) pItem->SetGuidValue(ITEM_FIELD_CREATOR, player->GetGUID()); // send info to the client From 0628ea53bbae9e4538498f79ca45780d0b9d61bb Mon Sep 17 00:00:00 2001 From: Macs-Account Date: Thu, 3 Jul 2025 09:41:30 -0600 Subject: [PATCH 33/43] fix(Core/Unit): Add previously unreferenced DealDamage script hook in Unit (#22207) --- src/server/game/Entities/Unit/Unit.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 924ddae88..79bd8fe2a 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -810,6 +810,7 @@ void Unit::DealDamageMods(Unit const* victim, uint32& damage, uint32* absorb) uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellInfo const* spellProto, bool durabilityLoss, bool /*allowGM*/, Spell const* damageSpell /*= nullptr*/) { + damage = sScriptMgr->DealDamage(attacker, victim, damage, damagetype); // Xinef: initialize damage done for rage calculations // Xinef: its rare to modify damage in hooks, however training dummy's sets damage to 0 uint32 rage_damage = damage + ((cleanDamage != nullptr) ? cleanDamage->absorbed_damage : 0); From dba7f694edead31012aa12763f5822446d852da2 Mon Sep 17 00:00:00 2001 From: Benjamin Jackson <38561765+heyitsbench@users.noreply.github.com> Date: Thu, 3 Jul 2025 11:41:52 -0400 Subject: [PATCH 34/43] fix(Scripts/Spells): Don't proc Misdirection from Mend Pet's heal effect. (#22293) --- src/server/scripts/Spells/spell_hunter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index f6195cc10..0f4013298 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -887,8 +887,9 @@ class spell_hun_misdirection : public AuraScript bool CheckProc(ProcEventInfo& eventInfo) { // Do not trigger from Mend Pet - if (eventInfo.GetProcSpell() && (eventInfo.GetProcSpell()->GetSpellInfo()->SpellFamilyFlags[0] & 0x800000)) + if ((eventInfo.GetProcSpell() && (eventInfo.GetProcSpell()->GetSpellInfo()->SpellFamilyFlags[0] & 0x800000)) || (eventInfo.GetHealInfo() && (eventInfo.GetHealInfo()->GetSpellInfo()->SpellFamilyFlags[0] & 0x800000))) return false; + return GetTarget()->GetRedirectThreatTarget(); } From 206589636eb6f9641b898cbc7943846580d3ee2d Mon Sep 17 00:00:00 2001 From: Ryan Turner Date: Thu, 3 Jul 2025 16:42:22 +0100 Subject: [PATCH 35/43] fix(DB/ArathiBasin): Replacement for previously lost tabards added and adjustements to quests (#22338) --- .../rev_1750443101763711500.sql | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1750443101763711500.sql diff --git a/data/sql/updates/pending_db_world/rev_1750443101763711500.sql b/data/sql/updates/pending_db_world/rev_1750443101763711500.sql new file mode 100644 index 000000000..efa05b347 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1750443101763711500.sql @@ -0,0 +1,83 @@ +-- Bracket 20-29 | `The Battle for Arathi Basin!` (1/4) (Alliance and Horde). +UPDATE `quest_template_addon` SET `MaxLevel` = 29 WHERE `ID` IN (8168, 8171); + +-- Bracket 30-39 | `The Battle for Arathi Basin!` (2/4) (Alliance and Horde). +UPDATE `quest_template_addon` SET `MaxLevel` = 39 WHERE `ID` IN (8167, 8170); + +-- Bracket 40-49 | `The Battle for Arathi Basin!` (3/4) (Alliance and Horde). +UPDATE `quest_template_addon` SET `MaxLevel` = 49 WHERE `ID` IN (8166, 8169); + +-- `Control Four Bases` requires `The Battle for Arathi Basin!` (4/4) - Alliance to be completed. +UPDATE `quest_template_addon` SET `PrevQuestID` = 8105 WHERE `ID` = 8114; + +-- `Take Four Bases` requires `The Battle for Arathi Basin!` (4/4) - Horde to be completed. +UPDATE `quest_template_addon` SET `PrevQuestID` = 8120 WHERE `ID` = 8121; + +-- Player requires to be `Friendly` with `The Defilers` to access `Take Four Bases`. +UPDATE `quest_template_addon` SET `RequiredMinRepFaction` = 510, `RequiredMinRepValue` = 3000 WHERE `ID` = 8121; + +-- Player requires to be `Friendly` with `The League of Arathor` to access `Control Four Bases`. +UPDATE `quest_template_addon` SET `RequiredMinRepFaction` = 509, `RequiredMinRepValue` = 3000 WHERE `ID` = 8114; + +-- Adds to gossip_menu: 9832 (Tabard Vendors Gossip) the gossip_menu_option to match the BrodcastTextID for Arathor and Defilers Tabard +DELETE FROM `gossip_menu_option` WHERE (`MenuID` = 9832) AND (`OptionID` IN (11, 12)); +INSERT INTO `gossip_menu_option` (`MenuID`, `OptionID`, `OptionIcon`, `OptionText`, `OptionBroadcastTextID`, `OptionType`, `OptionNpcFlag`, `ActionMenuID`, `ActionPoiID`, `BoxCoded`, `BoxMoney`, `BoxText`, `BoxBroadcastTextID`, `VerifiedBuild`) VALUES +(9832, 11, 0, 'I\'ve lost my Arathor Battle Tabard.', 30354, 1, 1, 0, 0, 0, 0, '', 0, 0), +(9832, 12, 0, 'I\'ve lost my Battle Tabard of the Defilers.', 30355, 1, 1, 0, 0, 0, 0, '', 0, 0); + +-- Alliance check for Tabard Arathor Battle Tabard and quest Control Five Bases +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 15 AND `SourceGroup` = 9832 AND `SourceEntry` IN (11, 12) AND `ConditionTypeOrReference` IN (2, 8); +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +-- Alliance check for Tabard Arathor Battle Tabard and quest Control Five Bases +(15, 9832, 11, 0, 0, 2, 0, 20132, 1, 1, 1, 0, 0, '', 'Only show gossip if player doesn\'t have Arathor Battle Tabard'), +(15, 9832, 11, 0, 0, 8, 0, 8115, 0, 0, 0, 0, 0, '', 'Only show gossip if player already finished quest Control Five Bases'), +-- Horde check for Tabard Battle Tabard of the Defilers and quest Take Five Bases +(15, 9832, 12, 0, 0, 2, 0, 20131, 1, 1, 1, 0, 0, '', 'Only show gossip if player doesn\'t have Battle Tabard of the Defilers'), +(15, 9832, 12, 0, 0, 8, 0, 8122, 0, 0, 0, 0, 0, '', 'Only show gossip if player already finished quest Take Five Bases'); + +-- This changes the link from 10 to 100, allowing more tabards to be added and not havign the close gossip lost in the middle SAI (makes harder to see) +UPDATE `smart_scripts` SET `link` = 100 WHERE `source_type` = 0 AND `link` = 10 AND `entryorguid` IN (5049, 5188, 5189, 5190, 5191, 16610, 16766, 28776); + +-- Changes the ID from 10 to 100 +UPDATE `smart_scripts`SET `id` = 100 WHERE `source_type` = 0 AND `id` = 10 AND `entryorguid` IN (5049, 5188, 5189, 5190, 5191, 16610, 16766, 28776); + +-- Removes and adds the for the Arathor and Defilers tabard +DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `id` IN (10, 11) AND `entryorguid` IN (5049, 5188, 5189, 5190, 5191, 16610, 16766, 28776); +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 +(5049, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lyesa Steelbrow - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(5049, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lyesa Steelbrow - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''); + +-- Removes and adds the for the Arathor and Defilers tabard +DELETE FROM `smart_scripts` WHERE `source_type` = 0 AND `id` IN (10, 11) AND `entryorguid` IN (5049, 5188, 5189, 5190, 5191, 16610, 16766, 28776); +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 +-- Lyesa Steelbrow (5049) Alliance +(5049, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lyesa Steelbrow - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(5049, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Lyesa Steelbrow - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''), + +-- Garyl (5188) Horde +(5188, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Garyl - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(5188, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Garyl - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''), + +-- Thrumn (5189) Horde +(5189, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Thrumn - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(5189, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Thrumn - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''), + +-- Merill Pleasance (5190) Horde +(5190, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Merill Pleasance - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(5190, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Merill Pleasance - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''), + +-- Shalumon (5191) Alliance +(5191, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Shalumon - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(5191, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Shalumon - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''), + +-- Kredis (16610) Horde +(16610, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Kredis - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(16610, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Kredis - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''), + +-- Issca (16766) Alliance +(16766, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Issca - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(16766, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Issca - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''), + +-- Elizabeth Ross (28776) Neutral (Dalaran) +(28776, 0, 10, 100, 62, 0, 100, 512, 9832, 11, 0, 0, 0, 0, 11, 54971, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Elizabeth Ross - On Gossip Option 11 Selected - Cast \'Create Arathor Battle Tabard\''), +(28776, 0, 11, 100, 62, 0, 100, 512, 9832, 12, 0, 0, 0, 0, 11, 54973, 2, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Elizabeth Ross - On Gossip Option 12 Selected - Cast \'Create Battle Tabard of the Defilers\''); From 08e6d4786a9b56d833b7357e167f564d2e945162 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Jul 2025 15:42:33 +0000 Subject: [PATCH 36/43] chore(DB): import pending files Referenced commit(s): 0628ea53bbae9e4538498f79ca45780d0b9d61bb --- .../rev_1750443101763711500.sql => db_world/2025_07_03_01.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1750443101763711500.sql => db_world/2025_07_03_01.sql} (99%) diff --git a/data/sql/updates/pending_db_world/rev_1750443101763711500.sql b/data/sql/updates/db_world/2025_07_03_01.sql similarity index 99% rename from data/sql/updates/pending_db_world/rev_1750443101763711500.sql rename to data/sql/updates/db_world/2025_07_03_01.sql index efa05b347..610ff344f 100644 --- a/data/sql/updates/pending_db_world/rev_1750443101763711500.sql +++ b/data/sql/updates/db_world/2025_07_03_01.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_03_00 -> 2025_07_03_01 -- Bracket 20-29 | `The Battle for Arathi Basin!` (1/4) (Alliance and Horde). UPDATE `quest_template_addon` SET `MaxLevel` = 29 WHERE `ID` IN (8168, 8171); From 4e9889db24e414f5be46ece8d11dc76de23ee344 Mon Sep 17 00:00:00 2001 From: Ryan Turner Date: Thu, 3 Jul 2025 16:43:23 +0100 Subject: [PATCH 37/43] fix(DB/Quests) - Corrected the Reputation requirements for the Scryers and Aldor (#22362) Co-authored-by: Tereneckla --- .../rev_1750935252027011500.sql | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1750935252027011500.sql diff --git a/data/sql/updates/pending_db_world/rev_1750935252027011500.sql b/data/sql/updates/pending_db_world/rev_1750935252027011500.sql new file mode 100644 index 000000000..0ee000a0d --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1750935252027011500.sql @@ -0,0 +1,25 @@ +-- The Aldor +-- Venom Sac: From Hatred to Neutral +UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 0, `RequiredMinRepFaction` = 0, `RequiredMaxRepFaction` = 932 WHERE `id` IN (10017, 10019); + +-- Mark of Kil'jaden: From Neutral to Honoured +UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 9000, `RequiredMinRepFaction` = 932, `RequiredMaxRepFaction` = 932 WHERE `id` IN (10325, 10326, 10327); + +-- Mark of Sargeras: From Neutral to Exalted +UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 0, `RequiredMinRepFaction` = 932, `RequiredMaxRepFaction` = 0 WHERE `id` IN (10653, 10654, 10655, 10826, 10827, 10828); + +-- Fel Armament: From Neutral to Exalted +UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 0, `RequiredMinRepFaction` = 932, `RequiredMaxRepFaction` = 0 WHERE `id` IN (10420, 10421); + +-- The Scryers +-- Basilisk Eye: From Hatred to Neutral +UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 0, `RequiredMinRepValue` = 0, `RequiredMaxRepFaction` = 934 WHERE `id` IN (10024, 10025); + +-- Firewing Signet: From Neutral to Honoured +UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 9000, `RequiredMinRepFaction` = 934, `RequiredMaxRepFaction` = 934 WHERE `id` IN (10412, 10415, 10414); + +-- Sunfury Signet: From Neutral to Exalted +UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 0, `RequiredMinRepFaction` = 934, `RequiredMaxRepFaction` = 0 WHERE `id` IN (10656, 10658, 10659, 10822, 10822, 10823, 10824); + +-- Arcane Tome: From Neutral to Exalted +UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 0, `RequiredMinRepFaction` = 934, `RequiredMaxRepFaction` = 0 WHERE `id` IN (10419, 10416); From 545d5a48e2ea03c9c94bd3186404efa977e3e662 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Jul 2025 15:43:34 +0000 Subject: [PATCH 38/43] chore(DB): import pending files Referenced commit(s): 08e6d4786a9b56d833b7357e167f564d2e945162 --- .../rev_1750935252027011500.sql => db_world/2025_07_03_02.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1750935252027011500.sql => db_world/2025_07_03_02.sql} (97%) diff --git a/data/sql/updates/pending_db_world/rev_1750935252027011500.sql b/data/sql/updates/db_world/2025_07_03_02.sql similarity index 97% rename from data/sql/updates/pending_db_world/rev_1750935252027011500.sql rename to data/sql/updates/db_world/2025_07_03_02.sql index 0ee000a0d..d73f08df2 100644 --- a/data/sql/updates/pending_db_world/rev_1750935252027011500.sql +++ b/data/sql/updates/db_world/2025_07_03_02.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_03_01 -> 2025_07_03_02 -- The Aldor -- Venom Sac: From Hatred to Neutral UPDATE `quest_template_addon` SET `RequiredMinRepValue` = 0, `RequiredMaxRepValue` = 0, `RequiredMinRepFaction` = 0, `RequiredMaxRepFaction` = 932 WHERE `id` IN (10017, 10019); From 87e500ff0f8f71303bd4323dea1de94a790740a4 Mon Sep 17 00:00:00 2001 From: Kitzunu <24550914+Kitzunu@users.noreply.github.com> Date: Thu, 3 Jul 2025 17:43:55 +0200 Subject: [PATCH 39/43] =?UTF-8?q?fix(DB/Autobroadcast):=20Update=20'text'?= =?UTF-8?q?=20column=20in=20autobroadcast=5Flocale=20t=E2=80=A6=20(#22382)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/sql/updates/pending_db_auth/rev_1751147997826401300.sql | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 data/sql/updates/pending_db_auth/rev_1751147997826401300.sql diff --git a/data/sql/updates/pending_db_auth/rev_1751147997826401300.sql b/data/sql/updates/pending_db_auth/rev_1751147997826401300.sql new file mode 100644 index 000000000..86bdeb4b2 --- /dev/null +++ b/data/sql/updates/pending_db_auth/rev_1751147997826401300.sql @@ -0,0 +1,3 @@ +-- +ALTER TABLE `autobroadcast_locale` + CHANGE COLUMN `text` `text` LONGTEXT NOT NULL COLLATE 'utf8mb4_unicode_ci' AFTER `locale`; From 4744ba3970c4371514578c8c59d04077f0bf1851 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Jul 2025 15:44:24 +0000 Subject: [PATCH 40/43] chore(DB): import pending files Referenced commit(s): 4e9889db24e414f5be46ece8d11dc76de23ee344 --- .../rev_1751147997826401300.sql => db_auth/2025_07_03_00.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_auth/rev_1751147997826401300.sql => db_auth/2025_07_03_00.sql} (75%) diff --git a/data/sql/updates/pending_db_auth/rev_1751147997826401300.sql b/data/sql/updates/db_auth/2025_07_03_00.sql similarity index 75% rename from data/sql/updates/pending_db_auth/rev_1751147997826401300.sql rename to data/sql/updates/db_auth/2025_07_03_00.sql index 86bdeb4b2..3f2bd8c02 100644 --- a/data/sql/updates/pending_db_auth/rev_1751147997826401300.sql +++ b/data/sql/updates/db_auth/2025_07_03_00.sql @@ -1,3 +1,4 @@ +-- DB update 2025_02_16_01 -> 2025_07_03_00 -- ALTER TABLE `autobroadcast_locale` CHANGE COLUMN `text` `text` LONGTEXT NOT NULL COLLATE 'utf8mb4_unicode_ci' AFTER `locale`; From 75915c2e127bcfe891400d36135fc8a133dbca84 Mon Sep 17 00:00:00 2001 From: Jelle Meeus Date: Thu, 3 Jul 2025 19:27:46 +0200 Subject: [PATCH 41/43] fix(DB/SAI): Use Holy Fire spell for Draenei Anchorite (#22414) --- data/sql/updates/pending_db_world/rev_1751551734064867759.sql | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 data/sql/updates/pending_db_world/rev_1751551734064867759.sql diff --git a/data/sql/updates/pending_db_world/rev_1751551734064867759.sql b/data/sql/updates/pending_db_world/rev_1751551734064867759.sql new file mode 100644 index 000000000..53a43c930 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1751551734064867759.sql @@ -0,0 +1,4 @@ +-- +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 16994) AND (`source_type` = 0) AND (`id` IN (0)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(16994, 0, 0, 0, 0, 0, 100, 0, 4000, 9000, 10000, 16000, 0, 0, 11, 17140, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Draenei Anchorite - In Combat - Cast \'Holy Fire\''); From bd6011414a31e973274712fb8d37335c1047fe62 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 3 Jul 2025 17:28:49 +0000 Subject: [PATCH 42/43] chore(DB): import pending files Referenced commit(s): 75915c2e127bcfe891400d36135fc8a133dbca84 --- .../rev_1751551734064867759.sql => db_world/2025_07_03_03.sql} | 1 + 1 file changed, 1 insertion(+) rename data/sql/updates/{pending_db_world/rev_1751551734064867759.sql => db_world/2025_07_03_03.sql} (94%) diff --git a/data/sql/updates/pending_db_world/rev_1751551734064867759.sql b/data/sql/updates/db_world/2025_07_03_03.sql similarity index 94% rename from data/sql/updates/pending_db_world/rev_1751551734064867759.sql rename to data/sql/updates/db_world/2025_07_03_03.sql index 53a43c930..02fa116c8 100644 --- a/data/sql/updates/pending_db_world/rev_1751551734064867759.sql +++ b/data/sql/updates/db_world/2025_07_03_03.sql @@ -1,3 +1,4 @@ +-- DB update 2025_07_03_02 -> 2025_07_03_03 -- DELETE FROM `smart_scripts` WHERE (`entryorguid` = 16994) AND (`source_type` = 0) AND (`id` IN (0)); INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES From 161898b7e6c1d8a0208c907e363a95b75331d20d Mon Sep 17 00:00:00 2001 From: Yehonal Date: Thu, 3 Jul 2025 20:31:18 +0200 Subject: [PATCH 43/43] fix(installer/windows): update OpenSSL version to 3.5.1 to fix gh build errors (#22416) Chocolatey on windows cannot download openssl 3.4.1 since the source has been removed. 3.5.1 is available instead. However, this must be tested This https://community.chocolatey.org/packages/openssl/3.4.1 is pointing to this: https://slproweb.com/download/Win32OpenSSL-3_4_1.exe that doesn't exist anymore and has been replaced by the 3.4.2, but chocolatey doesn't have this version yet --- 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 59c657899..d1064485b 100644 --- a/apps/installer/includes/os_configs/windows.sh +++ b/apps/installer/includes/os_configs/windows.sh @@ -24,7 +24,7 @@ fi choco install -y --skip-checksums $INSTALL_ARGS cmake.install -y --installargs 'ADD_CMAKE_TO_PATH=System' choco install -y --skip-checksums $INSTALL_ARGS visualstudio2022-workload-nativedesktop -choco install -y --skip-checksums $INSTALL_ARGS openssl --force --version=3.4.1 +choco install -y --skip-checksums $INSTALL_ARGS openssl --force --version=3.5.1 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.4