mirror of
https://github.com/liyunfan1223/azerothcore-wotlk.git
synced 2026-08-07 23:38:00 +00:00
refactor(Scripting): centralize script registry metadata (#26025)
This commit is contained in:
132
src/common/Utilities/TypeList.h
Normal file
132
src/common/Utilities/TypeList.h
Normal 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__
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user