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

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__

View File

@@ -22,20 +22,82 @@
#include "ScriptSystem.h"
#include "SmartAI.h"
#include "SpellMgr.h"
#include "Utilities/TypeList.h"
#include "UnitAI.h"
namespace
{
template<typename T>
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<typename Script, uint16 EnabledHookCountValue = 0, bool PromotedAfterDbLoadValue = false, bool LegacyDbValidationValue = false>
struct ScriptTypeInfo
{
for (auto const& [scriptID, script] : ScriptRegistry<T>::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<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
@@ -77,84 +139,24 @@ void ScriptMgr::Initialize()
_script_loader_callback();
_modules_loader_callback();
ScriptRegistry<AccountScript>::InitEnabledHooksIfNeeded(ACCOUNTHOOK_END);
ScriptRegistry<AchievementScript>::InitEnabledHooksIfNeeded(ACHIEVEMENTHOOK_END);
ScriptRegistry<ArenaScript>::InitEnabledHooksIfNeeded(ARENAHOOK_END);
ScriptRegistry<ArenaTeamScript>::InitEnabledHooksIfNeeded(ARENATEAMHOOK_END);
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);
Acore::for_each<ScriptRegistryTypes>([]<typename Info>()
{
if constexpr (Info::HasEnabledHooks)
ScriptRegistry<typename Info::type>::InitEnabledHooksIfNeeded(Info::EnabledHooks);
});
}
void ScriptMgr::Unload()
{
SCR_CLEAR<AccountScript>();
SCR_CLEAR<AchievementCriteriaScript>();
SCR_CLEAR<AchievementScript>();
SCR_CLEAR<AllCreatureScript>();
SCR_CLEAR<AllGameObjectScript>();
SCR_CLEAR<AllItemScript>();
SCR_CLEAR<AllMapScript>();
SCR_CLEAR<AreaTriggerScript>();
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>();
Acore::for_each<ScriptRegistryTypes>([]<typename Info>()
{
for (auto const& [scriptID, script] : ScriptRegistry<typename Info::type>::ScriptPointerList)
{
delete script;
}
ScriptRegistry<typename Info::type>::ScriptPointerList.clear();
});
delete[] SpellSummary;
}
@@ -165,21 +167,13 @@ void ScriptMgr::LoadDatabase()
sScriptSystemMgr->LoadScriptWaypoints();
// Add all scripts that must be loaded after db/maps
ScriptRegistry<WorldMapScript>::AddALScripts();
ScriptRegistry<BattlegroundMapScript>::AddALScripts();
ScriptRegistry<InstanceMapScript>::AddALScripts();
ScriptRegistry<SpellScriptLoader>::AddALScripts();
ScriptRegistry<ItemScript>::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();
// 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<ScriptRegistryTypes>([]<typename Info>()
{
if constexpr (Info::PromotedAfterDbLoad)
ScriptRegistry<typename Info::type>::AddALScripts();
});
FillSpellSummary();
@@ -195,43 +189,16 @@ void ScriptMgr::CheckIfScriptsInDatabaseExist()
{
if (uint32 sid = sObjectMgr->GetScriptId(scriptName))
{
if (!ScriptRegistry<SpellScriptLoader>::GetScriptById(sid) &&
!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))
{
LOG_ERROR("sql.sql", "Script named '{}' is assigned in the database, but has no code!", scriptName);
}
bool const hasRegisteredScript = Acore::any_of<ScriptRegistryTypes>([sid]<typename Info>()
{
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);
}
}
}