refactor(Scripting): centralize script registry metadata (#26025)

This commit is contained in:
kb31
2026-06-03 09:20:48 +01:00
committed by GitHub
parent c53c7dc93b
commit a4dee23e17
2 changed files with 233 additions and 134 deletions
+132
View File
@@ -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 <http://www.gnu.org/licenses/>.
*/
#ifndef TypeList_h__
#define TypeList_h__
#include <cstddef>
#include <utility>
namespace Acore
{
/**
* @brief Provide an empty tag type containing the specified @p Ts.
*
* @tparam Ts The types in this list.
*/
template <typename... Ts>
struct type_list { };
namespace Impl
{
template <typename T>
inline constexpr bool is_type_list = false;
template <typename... Ts>
inline constexpr bool is_type_list<type_list<Ts...>> = true;
template <typename List>
struct list_size;
template <typename... Ts>
struct list_size<type_list<Ts...>>
{
static constexpr std::size_t value = sizeof...(Ts);
};
template <typename... Ts, typename Func>
constexpr void for_each(type_list<Ts...>, Func&& f)
{
(f.template operator()<Ts>(), ...);
}
template <typename... Ts, typename Pred>
constexpr std::size_t count_if(type_list<Ts...>, Pred pred)
{
return ((pred.template operator()<Ts>() ? std::size_t{1} : std::size_t{0}) + ... + std::size_t{0});
}
template <typename... Ts, typename Pred>
constexpr bool any_of(type_list<Ts...>, Pred pred)
{
return (false || ... || pred.template operator()<Ts>());
}
}
/**
* @brief Satisfied only by Acore::type_list specializations.
*
* Constrains the public list operations so they are viable only for an
* actual type_list.
*/
template <typename T>
concept AnyTypeList = Impl::is_type_list<T>;
/**
* @brief The number of types in the specified @p List.
*
* @tparam List A type_list specialization.
*/
template <AnyTypeList List>
inline constexpr std::size_t size_v = Impl::list_size<List>::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 <AnyTypeList List, typename Func>
constexpr void for_each(Func&& f)
{
Impl::for_each(List{}, std::forward<Func>(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 <AnyTypeList List, typename Pred>
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 <AnyTypeList List, typename Pred>
constexpr bool any_of(Pred pred)
{
return Impl::any_of(List{}, pred);
}
}
#endif // TypeList_h__
+99 -132
View File
@@ -22,20 +22,82 @@
#include "ScriptSystem.h" #include "ScriptSystem.h"
#include "SmartAI.h" #include "SmartAI.h"
#include "SpellMgr.h" #include "SpellMgr.h"
#include "Utilities/TypeList.h"
#include "UnitAI.h" #include "UnitAI.h"
namespace namespace
{ {
template<typename T> // Metadata for the script-registry operations derived in this file.
inline void SCR_CLEAR() // EnabledHooks is the legacy *_HOOK_END count for enabled-hook dispatch.
// LegacyDbValidationCandidate preserves the old database-validation search set.
template<typename Script, uint16 EnabledHookCountValue = 0, bool PromotedAfterDbLoadValue = false, bool LegacyDbValidationValue = false>
struct ScriptTypeInfo
{ {
for (auto const& [scriptID, script] : ScriptRegistry<T>::ScriptPointerList) using type = Script;
{ static constexpr uint16 EnabledHooks = EnabledHookCountValue;
delete script; static constexpr bool HasEnabledHooks = EnabledHookCountValue > 0;
} static constexpr bool PromotedAfterDbLoad = PromotedAfterDbLoadValue;
static constexpr bool LegacyDbValidationCandidate = LegacyDbValidationValue;
};
ScriptRegistry<T>::ScriptPointerList.clear(); // script type hooks afterLoad dbCheck
} using ScriptRegistryTypes = Acore::type_list<
ScriptTypeInfo<AccountScript, ACCOUNTHOOK_END>,
ScriptTypeInfo<AchievementCriteriaScript, 0, true, true>,
ScriptTypeInfo<AchievementScript, ACHIEVEMENTHOOK_END, false, true>,
ScriptTypeInfo<AllCreatureScript>,
ScriptTypeInfo<AllGameObjectScript>,
ScriptTypeInfo<AllItemScript>,
ScriptTypeInfo<AllMapScript, ALLMAPHOOK_END>,
ScriptTypeInfo<AreaTriggerScript, 0, true, true>,
ScriptTypeInfo<ArenaScript, ARENAHOOK_END, false, true>,
ScriptTypeInfo<ArenaTeamScript, ARENATEAMHOOK_END, false, true>,
ScriptTypeInfo<AuctionHouseScript, AUCTIONHOUSEHOOK_END, false, true>,
ScriptTypeInfo<BattlefieldScript, BATTLEFIELDHOOK_END>,
ScriptTypeInfo<BGScript, ALLBATTLEGROUNDHOOK_END, false, true>,
ScriptTypeInfo<BattlegroundMapScript, 0, true, true>,
ScriptTypeInfo<BattlegroundScript, 0, true, true>,
ScriptTypeInfo<CommandSC, ALLCOMMANDHOOK_END, false, true>,
ScriptTypeInfo<CommandScript, 0, false, true>,
ScriptTypeInfo<ConditionScript, 0, true, true>,
ScriptTypeInfo<CreatureScript, 0, true, true>,
ScriptTypeInfo<DatabaseScript, DATABASEHOOK_END, false, true>,
ScriptTypeInfo<DynamicObjectScript, 0, false, true>,
ScriptTypeInfo<ALEScript>,
ScriptTypeInfo<FormulaScript, FORMULAHOOK_END, false, true>,
ScriptTypeInfo<GameEventScript, GAMEEVENTHOOK_END>,
ScriptTypeInfo<GameObjectScript, 0, true, true>,
ScriptTypeInfo<GlobalScript, GLOBALHOOK_END>,
ScriptTypeInfo<GroupScript, GROUPHOOK_END, false, true>,
ScriptTypeInfo<GuildScript, GUILDHOOK_END, false, true>,
ScriptTypeInfo<InstanceMapScript, 0, true, true>,
ScriptTypeInfo<ItemScript, 0, true, true>,
ScriptTypeInfo<LootScript, LOOTHOOK_END>,
ScriptTypeInfo<MailScript, MAILHOOK_END>,
ScriptTypeInfo<MiscScript, MISCHOOK_END, false, true>,
ScriptTypeInfo<MovementHandlerScript, MOVEMENTHOOK_END>,
ScriptTypeInfo<OutdoorPvPScript, 0, true, true>,
ScriptTypeInfo<PetScript, PETHOOK_END, false, true>,
ScriptTypeInfo<PlayerScript, PLAYERHOOK_END, false, true>,
ScriptTypeInfo<ServerScript, SERVERHOOK_END, false, true>,
ScriptTypeInfo<SpellSC, ALLSPELLHOOK_END, false, true>,
ScriptTypeInfo<SpellScriptLoader, 0, true, true>,
ScriptTypeInfo<TicketScript, TICKETHOOK_END, false, true>,
ScriptTypeInfo<TransportScript, 0, true, true>,
ScriptTypeInfo<UnitScript, UNITHOOK_END>,
ScriptTypeInfo<VehicleScript, 0, false, true>,
ScriptTypeInfo<WeatherScript, 0, true, true>,
ScriptTypeInfo<WorldMapScript, 0, true, true>,
ScriptTypeInfo<WorldObjectScript, WORLDOBJECTHOOK_END>,
ScriptTypeInfo<WorldScript, WORLDHOOK_END, false, true>>;
// 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<ScriptRegistryTypes> == 48, "Update count when adding a script registry type");
static_assert(Acore::count_if<ScriptRegistryTypes>([]<typename Info>() { return Info::HasEnabledHooks; }) == 27, "Enabled-hook script type count changed");
static_assert(Acore::count_if<ScriptRegistryTypes>([]<typename Info>() { return Info::PromotedAfterDbLoad; }) == 14, "After-load script type count changed");
static_assert(Acore::count_if<ScriptRegistryTypes>([]<typename Info>() { return Info::LegacyDbValidationCandidate; }) == 34, "Database-check script type count changed");
} }
struct TSpellSummary struct TSpellSummary
@@ -77,84 +139,24 @@ void ScriptMgr::Initialize()
_script_loader_callback(); _script_loader_callback();
_modules_loader_callback(); _modules_loader_callback();
ScriptRegistry<AccountScript>::InitEnabledHooksIfNeeded(ACCOUNTHOOK_END); Acore::for_each<ScriptRegistryTypes>([]<typename Info>()
ScriptRegistry<AchievementScript>::InitEnabledHooksIfNeeded(ACHIEVEMENTHOOK_END); {
ScriptRegistry<ArenaScript>::InitEnabledHooksIfNeeded(ARENAHOOK_END); if constexpr (Info::HasEnabledHooks)
ScriptRegistry<ArenaTeamScript>::InitEnabledHooksIfNeeded(ARENATEAMHOOK_END); ScriptRegistry<typename Info::type>::InitEnabledHooksIfNeeded(Info::EnabledHooks);
ScriptRegistry<AuctionHouseScript>::InitEnabledHooksIfNeeded(AUCTIONHOUSEHOOK_END); });
ScriptRegistry<BattlefieldScript>::InitEnabledHooksIfNeeded(BATTLEFIELDHOOK_END);
ScriptRegistry<BGScript>::InitEnabledHooksIfNeeded(ALLBATTLEGROUNDHOOK_END);
ScriptRegistry<CommandSC>::InitEnabledHooksIfNeeded(ALLCOMMANDHOOK_END);
ScriptRegistry<DatabaseScript>::InitEnabledHooksIfNeeded(DATABASEHOOK_END);
ScriptRegistry<FormulaScript>::InitEnabledHooksIfNeeded(FORMULAHOOK_END);
ScriptRegistry<GameEventScript>::InitEnabledHooksIfNeeded(GAMEEVENTHOOK_END);
ScriptRegistry<GlobalScript>::InitEnabledHooksIfNeeded(GLOBALHOOK_END);
ScriptRegistry<GroupScript>::InitEnabledHooksIfNeeded(GROUPHOOK_END);
ScriptRegistry<GuildScript>::InitEnabledHooksIfNeeded(GUILDHOOK_END);
ScriptRegistry<LootScript>::InitEnabledHooksIfNeeded(LOOTHOOK_END);
ScriptRegistry<MailScript>::InitEnabledHooksIfNeeded(MAILHOOK_END);
ScriptRegistry<MiscScript>::InitEnabledHooksIfNeeded(MISCHOOK_END);
ScriptRegistry<MovementHandlerScript>::InitEnabledHooksIfNeeded(MOVEMENTHOOK_END);
ScriptRegistry<PetScript>::InitEnabledHooksIfNeeded(PETHOOK_END);
ScriptRegistry<PlayerScript>::InitEnabledHooksIfNeeded(PLAYERHOOK_END);
ScriptRegistry<ServerScript>::InitEnabledHooksIfNeeded(SERVERHOOK_END);
ScriptRegistry<SpellSC>::InitEnabledHooksIfNeeded(ALLSPELLHOOK_END);
ScriptRegistry<TicketScript>::InitEnabledHooksIfNeeded(TICKETHOOK_END);
ScriptRegistry<UnitScript>::InitEnabledHooksIfNeeded(UNITHOOK_END);
ScriptRegistry<WorldObjectScript>::InitEnabledHooksIfNeeded(WORLDOBJECTHOOK_END);
ScriptRegistry<WorldScript>::InitEnabledHooksIfNeeded(WORLDHOOK_END);
ScriptRegistry<AllMapScript>::InitEnabledHooksIfNeeded(ALLMAPHOOK_END);
} }
void ScriptMgr::Unload() void ScriptMgr::Unload()
{ {
SCR_CLEAR<AccountScript>(); Acore::for_each<ScriptRegistryTypes>([]<typename Info>()
SCR_CLEAR<AchievementCriteriaScript>(); {
SCR_CLEAR<AchievementScript>(); for (auto const& [scriptID, script] : ScriptRegistry<typename Info::type>::ScriptPointerList)
SCR_CLEAR<AllCreatureScript>(); {
SCR_CLEAR<AllGameObjectScript>(); delete script;
SCR_CLEAR<AllItemScript>(); }
SCR_CLEAR<AllMapScript>();
SCR_CLEAR<AreaTriggerScript>(); ScriptRegistry<typename Info::type>::ScriptPointerList.clear();
SCR_CLEAR<ArenaScript>(); });
SCR_CLEAR<ArenaTeamScript>();
SCR_CLEAR<AuctionHouseScript>();
SCR_CLEAR<BGScript>();
SCR_CLEAR<BattlegroundMapScript>();
SCR_CLEAR<BattlegroundScript>();
SCR_CLEAR<CommandSC>();
SCR_CLEAR<CommandScript>();
SCR_CLEAR<ConditionScript>();
SCR_CLEAR<CreatureScript>();
SCR_CLEAR<DatabaseScript>();
SCR_CLEAR<DynamicObjectScript>();
SCR_CLEAR<ALEScript>();
SCR_CLEAR<FormulaScript>();
SCR_CLEAR<GameEventScript>();
SCR_CLEAR<GameObjectScript>();
SCR_CLEAR<GlobalScript>();
SCR_CLEAR<GroupScript>();
SCR_CLEAR<GuildScript>();
SCR_CLEAR<InstanceMapScript>();
SCR_CLEAR<ItemScript>();
SCR_CLEAR<LootScript>();
SCR_CLEAR<MailScript>();
SCR_CLEAR<MiscScript>();
SCR_CLEAR<MovementHandlerScript>();
SCR_CLEAR<OutdoorPvPScript>();
SCR_CLEAR<PetScript>();
SCR_CLEAR<PlayerScript>();
SCR_CLEAR<ServerScript>();
SCR_CLEAR<SpellSC>();
SCR_CLEAR<SpellScriptLoader>();
SCR_CLEAR<TicketScript>();
SCR_CLEAR<TransportScript>();
SCR_CLEAR<UnitScript>();
SCR_CLEAR<VehicleScript>();
SCR_CLEAR<WeatherScript>();
SCR_CLEAR<WorldMapScript>();
SCR_CLEAR<WorldObjectScript>();
SCR_CLEAR<WorldScript>();
delete[] SpellSummary; delete[] SpellSummary;
} }
@@ -165,21 +167,13 @@ void ScriptMgr::LoadDatabase()
sScriptSystemMgr->LoadScriptWaypoints(); sScriptSystemMgr->LoadScriptWaypoints();
// Add all scripts that must be loaded after db/maps // Add all scripts that must be loaded after db/maps. Each registry's
ScriptRegistry<WorldMapScript>::AddALScripts(); // after-load list is independent, so iteration order does not matter.
ScriptRegistry<BattlegroundMapScript>::AddALScripts(); Acore::for_each<ScriptRegistryTypes>([]<typename Info>()
ScriptRegistry<InstanceMapScript>::AddALScripts(); {
ScriptRegistry<SpellScriptLoader>::AddALScripts(); if constexpr (Info::PromotedAfterDbLoad)
ScriptRegistry<ItemScript>::AddALScripts(); ScriptRegistry<typename Info::type>::AddALScripts();
ScriptRegistry<CreatureScript>::AddALScripts(); });
ScriptRegistry<GameObjectScript>::AddALScripts();
ScriptRegistry<AreaTriggerScript>::AddALScripts();
ScriptRegistry<BattlegroundScript>::AddALScripts();
ScriptRegistry<OutdoorPvPScript>::AddALScripts();
ScriptRegistry<WeatherScript>::AddALScripts();
ScriptRegistry<ConditionScript>::AddALScripts();
ScriptRegistry<TransportScript>::AddALScripts();
ScriptRegistry<AchievementCriteriaScript>::AddALScripts();
FillSpellSummary(); FillSpellSummary();
@@ -195,45 +189,18 @@ void ScriptMgr::CheckIfScriptsInDatabaseExist()
{ {
if (uint32 sid = sObjectMgr->GetScriptId(scriptName)) if (uint32 sid = sObjectMgr->GetScriptId(scriptName))
{ {
if (!ScriptRegistry<SpellScriptLoader>::GetScriptById(sid) && bool const hasRegisteredScript = Acore::any_of<ScriptRegistryTypes>([sid]<typename Info>()
!ScriptRegistry<ServerScript>::GetScriptById(sid) &&
!ScriptRegistry<WorldScript>::GetScriptById(sid) &&
!ScriptRegistry<FormulaScript>::GetScriptById(sid) &&
!ScriptRegistry<WorldMapScript>::GetScriptById(sid) &&
!ScriptRegistry<InstanceMapScript>::GetScriptById(sid) &&
!ScriptRegistry<BattlegroundMapScript>::GetScriptById(sid) &&
!ScriptRegistry<ItemScript>::GetScriptById(sid) &&
!ScriptRegistry<CreatureScript>::GetScriptById(sid) &&
!ScriptRegistry<GameObjectScript>::GetScriptById(sid) &&
!ScriptRegistry<AreaTriggerScript>::GetScriptById(sid) &&
!ScriptRegistry<BattlegroundScript>::GetScriptById(sid) &&
!ScriptRegistry<OutdoorPvPScript>::GetScriptById(sid) &&
!ScriptRegistry<CommandScript>::GetScriptById(sid) &&
!ScriptRegistry<WeatherScript>::GetScriptById(sid) &&
!ScriptRegistry<AuctionHouseScript>::GetScriptById(sid) &&
!ScriptRegistry<ConditionScript>::GetScriptById(sid) &&
!ScriptRegistry<VehicleScript>::GetScriptById(sid) &&
!ScriptRegistry<DynamicObjectScript>::GetScriptById(sid) &&
!ScriptRegistry<TransportScript>::GetScriptById(sid) &&
!ScriptRegistry<AchievementCriteriaScript>::GetScriptById(sid) &&
!ScriptRegistry<PlayerScript>::GetScriptById(sid) &&
!ScriptRegistry<GuildScript>::GetScriptById(sid) &&
!ScriptRegistry<BGScript>::GetScriptById(sid) &&
!ScriptRegistry<AchievementScript>::GetScriptById(sid) &&
!ScriptRegistry<ArenaTeamScript>::GetScriptById(sid) &&
!ScriptRegistry<SpellSC>::GetScriptById(sid) &&
!ScriptRegistry<MiscScript>::GetScriptById(sid) &&
!ScriptRegistry<PetScript>::GetScriptById(sid) &&
!ScriptRegistry<CommandSC>::GetScriptById(sid) &&
!ScriptRegistry<ArenaScript>::GetScriptById(sid) &&
!ScriptRegistry<GroupScript>::GetScriptById(sid) &&
!ScriptRegistry<DatabaseScript>::GetScriptById(sid) &&
!ScriptRegistry<TicketScript>::GetScriptById(sid))
{ {
if constexpr (Info::LegacyDbValidationCandidate)
return ScriptRegistry<typename Info::type>::GetScriptById(sid) != nullptr;
return false;
});
if (!hasRegisteredScript)
LOG_ERROR("sql.sql", "Script named '{}' is assigned in the database, but has no code!", scriptName); LOG_ERROR("sql.sql", "Script named '{}' is assigned in the database, but has no code!", scriptName);
} }
} }
}
} }
void ScriptMgr::FillSpellSummary() void ScriptMgr::FillSpellSummary()