diff options
author | Seyden <saiifii@live.de> | 2021-10-22 12:51:45 +0200 |
---|---|---|
committer | Naios <naios-dev@live.de> | 2021-10-22 12:52:37 +0200 |
commit | 8f097e4425d182c03b26d847c0537207d9f3cd2e (patch) | |
tree | d9d6233d0cd2d75978a97ab8b3c73429886322a4 /src | |
parent | 972105183a16c9ac8c43f379cce4acc8bf764cd8 (diff) |
Core/Scripts: Implement script name reloading
* Authored by Seyden
* Co-authored by Naios
* We thank Shauren for your helpful feedback
Diffstat (limited to 'src')
36 files changed, 456 insertions, 211 deletions
diff --git a/src/server/game/AI/CoreAI/AreaTriggerAI.cpp b/src/server/game/AI/CoreAI/AreaTriggerAI.cpp index 6abe091bd02..73fc3ddcb6c 100644 --- a/src/server/game/AI/CoreAI/AreaTriggerAI.cpp +++ b/src/server/game/AI/CoreAI/AreaTriggerAI.cpp @@ -15,10 +15,12 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "AreaTrigger.h" #include "AreaTriggerAI.h" -AreaTriggerAI::AreaTriggerAI(AreaTrigger* a) : at(a) +AreaTriggerAI::AreaTriggerAI(AreaTrigger* a, uint32 scriptId) : _scriptId(scriptId ? scriptId : a->GetScriptId()), at(a) { + ASSERT(_scriptId, "A AreaTriggerAI was initialized with an invalid scriptId!"); } AreaTriggerAI::~AreaTriggerAI() diff --git a/src/server/game/AI/CoreAI/AreaTriggerAI.h b/src/server/game/AI/CoreAI/AreaTriggerAI.h index c0f33caad86..2153d577107 100644 --- a/src/server/game/AI/CoreAI/AreaTriggerAI.h +++ b/src/server/game/AI/CoreAI/AreaTriggerAI.h @@ -25,10 +25,12 @@ class Unit; class TC_GAME_API AreaTriggerAI { + uint32 _scriptId; + protected: AreaTrigger* const at; public: - explicit AreaTriggerAI(AreaTrigger* a); + explicit AreaTriggerAI(AreaTrigger* a, uint32 scriptId = {}); virtual ~AreaTriggerAI(); // Called when the AreaTrigger has just been initialized, just before added to map @@ -54,12 +56,15 @@ class TC_GAME_API AreaTriggerAI // Called when the AreaTrigger is removed virtual void OnRemove() { } + + // Gets the id of the AI (script id) + uint32 GetId() { return _scriptId; } }; class NullAreaTriggerAI : public AreaTriggerAI { public: - explicit NullAreaTriggerAI(AreaTrigger* areaTrigger) : AreaTriggerAI(areaTrigger) { } + using AreaTriggerAI::AreaTriggerAI; }; #endif diff --git a/src/server/game/AI/CoreAI/CombatAI.cpp b/src/server/game/AI/CoreAI/CombatAI.cpp index 5559408e387..f0696db80f3 100644 --- a/src/server/game/AI/CoreAI/CombatAI.cpp +++ b/src/server/game/AI/CoreAI/CombatAI.cpp @@ -188,7 +188,7 @@ void CasterAI::UpdateAI(uint32 diff) // ArcherAI ////////////// -ArcherAI::ArcherAI(Creature* c) : CreatureAI(c) +ArcherAI::ArcherAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId) { if (!me->m_spells[0]) TC_LOG_ERROR("misc", "ArcherAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry()); @@ -237,7 +237,7 @@ void ArcherAI::UpdateAI(uint32 /*diff*/) // TurretAI ////////////// -TurretAI::TurretAI(Creature* c) : CreatureAI(c) +TurretAI::TurretAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId) { if (!me->m_spells[0]) TC_LOG_ERROR("misc", "TurretAI set for creature (entry = %u) with spell1=0. AI will do nothing", me->GetEntry()); @@ -275,7 +275,7 @@ void TurretAI::UpdateAI(uint32 /*diff*/) // VehicleAI ////////////// -VehicleAI::VehicleAI(Creature* creature) : CreatureAI(creature), m_HasConditions(false), m_ConditionsTimer(VEHICLE_CONDITION_CHECK_TIME) +VehicleAI::VehicleAI(Creature* creature, uint32 scriptId) : CreatureAI(creature, scriptId), m_HasConditions(false), m_ConditionsTimer(VEHICLE_CONDITION_CHECK_TIME) { LoadConditions(); m_DoDismiss = false; diff --git a/src/server/game/AI/CoreAI/CombatAI.h b/src/server/game/AI/CoreAI/CombatAI.h index 27d80e38280..58a6414b00e 100644 --- a/src/server/game/AI/CoreAI/CombatAI.h +++ b/src/server/game/AI/CoreAI/CombatAI.h @@ -25,7 +25,7 @@ class Creature; class TC_GAME_API AggressorAI : public CreatureAI { public: - explicit AggressorAI(Creature* c) : CreatureAI(c) { } + using CreatureAI::CreatureAI; void UpdateAI(uint32) override; static int32 Permissible(Creature const* creature); @@ -36,7 +36,7 @@ typedef std::vector<uint32> SpellVct; class TC_GAME_API CombatAI : public CreatureAI { public: - explicit CombatAI(Creature* c) : CreatureAI(c) { } + using CreatureAI::CreatureAI; void InitializeAI() override; void Reset() override; @@ -55,7 +55,7 @@ class TC_GAME_API CombatAI : public CreatureAI class TC_GAME_API CasterAI : public CombatAI { public: - explicit CasterAI(Creature* c) : CombatAI(c) { m_attackDist = MELEE_RANGE; } + explicit CasterAI(Creature* c, uint32 scriptId = {}) : CombatAI(c, scriptId) { m_attackDist = MELEE_RANGE; } void InitializeAI() override; void AttackStart(Unit* victim) override { AttackStartCaster(victim, m_attackDist); } void UpdateAI(uint32 diff) override; @@ -67,7 +67,7 @@ class TC_GAME_API CasterAI : public CombatAI struct TC_GAME_API ArcherAI : public CreatureAI { public: - explicit ArcherAI(Creature* c); + explicit ArcherAI(Creature* c, uint32 scriptId = {}); void AttackStart(Unit* who) override; void UpdateAI(uint32 diff) override; @@ -80,7 +80,7 @@ struct TC_GAME_API ArcherAI : public CreatureAI struct TC_GAME_API TurretAI : public CreatureAI { public: - explicit TurretAI(Creature* c); + explicit TurretAI(Creature* c, uint32 scriptId = {}); bool CanAIAttack(Unit const* who) const override; void AttackStart(Unit* who) override; void UpdateAI(uint32 diff) override; @@ -97,7 +97,7 @@ struct TC_GAME_API TurretAI : public CreatureAI struct TC_GAME_API VehicleAI : public CreatureAI { public: - explicit VehicleAI(Creature* creature); + explicit VehicleAI(Creature* creature, uint32 scriptId = {}); void UpdateAI(uint32 diff) override; void MoveInLineOfSight(Unit*) override { } diff --git a/src/server/game/AI/CoreAI/GameObjectAI.cpp b/src/server/game/AI/CoreAI/GameObjectAI.cpp index 7ce811275f2..4dca8fbd59d 100644 --- a/src/server/game/AI/CoreAI/GameObjectAI.cpp +++ b/src/server/game/AI/CoreAI/GameObjectAI.cpp @@ -19,6 +19,12 @@ #include "CreatureAI.h" #include "GameObject.h" #include "QuestDef.h" +#include "Errors.h" + +GameObjectAI::GameObjectAI(GameObject* g, uint32 scriptId) : _scriptId(scriptId ? scriptId : g->GetScriptId()), me(g) +{ + ASSERT(_scriptId, "A GameObjectAI was initialized with an invalid scriptId!"); +} int32 GameObjectAI::Permissible(GameObject const* /*go*/) { @@ -30,8 +36,6 @@ Optional<QuestGiverStatus> GameObjectAI::GetDialogStatus(Player* /*player*/) return {}; } -NullGameObjectAI::NullGameObjectAI(GameObject* g) : GameObjectAI(g) { } - int32 NullGameObjectAI::Permissible(GameObject const* /*go*/) { return PERMIT_BASE_IDLE; diff --git a/src/server/game/AI/CoreAI/GameObjectAI.h b/src/server/game/AI/CoreAI/GameObjectAI.h index 41c29d93366..a44d8b1f3c6 100644 --- a/src/server/game/AI/CoreAI/GameObjectAI.h +++ b/src/server/game/AI/CoreAI/GameObjectAI.h @@ -33,12 +33,20 @@ enum class QuestGiverStatus : uint32; class TC_GAME_API GameObjectAI { + private: + // Script Id + uint32 const _scriptId; + protected: GameObject* const me; + public: - explicit GameObjectAI(GameObject* g) : me(g) { } + explicit GameObjectAI(GameObject* g, uint32 scriptId = {}); virtual ~GameObjectAI() { } + // Gets the id of the AI (script id) + uint32 GetId() const { return _scriptId; } + virtual void UpdateAI(uint32 /*diff*/) { } virtual void InitializeAI() { Reset(); } @@ -99,7 +107,7 @@ class TC_GAME_API GameObjectAI class TC_GAME_API NullGameObjectAI : public GameObjectAI { public: - explicit NullGameObjectAI(GameObject* g); + using GameObjectAI::GameObjectAI; void UpdateAI(uint32 /*diff*/) override { } diff --git a/src/server/game/AI/CoreAI/GuardAI.cpp b/src/server/game/AI/CoreAI/GuardAI.cpp index 2288c06122d..b455fdfab32 100644 --- a/src/server/game/AI/CoreAI/GuardAI.cpp +++ b/src/server/game/AI/CoreAI/GuardAI.cpp @@ -22,10 +22,6 @@ #include "MotionMaster.h" #include "Player.h" -GuardAI::GuardAI(Creature* creature) : ScriptedAI(creature) -{ -} - int32 GuardAI::Permissible(Creature const* creature) { if (creature->IsGuard()) diff --git a/src/server/game/AI/CoreAI/GuardAI.h b/src/server/game/AI/CoreAI/GuardAI.h index 037094cff4b..5cfc245de61 100644 --- a/src/server/game/AI/CoreAI/GuardAI.h +++ b/src/server/game/AI/CoreAI/GuardAI.h @@ -25,7 +25,7 @@ class Creature; class TC_GAME_API GuardAI : public ScriptedAI { public: - explicit GuardAI(Creature* creature); + using ScriptedAI::ScriptedAI; static int32 Permissible(Creature const* creature); void UpdateAI(uint32 diff) override; diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp index 61d66422dac..02f54b13cd3 100644 --- a/src/server/game/AI/CoreAI/PassiveAI.cpp +++ b/src/server/game/AI/CoreAI/PassiveAI.cpp @@ -18,9 +18,9 @@ #include "PassiveAI.h" #include "Creature.h" -PassiveAI::PassiveAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } -PossessedAI::PossessedAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } -NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); } +PassiveAI::PassiveAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId) { me->SetReactState(REACT_PASSIVE); } +PossessedAI::PossessedAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId) { me->SetReactState(REACT_PASSIVE); } +NullCreatureAI::NullCreatureAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId) { me->SetReactState(REACT_PASSIVE); } int32 NullCreatureAI::Permissible(Creature const* creature) { diff --git a/src/server/game/AI/CoreAI/PassiveAI.h b/src/server/game/AI/CoreAI/PassiveAI.h index 41d4c2bfb12..9dc33ec02b9 100644 --- a/src/server/game/AI/CoreAI/PassiveAI.h +++ b/src/server/game/AI/CoreAI/PassiveAI.h @@ -23,7 +23,7 @@ class TC_GAME_API PassiveAI : public CreatureAI { public: - explicit PassiveAI(Creature* c); + explicit PassiveAI(Creature* c, uint32 scriptId = {}); void MoveInLineOfSight(Unit*) override { } void AttackStart(Unit*) override { } @@ -35,7 +35,7 @@ class TC_GAME_API PassiveAI : public CreatureAI class TC_GAME_API PossessedAI : public CreatureAI { public: - explicit PossessedAI(Creature* c); + explicit PossessedAI(Creature* c, uint32 scriptId = {}); void MoveInLineOfSight(Unit*) override { } void AttackStart(Unit* target) override; @@ -53,7 +53,7 @@ class TC_GAME_API PossessedAI : public CreatureAI class TC_GAME_API NullCreatureAI : public CreatureAI { public: - explicit NullCreatureAI(Creature* c); + explicit NullCreatureAI(Creature* c, uint32 scriptId = {}); void MoveInLineOfSight(Unit*) override { } void AttackStart(Unit*) override { } @@ -67,7 +67,7 @@ class TC_GAME_API NullCreatureAI : public CreatureAI class TC_GAME_API CritterAI : public PassiveAI { public: - explicit CritterAI(Creature* c) : PassiveAI(c) { } + using PassiveAI::PassiveAI; void DamageTaken(Unit* done_by, uint32& /*damage*/) override; void EnterEvadeMode(EvadeReason why) override; @@ -78,7 +78,8 @@ class TC_GAME_API CritterAI : public PassiveAI class TC_GAME_API TriggerAI : public NullCreatureAI { public: - explicit TriggerAI(Creature* c) : NullCreatureAI(c) { } + using NullCreatureAI::NullCreatureAI; + void IsSummonedBy(Unit* summoner) override; static int32 Permissible(Creature const* creature); diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index 701bee39d35..de6c5c2308c 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -44,7 +44,7 @@ int32 PetAI::Permissible(Creature const* creature) return PERMIT_BASE_NO; } -PetAI::PetAI(Creature* c) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK) +PetAI::PetAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId), i_tracker(TIME_INTERVAL_LOOK) { if (!me->GetCharmInfo()) throw InvalidAIException("Creature doesn't have a valid charm info"); diff --git a/src/server/game/AI/CoreAI/PetAI.h b/src/server/game/AI/CoreAI/PetAI.h index e243d06debc..57cbc9ca404 100644 --- a/src/server/game/AI/CoreAI/PetAI.h +++ b/src/server/game/AI/CoreAI/PetAI.h @@ -30,7 +30,7 @@ class TC_GAME_API PetAI : public CreatureAI { public: - explicit PetAI(Creature* c); + explicit PetAI(Creature* c, uint32 scriptId = {}); void UpdateAI(uint32) override; static int32 Permissible(Creature const* creature); diff --git a/src/server/game/AI/CoreAI/ReactorAI.h b/src/server/game/AI/CoreAI/ReactorAI.h index be76f855781..08e89b10912 100644 --- a/src/server/game/AI/CoreAI/ReactorAI.h +++ b/src/server/game/AI/CoreAI/ReactorAI.h @@ -24,7 +24,7 @@ class TC_GAME_API ReactorAI : public CreatureAI { public: - explicit ReactorAI(Creature* c) : CreatureAI(c) { } + using CreatureAI::CreatureAI; void MoveInLineOfSight(Unit*) override { } void UpdateAI(uint32 diff) override; diff --git a/src/server/game/AI/CoreAI/TotemAI.cpp b/src/server/game/AI/CoreAI/TotemAI.cpp index fc499414587..7f36ae8e5bf 100644 --- a/src/server/game/AI/CoreAI/TotemAI.cpp +++ b/src/server/game/AI/CoreAI/TotemAI.cpp @@ -32,7 +32,7 @@ int32 TotemAI::Permissible(Creature const* creature) return PERMIT_BASE_NO; } -TotemAI::TotemAI(Creature* c) : CreatureAI(c), i_victimGuid() +TotemAI::TotemAI(Creature* c, uint32 scriptId) : CreatureAI(c, scriptId), i_victimGuid() { ASSERT(c->IsTotem()); } diff --git a/src/server/game/AI/CoreAI/TotemAI.h b/src/server/game/AI/CoreAI/TotemAI.h index fd25ca86df6..e4efea9dc7e 100644 --- a/src/server/game/AI/CoreAI/TotemAI.h +++ b/src/server/game/AI/CoreAI/TotemAI.h @@ -28,7 +28,7 @@ class TC_GAME_API TotemAI : public CreatureAI { public: - explicit TotemAI(Creature* c); + explicit TotemAI(Creature* c, uint32 scriptId = {}); void MoveInLineOfSight(Unit* who) override; void AttackStart(Unit* victim) override; diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index e3f3cabb5ef..e6d7dc2d1df 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -21,6 +21,7 @@ #include "Creature.h" #include "CreatureAIImpl.h" #include "CreatureTextMgr.h" +#include "Errors.h" #include "Language.h" #include "Log.h" #include "Map.h" @@ -48,8 +49,11 @@ AISpellInfoType* GetAISpellInfo(uint32 spellId, Difficulty difficulty) return Trinity::Containers::MapGetValuePtr(UnitAI::AISpellInfo, { spellId, difficulty }); } -CreatureAI::CreatureAI(Creature* creature) : UnitAI(creature), me(creature), _boundary(nullptr), _negateBoundary(false), m_MoveInLineOfSight_locked(false) +CreatureAI::CreatureAI(Creature* creature, uint32 scriptId) + : UnitAI(creature), me(creature), _boundary(nullptr), + _negateBoundary(false), _scriptId(scriptId ? scriptId : creature->GetScriptId()), m_MoveInLineOfSight_locked(false) { + ASSERT(_scriptId, "A CreatureAI was initialized with an invalid scriptId!"); } CreatureAI::~CreatureAI() diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index fe557f76529..1c6943a245c 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -67,10 +67,13 @@ class TC_GAME_API CreatureAI : public UnitAI EVADE_REASON_OTHER }; - explicit CreatureAI(Creature* creature); + explicit CreatureAI(Creature* creature, uint32 scriptId = {}); virtual ~CreatureAI(); + // Gets the id of the AI (script id) + uint32 GetId() const { return _scriptId; } + void Talk(uint8 id, WorldObject const* whisperTarget = nullptr); /// == Reactions At ================================= @@ -230,6 +233,8 @@ class TC_GAME_API CreatureAI : public UnitAI bool _negateBoundary; private: + uint32 const _scriptId; + bool m_MoveInLineOfSight_locked; void _OnOwnerCombatInteraction(Unit* target); }; diff --git a/src/server/game/AI/CreatureAIFactory.h b/src/server/game/AI/CreatureAIFactory.h index d8d9c5ea828..3d269416902 100644 --- a/src/server/game/AI/CreatureAIFactory.h +++ b/src/server/game/AI/CreatureAIFactory.h @@ -18,6 +18,7 @@ #ifndef TRINITY_CREATUREAIFACTORY_H #define TRINITY_CREATUREAIFACTORY_H +#include "ObjectMgr.h" #include "ObjectRegistry.h" #include "SelectableAI.h" @@ -25,13 +26,14 @@ class Creature; class CreatureAI; template <class REAL_AI, bool is_db_allowed = true> -struct CreatureAIFactory : public SelectableAI<Creature, CreatureAI, is_db_allowed> +struct CreatureAIFactory : public SelectableAI<Creature, CreatureAI> { - CreatureAIFactory(std::string const& name) : SelectableAI<Creature, CreatureAI, is_db_allowed>(name) { } + CreatureAIFactory(std::string const& name) + : SelectableAI<Creature, CreatureAI>(name, sObjectMgr->GetScriptId(name, false), is_db_allowed) { } inline CreatureAI* Create(Creature* c) const override { - return new REAL_AI(c); + return new REAL_AI(c, this->GetScriptId()); } int32 Permit(Creature const* c) const override diff --git a/src/server/game/AI/CreatureAIRegistry.cpp b/src/server/game/AI/CreatureAIRegistry.cpp index 2ae35369966..a1a2d3dbdb5 100644 --- a/src/server/game/AI/CreatureAIRegistry.cpp +++ b/src/server/game/AI/CreatureAIRegistry.cpp @@ -25,6 +25,7 @@ #include "ReactorAI.h" #include "SmartAI.h" #include "TotemAI.h" +#include "ObjectMgr.h" #include "MovementGenerator.h" @@ -37,6 +38,7 @@ namespace AIRegistry (new CreatureAIFactory<AggressorAI>("AggressorAI"))->RegisterSelf(); (new CreatureAIFactory<ReactorAI>("ReactorAI"))->RegisterSelf(); (new CreatureAIFactory<PassiveAI>("PassiveAI"))->RegisterSelf(); + (new CreatureAIFactory<PossessedAI, false>("PossessedAI"))->RegisterSelf(); (new CreatureAIFactory<CritterAI>("CritterAI"))->RegisterSelf(); (new CreatureAIFactory<GuardAI>("GuardAI"))->RegisterSelf(); (new CreatureAIFactory<PetAI, false>("PetAI"))->RegisterSelf(); @@ -54,5 +56,7 @@ namespace AIRegistry (new IdleMovementFactory())->RegisterSelf(); (new RandomMovementFactory())->RegisterSelf(); (new WaypointMovementFactory())->RegisterSelf(); + + (void)sObjectMgr->GetScriptId("NullAreaTriggerAI", false); } } diff --git a/src/server/game/AI/CreatureAISelector.cpp b/src/server/game/AI/CreatureAISelector.cpp index 99eb3b05ff6..2da8ec2631b 100644 --- a/src/server/game/AI/CreatureAISelector.cpp +++ b/src/server/game/AI/CreatureAISelector.cpp @@ -16,6 +16,7 @@ */ #include "AIException.h" +#include "AreaTrigger.h" #include "Creature.h" #include "CreatureAISelector.h" #include "CreatureAIFactory.h" @@ -25,6 +26,8 @@ #include "GameObject.h" #include "GameObjectAIFactory.h" +#include "AreaTriggerAI.h" + #include "ScriptMgr.h" namespace FactorySelector @@ -53,7 +56,7 @@ namespace FactorySelector }; template <class AI, class T> - inline FactoryHolder<AI, T> const* SelectFactory(T* obj) + inline FactoryHolder<AI, T> const* SelectFactory(T const* obj) { static_assert(std::is_same<AI, CreatureAI>::value || std::is_same<AI, GameObjectAI>::value, "Invalid template parameter"); static_assert(std::is_same<AI, CreatureAI>::value == std::is_same<T, Creature>::value, "Incompatible AI for type"); @@ -98,6 +101,31 @@ namespace FactorySelector return SelectFactory<CreatureAI>(creature)->Create(creature); } + uint32 GetSelectedAIId(Creature const* creature) + { + if (creature->IsPet()) + { + auto const* registry = ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PetAI")); + auto const* factory = dynamic_cast<SelectableAI<Creature, CreatureAI> const*>(registry); + ASSERT(factory); + + return factory->GetScriptId(); + } + + if (uint32 id = creature->GetScriptId()) + { + if (sScriptMgr->CanCreateCreatureAI(id)) + { + return id; + } + } + + auto const* factory = dynamic_cast<SelectableAI<Creature, CreatureAI> const*>(SelectFactory<CreatureAI>(creature)); + ASSERT(factory); + + return factory->GetScriptId(); + } + MovementGenerator* SelectMovementGenerator(Unit* unit) { MovementGeneratorType type = unit->GetDefaultMovementType(); @@ -117,4 +145,46 @@ namespace FactorySelector return SelectFactory<GameObjectAI>(go)->Create(go); } + + uint32 GetSelectedAIId(GameObject const* go) + { + if (uint32 id = go->GetScriptId()) + { + if (sScriptMgr->CanCreateGameObjectAI(id)) + { + return id; + } + } + + auto const* factory = dynamic_cast<SelectableAI<GameObject, GameObjectAI> const*>(SelectFactory<GameObjectAI>(go)); + ASSERT(factory); + + return factory->GetScriptId(); + } + + static uint32 GetNullAreaTriggerAIScriptId() + { + return sObjectMgr->GetScriptId("NullAreaTriggerAI", false); + } + + AreaTriggerAI* SelectAreaTriggerAI(AreaTrigger* at) + { + if (AreaTriggerAI* ai = sScriptMgr->GetAreaTriggerAI(at)) + return ai; + else + return new NullAreaTriggerAI(at, GetNullAreaTriggerAIScriptId()); + } + + uint32 GetSelectedAIId(AreaTrigger const* at) + { + if (uint32 id = at->GetScriptId()) + { + if (sScriptMgr->CanCreateAreaTriggerAI(id)) + { + return id; + } + } + + return GetNullAreaTriggerAIScriptId(); + } } diff --git a/src/server/game/AI/CreatureAISelector.h b/src/server/game/AI/CreatureAISelector.h index b4046a5e982..d8447f6f350 100644 --- a/src/server/game/AI/CreatureAISelector.h +++ b/src/server/game/AI/CreatureAISelector.h @@ -24,11 +24,18 @@ class MovementGenerator; class Unit; class GameObjectAI; class GameObject; +class AreaTriggerAI; +class AreaTrigger; namespace FactorySelector { TC_GAME_API CreatureAI* SelectAI(Creature* creature); TC_GAME_API MovementGenerator* SelectMovementGenerator(Unit* unit); TC_GAME_API GameObjectAI* SelectGameObjectAI(GameObject* go); + TC_GAME_API AreaTriggerAI* SelectAreaTriggerAI(AreaTrigger* at); + + TC_GAME_API uint32 GetSelectedAIId(Creature const* creature); + TC_GAME_API uint32 GetSelectedAIId(GameObject const* go); + TC_GAME_API uint32 GetSelectedAIId(AreaTrigger const* at); } #endif diff --git a/src/server/game/AI/GameObjectAIFactory.h b/src/server/game/AI/GameObjectAIFactory.h index 99d3214b5de..6a66a59f967 100644 --- a/src/server/game/AI/GameObjectAIFactory.h +++ b/src/server/game/AI/GameObjectAIFactory.h @@ -25,13 +25,14 @@ class GameObject; class GameObjectAI; template <class REAL_GO_AI, bool is_db_allowed = true> -struct GameObjectAIFactory : public SelectableAI<GameObject, GameObjectAI, is_db_allowed> +struct GameObjectAIFactory : public SelectableAI<GameObject, GameObjectAI> { - GameObjectAIFactory(std::string const& name) : SelectableAI<GameObject, GameObjectAI, is_db_allowed>(name) { } + GameObjectAIFactory(std::string const& name) + : SelectableAI<GameObject, GameObjectAI>(name, sObjectMgr->GetScriptId(name, false), is_db_allowed) { } GameObjectAI* Create(GameObject* go) const override { - return new REAL_GO_AI(go); + return new REAL_GO_AI(go, GetScriptId()); } int32 Permit(GameObject const* go) const override diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 5042dcd6144..7c13e6e26ff 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -116,7 +116,9 @@ void SummonList::DoActionImpl(int32 action, StorageType const& summons) } } -ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature), +ScriptedAI::ScriptedAI(Creature* creature) : ScriptedAI(creature, creature->GetScriptId()) { } + +ScriptedAI::ScriptedAI(Creature* creature, uint32 scriptId) : CreatureAI(creature, scriptId), IsFleeing(false), _isCombatMovementAllowed(true) { diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index b30bcd6a83b..3dfb133a916 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -134,6 +134,7 @@ class TC_GAME_API DummyEntryCheckPredicate struct TC_GAME_API ScriptedAI : public CreatureAI { explicit ScriptedAI(Creature* creature); + explicit ScriptedAI(Creature* creature, uint32 scriptId); virtual ~ScriptedAI() { } // ************* diff --git a/src/server/game/AI/SelectableAI.h b/src/server/game/AI/SelectableAI.h index 7b28049ffd5..fc6ec88af5c 100644 --- a/src/server/game/AI/SelectableAI.h +++ b/src/server/game/AI/SelectableAI.h @@ -27,12 +27,19 @@ class DBPermit virtual bool IsScriptNameAllowedInDB() const = 0; }; -template <class O, class AI, bool is_db_allowed = true> +template <class O, class AI> struct SelectableAI : public FactoryHolder<AI, O>, public Permissible<O>, public DBPermit { - SelectableAI(std::string const& name) : FactoryHolder<AI, O>(name), Permissible<O>(), DBPermit() { } + SelectableAI(std::string const& name, uint32 scriptId, bool isDBAllowed) + : FactoryHolder<AI, O>(name), Permissible<O>(), DBPermit(), _scriptId(scriptId), _isDBAllowed(isDBAllowed) { } - bool IsScriptNameAllowedInDB() const final override { return is_db_allowed; } + bool IsScriptNameAllowedInDB() const final override { return _isDBAllowed; } + + uint32 GetScriptId() const { return _scriptId; } + +private: + uint32 _scriptId; + bool _isDBAllowed; }; diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index ca3d95e5aeb..9ab05c55271 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -30,7 +30,7 @@ #include "ScriptMgr.h" #include "Vehicle.h" -SmartAI::SmartAI(Creature* creature) : CreatureAI(creature), mIsCharmed(false), mFollowCreditType(0), mFollowArrivedTimer(0), mFollowCredit(0), mFollowArrivedEntry(0), mFollowDist(0.f), mFollowAngle(0.f), +SmartAI::SmartAI(Creature* creature, uint32 scriptId) : CreatureAI(creature, scriptId), mIsCharmed(false), mFollowCreditType(0), mFollowArrivedTimer(0), mFollowCredit(0), mFollowArrivedEntry(0), mFollowDist(0.f), mFollowAngle(0.f), _escortState(SMART_ESCORT_NONE), _escortNPCFlags(0), _escortInvokerCheckTimer(1000), _currentWaypointNode(0), _waypointReached(false), _waypointPauseTimer(0), _waypointPauseForced(false), _repeatWaypointPath(false), _OOCReached(false), _waypointPathEnded(false), mRun(true), mEvadeDisabled(false), mCanAutoAttack(true), mCanCombatMove(true), mInvincibilityHpLevel(0), mDespawnTime(0), mDespawnState(0), mConditionsTimer(0), _gossipReturn(false), mEscortQuestID(0) diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h index f0967af9109..6c40f429c13 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.h +++ b/src/server/game/AI/SmartScripts/SmartAI.h @@ -44,7 +44,7 @@ class TC_GAME_API SmartAI : public CreatureAI { public: ~SmartAI() { } - explicit SmartAI(Creature* c); + explicit SmartAI(Creature* c, uint32 scriptId = {}); // core related static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; } @@ -247,7 +247,7 @@ class TC_GAME_API SmartAI : public CreatureAI class TC_GAME_API SmartGameObjectAI : public GameObjectAI { public: - SmartGameObjectAI(GameObject* g) : GameObjectAI(g), _gossipReturn(false) { } + SmartGameObjectAI(GameObject* g, uint32 scriptId = {}) : GameObjectAI(g, scriptId), _gossipReturn(false) { } ~SmartGameObjectAI() { } void UpdateAI(uint32 diff) override; diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index aef00f3d236..e01b9034cd8 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -786,6 +786,7 @@ enum RBACPermissions // 878-879 previously used, do not reuse RBAC_PERM_COMMAND_PDUMP_COPY = 880, RBAC_PERM_COMMAND_RELOAD_VEHICLE_TEMPLATE = 881, + RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPT_NAMES = 882, // // IF YOU ADD NEW PERMISSIONS, ADD THEM IN 3.3.5 BRANCH AS WELL! // diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index ae69a5ed902..cd4c495b64e 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -21,6 +21,7 @@ #include "AreaTriggerPackets.h" #include "CellImpl.h" #include "Chat.h" +#include "CreatureAISelector.h" #include "DB2Stores.h" #include "GridNotifiersImpl.h" #include "Language.h" @@ -948,11 +949,7 @@ void AreaTrigger::DebugVisualizePosition() void AreaTrigger::AI_Initialize() { AI_Destroy(); - AreaTriggerAI* ai = sScriptMgr->GetAreaTriggerAI(this); - if (!ai) - ai = new NullAreaTriggerAI(this); - - _ai.reset(ai); + _ai.reset(FactorySelector::SelectAreaTriggerAI(this)); _ai->OnInitialize(); } @@ -961,6 +958,7 @@ void AreaTrigger::AI_Destroy() _ai.reset(); } + void AreaTrigger::BuildValuesCreate(ByteBuffer* data, Player const* target) const { UF::UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index bf2a9a1fc1f..f1c7b99c186 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -34,6 +34,7 @@ #include "Creature.h" #include "CreatureAI.h" #include "CreatureAIImpl.h" +#include "CreatureAIFactory.h" #include "CreatureGroups.h" #include "DB2Stores.h" #include "Formulas.h" @@ -9181,9 +9182,9 @@ void Unit::UpdateCharmAI() { i_disabledAI = i_AI; if (isPossessed() || IsVehicle()) - i_AI = new PossessedAI(ToCreature()); + i_AI = ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PossessedAI"))->Create(ToCreature()); else - i_AI = new PetAI(ToCreature()); + i_AI = ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PetAI"))->Create(ToCreature()); } } break; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index b033d6b7896..b1bf17ec7d7 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -53,6 +53,7 @@ #include "Random.h" #include "ReputationMgr.h" #include "ScriptMgr.h" +#include "ScriptReloadMgr.h" #include "SpellInfo.h" #include "SpellMgr.h" #include "SpellScript.h" @@ -64,6 +65,7 @@ #include "World.h" #include <G3D/g3dmath.h> #include <numeric> +#include <limits> ScriptMapMap sSpellScripts; ScriptMapMap sEventScripts; @@ -9492,16 +9494,39 @@ bool ObjectMgr::IsVendorItemValid(uint32 vendor_entry, VendorItem const& vItem, return true; } +ObjectMgr::ScriptNameContainer::ScriptNameContainer() +{ + // We insert an empty placeholder here so we can use the + // script id 0 as dummy for "no script found". + uint32 const id = insert("", false); + + ASSERT(id == 0); + (void)id; +} + void ObjectMgr::ScriptNameContainer::reserve(size_t capacity) { IndexToName.reserve(capacity); } -void ObjectMgr::ScriptNameContainer::insert(std::string&& scriptName) +uint32 ObjectMgr::ScriptNameContainer::insert(std::string const& scriptName, bool isScriptNameBound) { - auto insertResult = NameToIndex.insert({ std::move(scriptName), NameToIndex.size() }); - if (insertResult.second) - IndexToName.push_back(insertResult.first); + // c++17 try_emplace + auto const itr = NameToIndex.find(scriptName); + if (itr != NameToIndex.end()) + { + return itr->second.Id; + } + else + { + ASSERT(NameToIndex.size() < std::numeric_limits<uint32>::max()); + uint32 const id = static_cast<uint32>(NameToIndex.size()); + + auto result = NameToIndex.insert({ scriptName, Entry{ id, isScriptNameBound } }); + IndexToName.emplace_back(result.first); + + return id; + } } size_t ObjectMgr::ScriptNameContainer::size() const @@ -9509,111 +9534,75 @@ size_t ObjectMgr::ScriptNameContainer::size() const return IndexToName.size(); } -std::string const& ObjectMgr::ScriptNameContainer::operator[](size_t index) const +ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::find(size_t index) const { - static std::string const empty; - return index < IndexToName.size() ? IndexToName[index]->first : empty; + return index < IndexToName.size() ? IndexToName[index] : end(); } -uint32 ObjectMgr::ScriptNameContainer::operator[](std::string const& name) const +ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::find(std::string const& name) const { // assume "" is the first element if (name.empty()) - return 0; + return end(); - if (uint32 const* id = Trinity::Containers::MapGetValuePtr(NameToIndex, name)) - return *id; + return NameToIndex.find(name); +} - return 0; +ObjectMgr::ScriptNameContainer::NameMap::const_iterator ObjectMgr::ScriptNameContainer::end() const +{ + return NameToIndex.end(); } -std::unordered_set<std::string> ObjectMgr::ScriptNameContainer::GetAllScriptNames() const +std::unordered_set<std::string> ObjectMgr::ScriptNameContainer::GetAllDBScriptNames() const { std::unordered_set<std::string> scriptNames; - std::transform(NameToIndex.begin(), NameToIndex.end(), std::inserter(scriptNames, scriptNames.end()), - [](std::pair<std::string const, uint32> const& pair) + + for (std::pair<std::string const, Entry> const& entry : NameToIndex) { - return pair.first; - }); + if (entry.second.IsScriptDatabaseBound) + { + scriptNames.insert(entry.first); + } + } return scriptNames; } -void ObjectMgr::LoadScriptNames() +std::unordered_set<std::string> ObjectMgr::GetAllDBScriptNames() const { - uint32 oldMSTime = getMSTime(); - - // We insert an empty placeholder here so we can use the - // script id 0 as dummy for "no script found". - _scriptNamesStore.insert(""); - - QueryResult result = WorldDatabase.Query( - "SELECT DISTINCT(ScriptName) FROM battleground_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM conversation_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM creature WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM creature_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM criteria_data WHERE ScriptName <> '' AND type = 11 " - "UNION " - "SELECT DISTINCT(ScriptName) FROM gameobject WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM gameobject_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM item_script_names WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM areatrigger_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM spell_script_names WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM transports WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM game_weather WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM conditions WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM outdoorpvp_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM scene_template WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(ScriptName) FROM quest_template_addon WHERE ScriptName <> '' " - "UNION " - "SELECT DISTINCT(script) FROM instance_template WHERE script <> ''"); + return _scriptNamesStore.GetAllDBScriptNames(); +} - if (!result) +std::string const& ObjectMgr::GetScriptName(uint32 id) const +{ + auto const itr = _scriptNamesStore.find(id); + if (itr != _scriptNamesStore.end()) { - TC_LOG_INFO("server.loading", ">> Loaded empty set of Script Names!"); - return; + return itr->first; } - - _scriptNamesStore.reserve(result->GetRowCount() + 1); - - do + else { - _scriptNamesStore.insert((*result)[0].GetString()); + static std::string const empty; + return empty; } - while (result->NextRow()); - - TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " ScriptNames in %u ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime)); -} - -std::unordered_set<std::string> ObjectMgr::GetAllScriptNames() const -{ - return _scriptNamesStore.GetAllScriptNames(); } -std::string const& ObjectMgr::GetScriptName(uint32 id) const +bool ObjectMgr::IsScriptDatabaseBound(uint32 id) const { - return _scriptNamesStore[id]; + auto const itr = _scriptNamesStore.find(id); + if (itr != _scriptNamesStore.end()) + { + return itr->second.IsScriptDatabaseBound; + } + else + { + return false; + } } -uint32 ObjectMgr::GetScriptId(std::string const& name) +uint32 ObjectMgr::GetScriptId(std::string const& name, bool isDatabaseBound) { - return _scriptNamesStore[name]; + return _scriptNamesStore.insert(name, isDatabaseBound); } CreatureBaseStats const* ObjectMgr::GetCreatureBaseStats(uint8 level, uint8 unitClass) diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index b555dd2fa52..6d21323be11 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -1034,19 +1034,30 @@ class TC_GAME_API ObjectMgr class ScriptNameContainer { - using NameMap = std::map<std::string, uint32>; + public: + struct Entry + { + uint32 Id; + bool IsScriptDatabaseBound; + }; + + private: + using NameMap = std::map<std::string, Entry>; NameMap NameToIndex; std::vector<NameMap::const_iterator> IndexToName; public: + ScriptNameContainer(); + void reserve(size_t capacity); - void insert(std::string&& scriptName); + uint32 insert(std::string const& scriptName, bool isScriptNameBound = true); size_t size() const; - std::string const& operator[](size_t index) const; - uint32 operator[](std::string const& name) const; + NameMap::const_iterator find(size_t index) const; + NameMap::const_iterator find(std::string const& name) const; + NameMap::const_iterator end() const; - std::unordered_set<std::string> GetAllScriptNames() const; + std::unordered_set<std::string> GetAllDBScriptNames() const; }; typedef std::map<uint32, uint32> CharacterConversionMap; @@ -1602,10 +1613,10 @@ class TC_GAME_API ObjectMgr bool RemoveVendorItem(uint32 entry, uint32 item, uint8 type, bool persist = true); // for event bool IsVendorItemValid(uint32 vendor_entry, VendorItem const& vItem, Player* player = nullptr, std::set<uint32>* skip_vendors = nullptr, uint32 ORnpcflag = 0) const; - void LoadScriptNames(); - std::unordered_set<std::string> GetAllScriptNames() const; + std::unordered_set<std::string> GetAllDBScriptNames() const; std::string const& GetScriptName(uint32 id) const; - uint32 GetScriptId(std::string const& name); + bool IsScriptDatabaseBound(uint32 id) const; + uint32 GetScriptId(std::string const& name, bool isDatabaseBound = true); Trinity::IteratorPair<SpellClickInfoContainer::const_iterator> GetSpellClickInfoMapBounds(uint32 creature_id) const { diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index f77b93c0d5e..b5e587eb149 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -23,6 +23,7 @@ #include "Creature.h" #include "CreatureAI.h" #include "CreatureAIImpl.h" +#include "CreatureAISelector.h" #include "DB2Stores.h" #include "Errors.h" #include "GameObject.h" @@ -153,6 +154,9 @@ public: /// Unloads the script registry. virtual void Unload() = 0; + + /// Updates the scripts to reflect the current id + virtual void SyncScriptNames() = 0; }; template<class> @@ -241,6 +245,12 @@ public: registry->Unload(); } + void SyncScriptNames() final override + { + for (auto const registry : _registries) + registry->SyncScriptNames(); + } + template<typename T> void QueueForDelayedDelete(T&& any) { @@ -330,6 +340,9 @@ public: /// Called before Unload virtual void BeforeUnload() { } + + /// Called manually to sync scriptnames + virtual void OnScriptNamesSync() { }; }; template<typename ScriptType, typename Base> @@ -518,38 +531,84 @@ class CreatureGameObjectAreaTriggerScriptRegistrySwapHooks return map->GetAreaTrigger(guid); } - template<typename T> - static void VisitObjectsToSwapOnMap(Map* map, std::unordered_set<uint32> const& idsToRemove, T visitor) + static auto VisitObjectsToSwapOnMap(std::unordered_set<uint32> const& idsToRemove) { - auto evaluator = [&](std::unordered_map<ObjectGuid, ObjectType*>& objects) + return [&idsToRemove](Map* map, auto&& visitor) { - for (auto object : objects) + auto evaluator = [&](std::unordered_map<ObjectGuid, ObjectType*>& objects) { - // When the script Id of the script isn't removed in this - // context change, do nothing. - if (idsToRemove.find(object.second->GetScriptId()) != idsToRemove.end()) - visitor(object.second); - } + for (auto object : objects) + { + // When the script Id of the script isn't removed in this + // context change, do nothing. + uint32 aiId = object.second->AI() ? object.second->AI()->GetId() : 0; + if (idsToRemove.find(aiId) != idsToRemove.end()) + visitor(object.second); + } + }; + + AIFunctionMapWorker<typename std::decay<decltype(evaluator)>::type> worker(std::move(evaluator)); + TypeContainerVisitor<decltype(worker), MapStoredObjectTypesContainer> containerVisitor(worker); + + containerVisitor.Visit(map->GetObjectsStore()); }; + } - AIFunctionMapWorker<typename std::decay<decltype(evaluator)>::type> worker(std::move(evaluator)); - TypeContainerVisitor<decltype(worker), MapStoredObjectTypesContainer> containerVisitor(worker); + static auto VisitObjectsWhereIdWasUpdated() + { + return [](Map* map, auto&& visitor) + { + auto evaluator = [&](std::unordered_map<ObjectGuid, ObjectType*>& objects) + { + for (auto object : objects) + { + if (object.second->AI()) + { + ASSERT(object.second->AI()->GetId()); + + uint32 aiId = object.second->AI()->GetId(); + uint32 scriptId = FactorySelector::GetSelectedAIId(object.second); + + ASSERT(scriptId); + + if (aiId == scriptId) + { + // Skip if the ai id matches + continue; + } + + if (!sObjectMgr->IsScriptDatabaseBound(scriptId) + && !sObjectMgr->IsScriptDatabaseBound(aiId)) + { + // Skip if we are dealing with two selectable AI scripts + continue; + } + + visitor(object.second); + } + else + visitor(object.second); + } + }; - containerVisitor.Visit(map->GetObjectsStore()); + AIFunctionMapWorker<typename std::decay<decltype(evaluator)>::type> worker(std::move(evaluator)); + TypeContainerVisitor<decltype(worker), MapStoredObjectTypesContainer> containerVisitor(worker); + + containerVisitor.Visit(map->GetObjectsStore()); + }; } - static void DestroyScriptIdsFromSet(std::unordered_set<uint32> const& idsToRemove) + template<typename T> + static void DestroyScriptIdsWithVisitor(T&& visitor) { // First reset all swapped scripts safe by guid - // Skip creatures and gameobjects with an empty guid - // (that were not added to the world as of now) sMapMgr->DoForAllMaps([&](Map* map) { std::vector<ObjectGuid> guidsToReset; - VisitObjectsToSwapOnMap(map, idsToRemove, [&](ObjectType* object) + visitor(map, [&](ObjectType* object) { - if (object->AI() && !object->GetGUID().IsEmpty()) + if (object->AI()) guidsToReset.push_back(object->GetGUID()); }); @@ -559,7 +618,7 @@ class CreatureGameObjectAreaTriggerScriptRegistrySwapHooks UnloadResetScript(entity); } - VisitObjectsToSwapOnMap(map, idsToRemove, [&](ObjectType* object) + visitor(map, [&](ObjectType* object) { // Destroy the scripts instantly UnloadDestroyScript(object); @@ -567,15 +626,16 @@ class CreatureGameObjectAreaTriggerScriptRegistrySwapHooks }); } - static void InitializeScriptIdsFromSet(std::unordered_set<uint32> const& idsToRemove) + template<typename T> + static void InitializeScriptIdsWithVisitor(T&& visitor) { sMapMgr->DoForAllMaps([&](Map* map) { std::vector<ObjectGuid> guidsToReset; - VisitObjectsToSwapOnMap(map, idsToRemove, [&](ObjectType* object) + visitor(map, [&](ObjectType* object) { - if (!object->AI() && !object->GetGUID().IsEmpty()) + if (!object->AI()) { // Initialize the script LoadInitializeScript(object); @@ -601,7 +661,7 @@ public: void BeforeReleaseContext(std::string const& context) final override { auto idsToRemove = static_cast<Base*>(this)->GetScriptIDsToRemove(context); - DestroyScriptIdsFromSet(idsToRemove); + DestroyScriptIdsWithVisitor(VisitObjectsToSwapOnMap(idsToRemove)); // Add the new ids which are removed to the global ids to remove set ids_removed_.insert(idsToRemove.begin(), idsToRemove.end()); @@ -618,8 +678,9 @@ public: ids_removed_.insert(static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().begin(), static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().end()); - DestroyScriptIdsFromSet(ids_removed_); - InitializeScriptIdsFromSet(ids_removed_); + auto const visitor = VisitObjectsToSwapOnMap(ids_removed_); + DestroyScriptIdsWithVisitor(visitor); + InitializeScriptIdsWithVisitor(visitor); ids_removed_.clear(); } @@ -629,6 +690,13 @@ public: ASSERT(ids_removed_.empty()); } + void OnScriptNamesSync() final override + { + auto const visitor = VisitObjectsWhereIdWasUpdated(); + DestroyScriptIdsWithVisitor(visitor); + InitializeScriptIdsWithVisitor(visitor); + } + private: std::unordered_set<uint32> ids_removed_; }; @@ -876,6 +944,11 @@ public: _ids_of_contexts.clear(); } + void SyncScriptNames() final override + { + this->OnScriptNamesSync(); + } + // Adds a database bound script void AddScript(ScriptType* script) { @@ -886,46 +959,33 @@ public: std::unique_ptr<ScriptType> script_ptr(script); - // Get an ID for the script. An ID only exists if it's a script that is assigned in the database - // through a script name (or similar). - if (uint32 const id = sObjectMgr->GetScriptId(script->GetName())) + // Get an ID for the script. + uint32 const id = sObjectMgr->GetScriptId(script->GetName()); + + // Try to find an existing script. + for (auto const& stored_script : _scripts) { - // Try to find an existing script. - for (auto const& stored_script : _scripts) + // If the script names match... + if (stored_script.second->GetName() == script->GetName()) { - // If the script names match... - if (stored_script.second->GetName() == script->GetName()) - { - // If the script is already assigned -> delete it! - TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, " - "so the script can't work.", script->GetName().c_str()); - - // Error that should be fixed ASAP. - sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr)); - ABORT(); - return; - } - } - - // If the script isn't assigned -> assign it! - _scripts.insert(std::make_pair(id, std::move(script_ptr))); - _ids_of_contexts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), id)); - _recently_added_ids.insert(id); + // If the script is already assigned -> delete it! + TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, " + "so the script can't work.", script->GetName().c_str()); - sScriptRegistryCompositum->SetScriptNameInContext(script->GetName(), - sScriptMgr->GetCurrentScriptContext()); + // Error that should be fixed ASAP. + sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr)); + ABORT(); + return; + } } - else - { - // The script uses a script name from database, but isn't assigned to anything. - TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.", - script->GetName().c_str()); - // Avoid calling "delete script;" because we are currently in the script constructor - // In a valid scenario this will not happen because every script has a name assigned in the database - sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr)); - return; - } + // If the script isn't assigned -> assign it! + _scripts.insert(std::make_pair(id, std::move(script_ptr))); + _ids_of_contexts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), id)); + _recently_added_ids.insert(id); + + sScriptRegistryCompositum->SetScriptNameInContext(script->GetName(), + sScriptMgr->GetCurrentScriptContext()); } // Gets a script by its ID (assigned by ObjectMgr). @@ -1034,6 +1094,10 @@ public: _scripts.clear(); } + void SyncScriptNames() final override + { + } + // Adds a non database bound script void AddScript(ScriptType* script) { @@ -1117,7 +1181,7 @@ ScriptObject::~ScriptObject() } ScriptMgr::ScriptMgr() - : _scriptCount(0), _script_loader_callback(nullptr) + : _scriptCount(0), _scriptIdUpdated(false), _script_loader_callback(nullptr) { } @@ -1165,7 +1229,7 @@ void ScriptMgr::Initialize() sScriptMgr->SwapScriptContext(true); // Print unused script names. - std::unordered_set<std::string> unusedScriptNames = sObjectMgr->GetAllScriptNames(); + std::unordered_set<std::string> unusedScriptNames = sObjectMgr->GetAllDBScriptNames(); // Remove the used scripts from the given container. sScriptRegistryCompositum->RemoveUsedScriptsFromContainer(unusedScriptNames); @@ -1185,6 +1249,20 @@ void ScriptMgr::Initialize() GetScriptCount(), GetMSTimeDiffToNow(oldMSTime)); } +void ScriptMgr::NotifyScriptIDUpdate() +{ + _scriptIdUpdated = true; +} + +void ScriptMgr::SyncScripts() +{ + if (_scriptIdUpdated) + { + _scriptIdUpdated = false; + sScriptRegistryCompositum->SyncScriptNames(); + } +} + void ScriptMgr::SetScriptContext(std::string const& context) { _currentContext = context; @@ -1598,6 +1676,11 @@ bool ScriptMgr::OnCastItemCombatSpell(Player* player, Unit* victim, SpellInfo co return tmpscript->OnCastItemCombatSpell(player, victim, spellInfo, item); } +bool ScriptMgr::CanCreateCreatureAI(uint32 scriptId) const +{ + return !!ScriptRegistry<CreatureScript>::Instance()->GetScriptById(scriptId); +} + CreatureAI* ScriptMgr::GetCreatureAI(Creature* creature) { ASSERT(creature); @@ -1606,6 +1689,11 @@ CreatureAI* ScriptMgr::GetCreatureAI(Creature* creature) return tmpscript->GetAI(creature); } +bool ScriptMgr::CanCreateGameObjectAI(uint32 scriptId) const +{ + return !!ScriptRegistry<GameObjectScript>::Instance()->GetScriptById(scriptId); +} + GameObjectAI* ScriptMgr::GetGameObjectAI(GameObject* gameobject) { ASSERT(gameobject); @@ -1614,6 +1702,11 @@ GameObjectAI* ScriptMgr::GetGameObjectAI(GameObject* gameobject) return tmpscript->GetAI(gameobject); } +bool ScriptMgr::CanCreateAreaTriggerAI(uint32 scriptId) const +{ + return !!ScriptRegistry<AreaTriggerEntityScript>::Instance()->GetScriptById(scriptId); +} + AreaTriggerAI* ScriptMgr::GetAreaTriggerAI(AreaTrigger* areatrigger) { ASSERT(areatrigger); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 9b4bd01e2b6..863661022af 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -901,6 +901,12 @@ class TC_GAME_API ScriptMgr _script_loader_callback = script_loader_callback; } + public: /* Updating script ids */ + /// Inform the ScriptMgr that an entity has a changed script id + void NotifyScriptIDUpdate(); + /// Synchronize all scripts with their current ids + void SyncScripts(); + public: /* Script contexts */ /// Set the current script context, which allows the ScriptMgr /// to accept new scripts in this context. @@ -989,10 +995,12 @@ class TC_GAME_API ScriptMgr public: /* CreatureScript */ + bool CanCreateCreatureAI(uint32 scriptId) const; CreatureAI* GetCreatureAI(Creature* creature); public: /* GameObjectScript */ + bool CanCreateGameObjectAI(uint32 scriptId) const; GameObjectAI* GetGameObjectAI(GameObject* go); public: /* AreaTriggerScript */ @@ -1130,6 +1138,7 @@ class TC_GAME_API ScriptMgr public: /* AreaTriggerEntityScript */ + bool CanCreateAreaTriggerAI(uint32 scriptId) const; AreaTriggerAI* GetAreaTriggerAI(AreaTrigger* areaTrigger); public: /* ConversationScript */ @@ -1152,6 +1161,7 @@ class TC_GAME_API ScriptMgr private: uint32 _scriptCount; + bool _scriptIdUpdated; ScriptLoaderCallbackType _script_loader_callback; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 212ae90e00b..717100a1f45 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1781,9 +1781,6 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading GameObject models..."); LoadGameObjectModelList(m_dataPath); - TC_LOG_INFO("server.loading", "Loading Script Names..."); - sObjectMgr->LoadScriptNames(); - TC_LOG_INFO("server.loading", "Loading Instance Template..."); sObjectMgr->LoadInstanceTemplate(); @@ -2546,6 +2543,9 @@ void World::Update(uint32 diff) m_timers[WUPDATE_AHBOT].Reset(); } + /// Synchronize all scripts with their ids before updating the sScriptReloadMgr + sScriptMgr->SyncScripts(); + /// <li> Handle file changes if (m_timers[WUPDATE_CHECK_FILECHANGES].Passed()) { diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 3e8a76b57c0..16bfea63ad5 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -41,6 +41,7 @@ EndScriptData */ #include "LootMgr.h" #include "MapManager.h" #include "ObjectMgr.h" +#include "ScriptMgr.h" #include "SkillDiscovery.h" #include "SkillExtraItems.h" #include "SmartAI.h" @@ -152,6 +153,7 @@ public: { "spell_pet_auras", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PET_AURAS, true, &HandleReloadSpellPetAurasCommand, "" }, { "spell_proc", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_PROC, true, &HandleReloadSpellProcsCommand, "" }, { "spell_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPTS, true, &HandleReloadSpellScriptsCommand, "" }, + { "spell_script_names", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_SCRIPT_NAMES, true, &HandleReloadSpellScriptNamesCommand, "" }, { "spell_target_position", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_TARGET_POSITION, true, &HandleReloadSpellTargetPositionCommand, "" }, { "spell_threats", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_THREATS, true, &HandleReloadSpellThreatsCommand, "" }, { "spell_group_stack_rules", rbac::RBAC_PERM_COMMAND_RELOAD_SPELL_GROUP_STACK_RULES, true, &HandleReloadSpellGroupStackRulesCommand, "" }, @@ -276,6 +278,7 @@ public: TC_LOG_INFO("misc", "Re-Loading Scripts..."); HandleReloadEventScriptsCommand(handler, "a"); HandleReloadSpellScriptsCommand(handler, "a"); + HandleReloadSpellScriptNamesCommand(handler, "a"); handler->SendGlobalGMSysMessage("DB tables `*_scripts` reloaded."); HandleReloadWpScriptsCommand(handler, "a"); HandleReloadWpCommand(handler, "a"); @@ -296,6 +299,7 @@ public: HandleReloadSpellThreatsCommand(handler, "a"); HandleReloadSpellGroupStackRulesCommand(handler, "a"); HandleReloadSpellPetAurasCommand(handler, "a"); + HandleReloadSpellScriptNamesCommand(handler, "a"); return true; } @@ -349,6 +353,7 @@ public: { TC_LOG_INFO("misc", "Re-Loading Additional Criteria Data..."); sCriteriaMgr->LoadCriteriaData(); + sScriptMgr->NotifyScriptIDUpdate(); handler->SendGlobalGMSysMessage("DB table `criteria_data` reloaded."); return true; } @@ -389,6 +394,7 @@ public: { TC_LOG_INFO("misc", "Re-Loading Battleground Templates..."); sBattlegroundMgr->LoadBattlegroundTemplates(); + sScriptMgr->NotifyScriptIDUpdate(); handler->SendGlobalGMSysMessage("DB table `battleground_template` reloaded."); return true; } @@ -461,6 +467,7 @@ public: } sObjectMgr->InitializeQueriesData(QUERY_DATA_CREATURES); + sScriptMgr->NotifyScriptIDUpdate(); handler->SendGlobalGMSysMessage("Creature template reloaded."); return true; } @@ -550,6 +557,8 @@ public: TC_LOG_INFO("misc", "Re-Loading GameObjects for quests..."); sObjectMgr->LoadGameObjectForQuests(); handler->SendGlobalGMSysMessage("Data GameObjects for quests reloaded."); + sScriptMgr->NotifyScriptIDUpdate(); + return true; } @@ -974,6 +983,16 @@ public: return true; } + static bool HandleReloadSpellScriptNamesCommand(ChatHandler* handler, const char* /*args*/) + { + TC_LOG_INFO("misc", "Reloading spell_script_names table..."); + sObjectMgr->LoadSpellScriptNames(); + sScriptMgr->NotifyScriptIDUpdate(); + sObjectMgr->ValidateSpellScripts(); + handler->SendGlobalGMSysMessage("Spell scripts reloaded."); + return true; + } + static bool HandleReloadGameGraveyardZoneCommand(ChatHandler* handler, char const* /*args*/) { TC_LOG_INFO("misc", "Re-Loading Graveyard-zone links..."); @@ -1107,6 +1126,7 @@ public: { TC_LOG_INFO("misc", "Re-Loading Conditions..."); sConditionMgr->LoadConditions(true); + sScriptMgr->NotifyScriptIDUpdate(); handler->SendGlobalGMSysMessage("Conditions reloaded."); return true; } @@ -1155,7 +1175,8 @@ public: { TC_LOG_INFO("misc", "Reloading areatrigger_template table..."); sAreaTriggerDataStore->LoadAreaTriggerTemplates(); - handler->SendGlobalGMSysMessage("AreaTrigger templates reloaded. Already spawned AT won't be affected. New scriptname need a reboot."); + sScriptMgr->NotifyScriptIDUpdate(); + handler->SendGlobalGMSysMessage("AreaTrigger templates reloaded."); return true; } @@ -1163,7 +1184,8 @@ public: { TC_LOG_INFO("misc", "Reloading scene_template table..."); sObjectMgr->LoadSceneTemplates(); - handler->SendGlobalGMSysMessage("Scenes templates reloaded. New scriptname need a reboot."); + sScriptMgr->NotifyScriptIDUpdate(); + handler->SendGlobalGMSysMessage("Scenes templates reloaded."); return true; } @@ -1171,6 +1193,7 @@ public: { TC_LOG_INFO("misc", "Reloading conversation_* tables..."); sConversationDataStore->LoadConversationTemplates(); + sScriptMgr->NotifyScriptIDUpdate(); handler->SendGlobalGMSysMessage("Conversation templates reloaded."); return true; } |