diff --git a/src/common/Utilities/TypeList.h b/src/common/Utilities/TypeList.h new file mode 100644 index 000000000..54bbdc237 --- /dev/null +++ b/src/common/Utilities/TypeList.h @@ -0,0 +1,132 @@ +/* + * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef TypeList_h__ +#define TypeList_h__ + +#include +#include + +namespace Acore +{ + /** + * @brief Provide an empty tag type containing the specified @p Ts. + * + * @tparam Ts The types in this list. + */ + template + struct type_list { }; + + namespace Impl + { + template + inline constexpr bool is_type_list = false; + + template + inline constexpr bool is_type_list> = true; + + template + struct list_size; + + template + struct list_size> + { + static constexpr std::size_t value = sizeof...(Ts); + }; + + template + constexpr void for_each(type_list, Func&& f) + { + (f.template operator()(), ...); + } + + template + constexpr std::size_t count_if(type_list, Pred pred) + { + return ((pred.template operator()() ? std::size_t{1} : std::size_t{0}) + ... + std::size_t{0}); + } + + template + constexpr bool any_of(type_list, Pred pred) + { + return (false || ... || pred.template operator()()); + } + } + + /** + * @brief Satisfied only by Acore::type_list specializations. + * + * Constrains the public list operations so they are viable only for an + * actual type_list. + */ + template + concept AnyTypeList = Impl::is_type_list; + + /** + * @brief The number of types in the specified @p List. + * + * @tparam List A type_list specialization. + */ + template + inline constexpr std::size_t size_v = Impl::list_size::value; + + /** + * @brief Invoke the specified @p f once for each type in the specified + * @p List, in declaration order. + * + * @tparam List A type_list specialization. + * @tparam Func The type of the callable. + * @param f The callable to invoke. + */ + template + constexpr void for_each(Func&& f) + { + Impl::for_each(List{}, std::forward(f)); + } + + /** + * @brief Return the number of types in the specified @p List for which the + * specified @p pred returns true. + * + * @tparam List A type_list specialization. + * @tparam Pred The type of the predicate. + * @param pred The predicate to evaluate. + * @return The number of matching types. + */ + template + constexpr std::size_t count_if(Pred pred) + { + return Impl::count_if(List{}, pred); + } + + /** + * @brief Return true if the specified @p pred returns true for any type in + * the specified @p List, and false otherwise. + * + * @tparam List A type_list specialization. + * @tparam Pred The type of the predicate. + * @param pred The predicate to evaluate. + * @return True if any type matches, and false otherwise. + */ + template + constexpr bool any_of(Pred pred) + { + return Impl::any_of(List{}, pred); + } +} + +#endif // TypeList_h__ diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index b28f31445..b95741417 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -22,20 +22,82 @@ #include "ScriptSystem.h" #include "SmartAI.h" #include "SpellMgr.h" +#include "Utilities/TypeList.h" #include "UnitAI.h" namespace { - template - inline void SCR_CLEAR() + // Metadata for the script-registry operations derived in this file. + // EnabledHooks is the legacy *_HOOK_END count for enabled-hook dispatch. + // LegacyDbValidationCandidate preserves the old database-validation search set. + template + struct ScriptTypeInfo { - for (auto const& [scriptID, script] : ScriptRegistry::ScriptPointerList) - { - delete script; - } + using type = Script; + static constexpr uint16 EnabledHooks = EnabledHookCountValue; + static constexpr bool HasEnabledHooks = EnabledHookCountValue > 0; + static constexpr bool PromotedAfterDbLoad = PromotedAfterDbLoadValue; + static constexpr bool LegacyDbValidationCandidate = LegacyDbValidationValue; + }; - ScriptRegistry::ScriptPointerList.clear(); - } + // script type hooks afterLoad dbCheck + using ScriptRegistryTypes = Acore::type_list< + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo, + ScriptTypeInfo>; + + // These counts mirror the four hand-maintained lists this consolidation + // replaced. If a flag is mistyped or a type is added without its metadata, + // the build fails here instead of silently drifting. + static_assert(Acore::size_v == 48, "Update count when adding a script registry type"); + static_assert(Acore::count_if([]() { return Info::HasEnabledHooks; }) == 27, "Enabled-hook script type count changed"); + static_assert(Acore::count_if([]() { return Info::PromotedAfterDbLoad; }) == 14, "After-load script type count changed"); + static_assert(Acore::count_if([]() { return Info::LegacyDbValidationCandidate; }) == 34, "Database-check script type count changed"); } struct TSpellSummary @@ -77,84 +139,24 @@ void ScriptMgr::Initialize() _script_loader_callback(); _modules_loader_callback(); - ScriptRegistry::InitEnabledHooksIfNeeded(ACCOUNTHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(ACHIEVEMENTHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(ARENAHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(ARENATEAMHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(AUCTIONHOUSEHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(BATTLEFIELDHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(ALLBATTLEGROUNDHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(ALLCOMMANDHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(DATABASEHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(FORMULAHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(GAMEEVENTHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(GLOBALHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(GROUPHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(GUILDHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(LOOTHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(MAILHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(MISCHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(MOVEMENTHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(PETHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(PLAYERHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(SERVERHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(ALLSPELLHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(TICKETHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(UNITHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(WORLDOBJECTHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(WORLDHOOK_END); - ScriptRegistry::InitEnabledHooksIfNeeded(ALLMAPHOOK_END); + Acore::for_each([]() + { + if constexpr (Info::HasEnabledHooks) + ScriptRegistry::InitEnabledHooksIfNeeded(Info::EnabledHooks); + }); } void ScriptMgr::Unload() { - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); - SCR_CLEAR(); + Acore::for_each([]() + { + for (auto const& [scriptID, script] : ScriptRegistry::ScriptPointerList) + { + delete script; + } + + ScriptRegistry::ScriptPointerList.clear(); + }); delete[] SpellSummary; } @@ -165,21 +167,13 @@ void ScriptMgr::LoadDatabase() sScriptSystemMgr->LoadScriptWaypoints(); - // Add all scripts that must be loaded after db/maps - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); - ScriptRegistry::AddALScripts(); + // Add all scripts that must be loaded after db/maps. Each registry's + // after-load list is independent, so iteration order does not matter. + Acore::for_each([]() + { + if constexpr (Info::PromotedAfterDbLoad) + ScriptRegistry::AddALScripts(); + }); FillSpellSummary(); @@ -195,43 +189,16 @@ void ScriptMgr::CheckIfScriptsInDatabaseExist() { if (uint32 sid = sObjectMgr->GetScriptId(scriptName)) { - if (!ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid) && - !ScriptRegistry::GetScriptById(sid)) - { - LOG_ERROR("sql.sql", "Script named '{}' is assigned in the database, but has no code!", scriptName); - } + bool const hasRegisteredScript = Acore::any_of([sid]() + { + if constexpr (Info::LegacyDbValidationCandidate) + return ScriptRegistry::GetScriptById(sid) != nullptr; + + return false; + }); + + if (!hasRegisteredScript) + LOG_ERROR("sql.sql", "Script named '{}' is assigned in the database, but has no code!", scriptName); } } }