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);
}
}
}